diff --git a/VDash/MainWindow.xaml.cs b/VDash/MainWindow.xaml.cs
index 76a4100..a8c507f 100644
--- a/VDash/MainWindow.xaml.cs
+++ b/VDash/MainWindow.xaml.cs
@@ -13,6 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Valve.VR;
+using VDash.Overlay;
namespace VDash
{
@@ -25,7 +26,7 @@ namespace VDash
{
InitializeComponent();
- //Connect to VR runtime...
+ ///Connect to VR runtime...
EVRInitError initError = EVRInitError.None;
var ret = OpenVR.Init(ref initError, EVRApplicationType.VRApplication_Overlay);
@@ -35,25 +36,38 @@ namespace VDash
throw new Exception($"Error connecting to VR: {initError.ToString()}");
}
- //ulong desktopOverlay = 0;
- //var err = OpenVR.Overlay.FindOverlay("valve.steam.desktop", ref desktopOverlay);
+ StartOverlayWindow("Discord");
- //HmdVector2_t vec = new HmdVector2_t();
+ StartOverlayWindow("Microsoft Visual Studio");
- //OpenVR.Overlay.GetOverlayMouseScale(desktopOverlay, ref vec);
+ StartOverlayWindow("Notepad++");
- //var discordSelector = new WindowSelectors.FromWindowTitle("Discord", WindowSelectors.FromWindowTitle.TitleSearchType.Contains);
- //Overlay.WindowOverlay discordOverlay = new Overlay.WindowOverlay("discord", "Discord", discordSelector, true);
- //discordOverlay.Create();
+ //StartOverlayWindow("Putty");
- var vsSelector = new WindowSelectors.FromWindowTitle("Microsoft Visual Studio", WindowSelectors.FromWindowTitle.TitleSearchType.Contains);
- Overlay.WindowOverlay vsOverlay = new Overlay.WindowOverlay("vs", "Visual Studio", vsSelector, true);
- vsOverlay.Create();
+ StartOverlayWindow("Paint");
- //Overlay.WindowOverlay obsOverlay = new Overlay.WindowOverlay("obs", "OBS", new WindowSelectors.FromProcessName("obs64"), false);
- //obsOverlay.Create();
+ Overlay.WindowOverlay obsoverlay = new Overlay.WindowOverlay("obs", "obs", new WindowSelectors.FromProcessName("obs64"), false);
+ obsoverlay.Create();
+ obsoverlay.DestroyOnNoWindowFound = true;
+ var bringWindow = new Overlay.Actions.BringWindowToFrontAction(true);
+ obsoverlay.OverlayShownActions.Add(bringWindow);
+ }
+
+
+ //This is just for debugging I swear
+ int count = 0;
+ IOverlay StartOverlayWindow(string windowFilter)
+ {
+ var ws = new WindowSelectors.FromWindowTitle(windowFilter, WindowSelectors.FromWindowTitle.TitleSearchType.Contains);
+ Overlay.WindowOverlay overlay = new Overlay.WindowOverlay($"ws{count}", "Loading...", ws, true);
+ overlay.Create();
+
+ var bringWindow = new Overlay.Actions.BringWindowToFrontAction(true);
+ overlay.OverlayShownActions.Add(bringWindow);
+ count++;
+ return overlay;
}
}
}
diff --git a/VDash/Overlay/Actions/BringWindowToFrontAction.cs b/VDash/Overlay/Actions/BringWindowToFrontAction.cs
new file mode 100644
index 0000000..8907c6b
--- /dev/null
+++ b/VDash/Overlay/Actions/BringWindowToFrontAction.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using VDash.Utilities;
+
+namespace VDash.Overlay.Actions
+{
+ public class BringWindowToFrontAction : IOverlayAction
+ {
+ ///
+ /// Unminimize the window if it's minimized.
+ ///
+ public bool UnminimizeIfMinimized { get; set; }
+ public bool Enabled { get; set; } = true;
+
+ public BringWindowToFrontAction(bool unminimizeIfMinimized)
+ {
+ UnminimizeIfMinimized = unminimizeIfMinimized;
+ }
+
+ public void PreformAction(IOverlay intOverlay)
+ {
+ if (!Enabled) { return; }
+ if (!intOverlay.GetType().IsAssignableFrom(typeof(WindowOverlay)))
+ {
+ Console.WriteLine("This action cannot be performed on a non-WindowOverlay overlay.");
+ return;
+ }
+
+ WindowOverlay overlay = (WindowOverlay)intOverlay;
+ IntPtr handle = overlay.Selector.GetWindowHandle();
+ PreformAction(handle);
+ }
+
+ public void PreformAction(IntPtr handle)
+ {
+ //Make sure window isn't minimized
+ User32.WINDOWPLACEMENT placement = new User32.WINDOWPLACEMENT();
+ placement.Length = Marshal.SizeOf(placement);
+ User32.GetWindowPlacement(handle, ref placement);
+
+ if (placement.ShowCmd == User32.ShowWindowCommands.ShowMinimized)
+ {
+ User32.ShowWindow(handle, User32.ShowWindowCommands.Normal);
+ }
+
+ User32.SetForegroundWindow(handle);
+ }
+ }
+}
diff --git a/VDash/Overlay/Actions/IOverlayAction.cs b/VDash/Overlay/Actions/IOverlayAction.cs
new file mode 100644
index 0000000..44b5d0a
--- /dev/null
+++ b/VDash/Overlay/Actions/IOverlayAction.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VDash.Overlay.Actions
+{
+ public interface IOverlayAction
+ {
+ void PreformAction(IOverlay overlay);
+ bool Enabled { get; set; }
+ }
+}
diff --git a/VDash/Overlay/Actions/MaximizeWindowAction.cs b/VDash/Overlay/Actions/MaximizeWindowAction.cs
new file mode 100644
index 0000000..507cde2
--- /dev/null
+++ b/VDash/Overlay/Actions/MaximizeWindowAction.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using VDash.Utilities;
+
+namespace VDash.Overlay.Actions
+{
+ public class MaximizeWindowAction : IOverlayAction
+ {
+ public bool Enabled { get; set; }
+
+ public void PreformAction(IOverlay intOverlay)
+ {
+ if (!Enabled) { return; }
+ if (!intOverlay.GetType().IsAssignableFrom(typeof(WindowOverlay)))
+ {
+ Console.WriteLine("This action cannot be performed on a non-WindowOverlay overlay.");
+ return;
+ }
+
+ WindowOverlay overlay = (WindowOverlay)intOverlay;
+ IntPtr handle = overlay.Selector.GetWindowHandle();
+ PreformAction(handle);
+ }
+
+ public void PreformAction(IntPtr handle)
+ {
+ User32.ShowWindow(handle, User32.ShowWindowCommands.Maximize);
+ }
+ }
+}
diff --git a/VDash/Overlay/Actions/ResizeWindowAction.cs b/VDash/Overlay/Actions/ResizeWindowAction.cs
new file mode 100644
index 0000000..444023f
--- /dev/null
+++ b/VDash/Overlay/Actions/ResizeWindowAction.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VDash.Utilities;
+
+namespace VDash.Overlay.Actions
+{
+ public class ResizeWindowAction : IOverlayAction
+ {
+ public int NewWidth { get; set; }
+ public int NewHeight { get; set; }
+ public bool Enabled { get; set; }
+
+ public ResizeWindowAction(int newWidth, int newHeight)
+ {
+ NewWidth = newWidth;
+ NewHeight = newHeight;
+ }
+
+ public void PreformAction(IOverlay intOverlay)
+ {
+ if (!Enabled) { return; }
+ if (!intOverlay.GetType().IsAssignableFrom(typeof(WindowOverlay)))
+ {
+ Console.WriteLine("This action cannot be performed on a non-WindowOverlay overlay.");
+ return;
+ }
+
+ WindowOverlay overlay = (WindowOverlay)intOverlay;
+ IntPtr handle = overlay.Selector.GetWindowHandle();
+ PreformAction(handle);
+ }
+
+ public void PreformAction(IntPtr handle)
+ {
+ //Retain old rect
+ User32.Rect rect = new User32.Rect();
+ User32.GetWindowRect(handle, ref rect);
+
+ User32.SetWindowPos(handle, IntPtr.Zero, rect.left, rect.bottom, NewWidth, NewHeight, User32.SetWindowPosFlags.DoNotActivate |
+ User32.SetWindowPosFlags.IgnoreMove | User32.SetWindowPosFlags.IgnoreZOrder |
+ User32.SetWindowPosFlags.DoNotChangeOwnerZOrder);
+ }
+ }
+}
diff --git a/VDash/Overlay/Actions/RestoreWindowAction.cs b/VDash/Overlay/Actions/RestoreWindowAction.cs
new file mode 100644
index 0000000..7e9e2e8
--- /dev/null
+++ b/VDash/Overlay/Actions/RestoreWindowAction.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VDash.Utilities;
+
+namespace VDash.Overlay.Actions
+{
+ public class RestoreWindowAction : IOverlayAction
+ {
+
+ public bool Enabled { get; set; }
+
+ public void PreformAction(IOverlay intOverlay)
+ {
+ if (!Enabled) { return; }
+ if (!intOverlay.GetType().IsAssignableFrom(typeof(WindowOverlay)))
+ {
+ Console.WriteLine("This action cannot be performed on a non-WindowOverlay overlay.");
+ return;
+ }
+
+ WindowOverlay overlay = (WindowOverlay)intOverlay;
+ IntPtr handle = overlay.Selector.GetWindowHandle();
+ PreformAction(handle);
+ }
+
+ public void PreformAction(IntPtr handle)
+ {
+ User32.ShowWindow(handle, User32.ShowWindowCommands.Restore);
+ }
+ }
+}
+
diff --git a/VDash/Overlay/IOverlay.cs b/VDash/Overlay/IOverlay.cs
index 74ddff0..4a3a34a 100644
--- a/VDash/Overlay/IOverlay.cs
+++ b/VDash/Overlay/IOverlay.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using VDash.Overlay.Actions;
namespace VDash.Overlay
{
@@ -7,13 +9,18 @@ namespace VDash.Overlay
event EventHandler OverlayShown;
event EventHandler OverlayHidden;
- ulong Handle { get; }
+ List OverlayShownActions { get; }
+ List OverlayHiddenActions { get; }
+
+ ulong OverlayHandle { get; }
string Name { get; }
string Key { get; }
void Create();
void Destroy();
string GetOverlayName();
+ float GetOverlayWidth();
+ void SetOverlayWidth(float width);
void SetOverlayName(string newName);
}
}
diff --git a/VDash/Overlay/WindowOverlay.cs b/VDash/Overlay/WindowOverlay.cs
index 7f12b94..6f3ca76 100644
--- a/VDash/Overlay/WindowOverlay.cs
+++ b/VDash/Overlay/WindowOverlay.cs
@@ -2,6 +2,7 @@
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
@@ -9,8 +10,12 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Forms;
using Valve.VR;
+using VDash.Overlay.Actions;
+using VDash.Utilities;
using VDash.WindowSelectors;
+using System.Linq;
namespace VDash.Overlay
{
@@ -20,20 +25,21 @@ namespace VDash.Overlay
public event EventHandler OverlayShown;
public event EventHandler OverlayHidden;
- public CVRSystem VRSystem { get; private set; }
+ public bool UpdateDashboardName { get; set; } = false;
+ public bool DestroyOnNoWindowFound { get; set; } = false;
+ public int CurrentFPS { get; private set; }
+
private Task m_EventPumpTask = null;
+ ///
+ /// Tracks whether or not this overlay is actually created.
+ ///
bool m_Created = false;
bool m_Running = true;
- public bool UpdateDashboardName { get; set; } = false;
- internal ulong m_Handle;
- public ulong Handle
- {
- get
- {
- return m_Handle;
- }
- }
+ internal ulong m_OverlayHandle;
+ public ulong OverlayHandle { get { return m_OverlayHandle; } }
+ internal ulong m_ThumbnailIcon;
+ public ulong ThumbnailIcon { get { return m_ThumbnailIcon; } }
internal string m_Key;
public string Key { get { return m_Key; } }
@@ -41,38 +47,64 @@ namespace VDash.Overlay
internal string m_Name;
public string Name { get { return m_Name; } }
- IWindowSelector m_Selector = null;
+
+ public IWindowSelector Selector { get; private set; } = null;
+
+ public List OverlayShownActions { get; private set; } = new List();
+ public List OverlayHiddenActions { get; private set; } = new List();
+
+ char[] specialKeys = { '+', '^', '%', '~', '(', ')', '{', '}', '[', ']' };
public WindowOverlay(string key, string name, IWindowSelector selector, bool updateDashboardName)
{
m_Key = key;
m_Name = name;
- m_Selector = selector;
+ Selector = selector;
UpdateDashboardName = updateDashboardName;
}
+ ///
+ /// Represents whether or not an external caller wants this to stay destroyed, really.
+ /// To keep the DestroyOnNoWindowFound stuff in check. Don't want it creating when it shouldn't.
+ ///
+ bool m_Destroyed = true;
+
public void Create()
+ {
+ m_Destroyed = false;
+ createInternal();
+ }
+
+ private void createInternal()
{
if (m_Created) { throw new Exception("This overlay has already been created."); }
- //ConsoleManager.Show();
- ulong thumbHandle = OpenVR.k_ulOverlayHandleInvalid;
+
//Actually create the overlay
var overlay = OpenVR.Overlay;
- EVROverlayError err = overlay.CreateDashboardOverlay(m_Key, m_Name, ref m_Handle, ref thumbHandle);
+ EVROverlayError err = overlay.CreateDashboardOverlay(m_Key, m_Name, ref m_OverlayHandle, ref m_ThumbnailIcon);
if (err != EVROverlayError.None)
{
throw new Exception($"Error creating overlay: {err}");
}
- overlay.SetOverlayInputMethod(m_Handle, VROverlayInputMethod.Mouse);
+ overlay.SetOverlayInputMethod(m_OverlayHandle, VROverlayInputMethod.Mouse);
m_Running = true;
m_EventPumpTask = Task.Factory.StartNew(() => { EventPump(); });
m_Created = true;
}
+ public bool IsCreated() { return m_Created; }
+
+
public void Destroy()
+ {
+ m_Destroyed = true;
+ destroyInternal();
+ }
+
+ private void destroyInternal()
{
if (!m_Created) { throw new Exception("This object is not created."); }
m_Created = false;
@@ -83,19 +115,25 @@ namespace VDash.Overlay
//Wait for render thread to exit
m_EventPumpTask.Wait();
- var err = OpenVR.Overlay.DestroyOverlay(m_Handle);
+ var err = OpenVR.Overlay.DestroyOverlay(m_OverlayHandle);
if( err != EVROverlayError.None)
{
throw new Exception($"Error destroying overlay: {err}");
}
+
+ m_OverlayHandle = OpenVR.k_ulOverlayHandleInvalid;
+ m_ThumbnailIcon = OpenVR.k_ulOverlayHandleInvalid;
}
private void EventPump()
{
- #region Rendering setup
var overlay = OpenVR.Overlay;
+ DateTime lastClick = DateTime.MinValue;
+
+ #region Rendering setup
+
//Partially taken from here: https://stackoverflow.com/questions/6812068/c-sharp-which-is-the-fastest-way-to-take-a-screen-shot
//Desktop Duplication didn't work well, hence is why we're sticking with GDI. Still this is faster than using SetTextureRaw.
@@ -120,60 +158,107 @@ namespace VDash.Overlay
int frames = 0;
DateTime lastFps = DateTime.MinValue;
DateTime lastNameCheck = DateTime.MinValue;
+
while (m_Running)
{
- //Update the SteamVR dash name if necessary, only once a second.
- if (UpdateDashboardName && DateTime.Now.Subtract(lastNameCheck).TotalMilliseconds >= 1000)
+ if (DestroyOnNoWindowFound)
{
- var window = m_Selector.GetWindowInfo();
+ //Check to see if the window actually exists. Act accordingly.
+ if (!Selector.WindowExists())
+ {
+ if (IsCreated())
+ {
+ destroyInternal();
+ Console.WriteLine($"Destroyed {m_Key} because it's window does not exist.");
+ }
+
+ Thread.Sleep(100);
+ continue;
+ }
+ else if(Selector.WindowExists() && !m_Created && !m_Destroyed)
+ {
+ createInternal();
+ }
+ }
+
+ //This technically should never happen, buuuut...
+ IntPtr windowHandle = IntPtr.Zero;
+ try
+ {
+ windowHandle = Selector.GetWindowHandle();
+ }
+ catch (WindowNotFoundException)
+ {
+ }
+
+ //Update the SteamVR dash name if necessary, only once a second.
+ if (Selector.WindowExists() && UpdateDashboardName && DateTime.Now.Subtract(lastNameCheck).TotalMilliseconds >= 1000)
+ {
+ var window = Selector.GetWindowInfo();
if(GetOverlayName() != window.Title)
{
SetOverlayName(window.Title);
}
lastNameCheck = DateTime.Now;
+
+
+ #region Thumbnail rendering
+ using (var icon = User32.GetAppIcon(windowHandle))
+ {
+ using (var iconBmp = icon.ToBitmap())
+ {
+ var data = iconBmp.LockBits(new Rectangle(0, 0, iconBmp.Width, iconBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
+
+ overlay.SetOverlayRaw(m_ThumbnailIcon, data.Scan0, (uint)data.Width, (uint)data.Height, 4);
+
+ iconBmp.UnlockBits(data);
+ }
+ }
+ #endregion
}
//Skip rendering if overlay isn't visible
#if !DEBUG
- if ((m_Handle == Openvr.k_uloverlayhandleinvalid) || overlay == null || (!overlay.isoverlayvisible(m_handle)))
+ if ((m_OverlayHandle == OpenVR.k_ulOverlayHandleInvalid) || overlay == null || (!overlay.IsOverlayVisible(m_OverlayHandle)))
{
- thread.sleep(100);
+ Thread.Sleep(100);
continue;
}
#endif
- IntPtr windowHandle = IntPtr.Zero;
- try
- {
- windowHandle = m_Selector.GetWindowHandle();
- }
- catch (WindowNotFoundException)
- {
- //Console.WriteLine($"[{m_Key}] {m_Name}: Window not found.");
- }
+
#region VR Events
VREvent_t vrEvent = new VREvent_t();
- while (OpenVR.Overlay.PollNextOverlayEvent(m_Handle, ref vrEvent, (uint)Marshal.SizeOf(vrEvent)))
+ while (OpenVR.Overlay.PollNextOverlayEvent(m_OverlayHandle, ref vrEvent, (uint)Marshal.SizeOf(vrEvent)))
{
switch ((EVREventType)vrEvent.eventType)
{
case EVREventType.VREvent_MouseMove:
{
+ //Prevent the cursor from moving while we click. Super fkn annoying.
+ if(DateTime.Now.Subtract(lastClick).TotalMilliseconds < 400)
+ {
+ break;
+ }
+ //Make sure the window still exists
if (windowHandle != IntPtr.Zero)
{
var vrX = vrEvent.data.mouse.x;
var vrY = vrEvent.data.mouse.y;
var rect = new User32.Rect();
- User32.GetWindowRect(windowHandle, ref rect);
+
+ User32.GetClientRect(windowHandle, ref rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
+
+ var point = new System.Drawing.Point() { X = (int)vrX, Y = height - (int)vrY };
+
- var point = new User32.POINT() { x = (int)vrX, y = height - (int)vrY };
var succ = User32.ClientToScreen(windowHandle, ref point);
if (!succ)
@@ -182,7 +267,7 @@ namespace VDash.Overlay
}
- succ = User32.SetCursorPos((int)point.x, (int)point.y);
+ succ = User32.SetCursorPos((int)point.X, (int)point.Y);
if (!succ)
{
@@ -195,7 +280,6 @@ namespace VDash.Overlay
case EVREventType.VREvent_MouseButtonDown:
{
var clickType = (EVRMouseButton)vrEvent.data.mouse.button;
- Console.WriteLine($"Mouse button down: {clickType}");
var winClickType = User32.MouseEventFlags.MOUSEEVENTF_LEFTDOWN;
@@ -219,13 +303,13 @@ namespace VDash.Overlay
}
User32.mouse_event(winClickType, 0, 0, 0, UIntPtr.Zero);
+ lastClick = DateTime.Now;
break;
}
case EVREventType.VREvent_MouseButtonUp:
{
var clickType = (EVRMouseButton)vrEvent.data.mouse.button;
- Console.WriteLine($"Mouse button up: {clickType}");
var winClickType = User32.MouseEventFlags.MOUSEEVENTF_LEFTUP;
switch (clickType)
@@ -248,16 +332,83 @@ namespace VDash.Overlay
}
User32.mouse_event(winClickType, 0, 0, 0, UIntPtr.Zero);
+ lastClick = DateTime.MinValue;
break;
}
case EVREventType.VREvent_OverlayShown:
{
OverlayShown?.Invoke(this, new EventArgs());
+
+ if (Selector.WindowExists())
+ {
+ foreach (var action in OverlayShownActions)
+ {
+ action.PreformAction(this);
+ }
+ }
break;
}
case EVREventType.VREvent_OverlayHidden:
{
OverlayHidden?.Invoke(this, new EventArgs());
+
+ if (Selector.WindowExists())
+ {
+ foreach (var action in OverlayHiddenActions)
+ {
+ action.PreformAction(this);
+ }
+ }
+ break;
+ }
+ case EVREventType.VREvent_KeyboardCharInput:
+ {
+ //Console.WriteLine($"Got char input: {vrEvent.data.keyboard.cNewInput}");
+ var input = vrEvent.data.keyboard.cNewInput;
+
+ //We should only be getting one keypress at a time.
+ switch (input)
+ {
+ case "\b":
+ {
+ SendKeys.SendWait("{BACKSPACE}");
+ break;
+ }
+ case "\n":
+ {
+ SendKeys.SendWait("{ENTER}");
+ break;
+ }
+ default:
+ {
+ if (specialKeys.Contains(input[0]))
+ {
+ SendKeys.SendWait("{" + input + "}");
+ }
+ else
+ {
+ SendKeys.SendWait(input);
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+ case EVREventType.VREvent_ButtonPress:
+ {
+ EVRButtonId button = (EVRButtonId)vrEvent.data.controller.button;
+ //Console.WriteLine($"Button pressed: {button}");
+
+ switch (button)
+ {
+ case EVRButtonId.k_EButton_Knuckles_A:
+ {
+ overlay.ShowKeyboardForOverlay(m_OverlayHandle, (int)EGamepadTextInputMode.k_EGamepadTextInputModeNormal,
+ (int)EGamepadTextInputLineMode.k_EGamepadTextInputLineModeSingleLine, "desc?", 1024, "", true, 0);
+ break;
+ }
+ }
break;
}
}
@@ -265,162 +416,166 @@ namespace VDash.Overlay
}
#endregion
-#region Acutally Render
-
- if (windowHandle != IntPtr.Zero)
+ #region Acutally Render
+ //Redundant check, but w/e
+ if (Selector.WindowExists() && windowHandle != IntPtr.Zero)
{
var rect = new User32.Rect();
- User32.GetWindowRect(windowHandle, ref rect);
+ User32.GetClientRect(windowHandle, ref rect);
+ System.Drawing.Point pnt = new Point { X = 0, Y = 0 };
+
+ User32.ClientToScreen(windowHandle, ref pnt);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
-
-
- using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
+ //Make sure the coords are actually valid. Could be minimized or something.
+ if (width > 0 && height > 0)
{
- using (Graphics graphics = Graphics.FromImage(bmp))
+ using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
- graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
-
- //Draw cursor
- User32.CURSORINFO pci;
- pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(User32.CURSORINFO));
-
- if (User32.GetCursorInfo(out pci))
+ using (Graphics graphics = Graphics.FromImage(bmp))
{
+ graphics.CopyFromScreen((int)pnt.X, (int)pnt.Y, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
- User32.POINT cursorPoint = new User32.POINT() { x = pci.ptScreenPos.X, y = pci.ptScreenPos.Y};
+ #region Cursor drawing
+ User32.CURSORINFO pci;
+ pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(User32.CURSORINFO));
- if (User32.ScreenToClient(windowHandle, ref cursorPoint))
+ if (User32.GetCursorInfo(out pci))
{
- if (pci.flags == User32.CURSOR_SHOWING)
+ System.Drawing.Point cursorPoint = new System.Drawing.Point() { X = pci.ptScreenPos.X, Y = pci.ptScreenPos.Y };
+
+ if (User32.ScreenToClient(windowHandle, ref cursorPoint))
{
- User32.DrawIcon(graphics.GetHdc(), (int)cursorPoint.x, (int)cursorPoint.y, pci.hCursor);
- graphics.ReleaseHdc();
+
+ if (pci.flags == User32.CURSOR_SHOWING)
+ {
+ User32.DrawIcon(graphics.GetHdc(), (int)cursorPoint.X, (int)cursorPoint.Y, pci.hCursor);
+ graphics.ReleaseHdc();
+ }
+ }
+ else
+ {
+ Console.WriteLine("Error getting coords to draw cursor.");
}
}
else
{
- Console.WriteLine("Error getting coords to draw cursor.");
+ string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
+ Console.WriteLine($"Error getting cursor info: {errorMessage}");
}
- }
- else
- {
- string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
- Console.WriteLine($"Error getting cursor info: {errorMessage}");
- }
+ #endregion
+ var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
- var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
-
-
- if (screenTexture == null || (lastWidth != data.Width || lastHeight != data.Height))
- {
- lastWidth = data.Width;
- lastHeight = data.Height;
-
- if (screenTexture != null)
+ if (screenTexture == null || (lastWidth != data.Width || lastHeight != data.Height))
{
- screenTexture.Dispose();
+ lastWidth = data.Width;
+ lastHeight = data.Height;
- //Dispose not freeing screenTexture: https://gamedev.stackexchange.com/questions/93311/freeing-texture-memory-in-sharpdx-what-am-i-missing
- device.ImmediateContext.ClearState();
- device.ImmediateContext.Flush();
- }
-
- var textureDesc = new Texture2DDescription
- {
- CpuAccessFlags = CpuAccessFlags.Write,
- BindFlags = BindFlags.ShaderResource,
- Format = Format.B8G8R8A8_UNorm,
- //Format = Format.B8G8R8A8_UNorm_SRgb,
- Width = data.Width,
- Height = data.Height,
- OptionFlags = ResourceOptionFlags.None,
- MipLevels = 1,
- ArraySize = 1,
- SampleDescription = { Count = 1, Quality = 0 },
- Usage = ResourceUsage.Dynamic
- };
- screenTexture = new Texture2D(device, textureDesc);
- stream = new DataStream(4 * width * height, true, true);
-
- overlay.SetOverlayWidthInMeters(m_Handle, 2.5f);
-
- HmdVector2_t vecWindowSize = new HmdVector2_t() { v0 = width, v1 = height };
- OpenVR.Overlay.SetOverlayMouseScale(m_Handle, ref vecWindowSize);
- Console.WriteLine("Updated!");
-
-
- }
- //Render using graphics card, in DirectX context.
-
- //using ()
- {
- var screenView = new ShaderResourceView(device, screenTexture);
- device.ImmediateContext.PixelShader.SetShaderResource(0, screenView);
-
- var dr = new SharpDX.DataRectangle(data.Scan0, data.Stride);
-
- DataBox databox = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.WriteDiscard,
- SharpDX.Direct3D11.MapFlags.None, out stream);
-
-
-
- if (!databox.IsEmpty)
- {
- int count = 4 * data.Width * data.Height;
-
- //Stride compensation. Sometimes the BMP and DX texture strides are different. This fixes it.
- if (databox.RowPitch == data.Stride)
+ if (screenTexture != null)
{
- stream.Write(data.Scan0, 0, count);
+ screenTexture.Dispose();
+
+ //Dispose not freeing screenTexture: https://gamedev.stackexchange.com/questions/93311/freeing-texture-memory-in-sharpdx-what-am-i-missing
+ device.ImmediateContext.ClearState();
+ device.ImmediateContext.Flush();
}
- //Truncate the extra BMP bytes.
- else if (databox.RowPitch < data.Stride)
+
+ var textureDesc = new Texture2DDescription
{
- for (int i = 0; i < data.Height; i++)
+ CpuAccessFlags = CpuAccessFlags.Write,
+ BindFlags = BindFlags.ShaderResource,
+ Format = Format.B8G8R8A8_UNorm,
+ //Format = Format.B8G8R8A8_UNorm_SRgb,
+ Width = data.Width,
+ Height = data.Height,
+ OptionFlags = ResourceOptionFlags.None,
+ MipLevels = 1,
+ ArraySize = 1,
+ SampleDescription = { Count = 1, Quality = 0 },
+ Usage = ResourceUsage.Dynamic
+ };
+ screenTexture = new Texture2D(device, textureDesc);
+ stream = new DataStream(4 * width * height, true, true);
+
+ overlay.SetOverlayWidthInMeters(m_OverlayHandle, 2.5f);
+
+ HmdVector2_t vecWindowSize = new HmdVector2_t() { v0 = data.Width, v1 = data.Height };
+ OpenVR.Overlay.SetOverlayMouseScale(m_OverlayHandle, ref vecWindowSize);
+ Console.WriteLine("Updated!");
+
+
+ }
+ //Render using graphics card, in DirectX context.
+
+ //using ()
+ {
+ var screenView = new ShaderResourceView(device, screenTexture);
+ device.ImmediateContext.PixelShader.SetShaderResource(0, screenView);
+
+ var dr = new SharpDX.DataRectangle(data.Scan0, data.Stride);
+
+ DataBox databox = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.WriteDiscard,
+ SharpDX.Direct3D11.MapFlags.None, out stream);
+
+
+
+ if (!databox.IsEmpty)
+ {
+ int count = 4 * data.Width * data.Height;
+
+ //Stride compensation. Sometimes the BMP and DX texture strides are different. This fixes it.
+ if (databox.RowPitch == data.Stride)
{
- stream.Write(data.Scan0, (data.Stride * i), databox.RowPitch);
+ stream.Write(data.Scan0, 0, count);
+ }
+ //Truncate the extra BMP bytes.
+ else if (databox.RowPitch < data.Stride)
+ {
+ for (int i = 0; i < data.Height; i++)
+ {
+ stream.Write(data.Scan0, (data.Stride * i), databox.RowPitch);
+ }
+ }
+ //Add some junk on empty bytes on the end to make everyone happy.
+ else if (databox.RowPitch > data.Stride)
+ {
+ int difference = databox.RowPitch - data.Stride;
+ byte[] junk = new byte[difference];
+ for (int i = 0; i < data.Height; i++)
+ {
+ stream.Write(data.Scan0, (data.Stride * i), data.Stride);
+ stream.Write(junk, 0, difference);
+ }
}
}
- //Add some junk on empty bytes on the end to make everyone happy.
- else if(databox.RowPitch > data.Stride)
+
+
+ VRTextureBounds_t bounds = new VRTextureBounds_t();
+ OpenVR.Overlay.GetOverlayTextureBounds(m_OverlayHandle, ref bounds);
+
+ var texture = new Texture_t();
+ texture.handle = screenTexture.NativePointer;
+ texture.eType = ETextureType.DirectX;
+ texture.eColorSpace = EColorSpace.Auto;
+ EVROverlayError result = overlay.SetOverlayTexture(m_OverlayHandle, ref texture);
+ if (result != EVROverlayError.None)
{
- int difference = databox.RowPitch - data.Stride;
- byte[] junk = new byte[difference];
- for (int i = 0; i < data.Height; i++)
- {
- stream.Write(data.Scan0, (data.Stride * i), data.Stride);
- stream.Write(junk, 0, difference);
- }
+
+ Console.WriteLine($"Error rendering: {result}");
}
+
+ device.ImmediateContext.UnmapSubresource(screenTexture, 0);
+
+
+
+ frames++;
+
+ bmp.UnlockBits(data);
}
-
-
- VRTextureBounds_t bounds = new VRTextureBounds_t();
- OpenVR.Overlay.GetOverlayTextureBounds(m_Handle, ref bounds);
-
- var texture = new Texture_t();
- texture.handle = screenTexture.NativePointer;
- texture.eType = ETextureType.DirectX;
- texture.eColorSpace = EColorSpace.Auto;
- EVROverlayError result = overlay.SetOverlayTexture(m_Handle, ref texture);
- if (result != EVROverlayError.None)
- {
-
- Console.WriteLine($"Error rendering: {result}");
- }
-
- device.ImmediateContext.UnmapSubresource(screenTexture, 0);
-
-
-
- frames++;
-
- bmp.UnlockBits(data);
}
}
}
@@ -432,83 +587,24 @@ namespace VDash.Overlay
if (DateTime.Now.Subtract(lastFps).TotalMilliseconds > 1000)
{
//Console.WriteLine($"{frames} fps.");
+ CurrentFPS = frames;
frames = 0;
lastFps = DateTime.Now;
}
}
//Clean up after we exit while loop.
+ screenTexture.Dispose();
+ device.ImmediateContext.ClearState();
+ device.ImmediateContext.Flush();
+
+ CurrentFPS = 0;
+
device.Dispose();
adapter.Dispose();
factory.Dispose();
}
- private class User32
- {
- [StructLayout(LayoutKind.Sequential)]
- public struct Rect
- {
- public int left;
- public int top;
- public int right;
- public int bottom;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct POINT
- {
- public long x;
- public long y;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct CURSORINFO
- {
- public Int32 cbSize;
- public Int32 flags;
- public IntPtr hCursor;
- public Point ptScreenPos;
- }
- public const Int32 CURSOR_SHOWING = 0x00000001;
-
- [DllImport("user32.dll")]
- public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
-
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool SetCursorPos(int x, int y);
-
- [DllImport("user32.dll")]
- public static extern bool ClientToScreen(IntPtr hwnd, ref POINT lpPoint);
-
- [DllImport("user32.dll")]
- public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
-
- [DllImport("user32.dll")]
- public static extern bool GetCursorInfo(out CURSORINFO pci);
-
- [DllImport("user32.dll")]
- public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
-
- [DllImport("user32.dll")]
- public static extern void mouse_event(MouseEventFlags dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
- public enum MouseEventFlags
- {
- MOUSEEVENTF_ABSOLUTE = 0x8000,
- MOUSEEVENTF_LEFTDOWN = 0x0002,
- MOUSEEVENTF_LEFTUP = 0x0004,
- MOUSEEVENTF_MIDDLEDOWN = 0x0020,
- MOUSEEVENTF_MIDDLEUP = 0x0040,
- MOUSEEVENTF_MOVE = 0x0001,
- MOUSEEVENTF_RIGHTDOWN = 0x0008,
- MOUSEEVENTF_RIGHTUP = 0x0010,
- MOUSEEVENTF_XDOWN = 0x0080,
- MOUSEEVENTF_XUP = 0x0100,
- MOUSEEVENTF_WHEEL = 0x0800,
- MOUSEEVENTF_HWHEEL = 0x01000
- }
- }
-
public void Dispose()
{
Destroy();
@@ -518,14 +614,26 @@ namespace VDash.Overlay
{
EVROverlayError err = EVROverlayError.None;
StringBuilder sb = new StringBuilder(255);
- uint res = OpenVR.Overlay.GetOverlayName(m_Handle, sb, 255, ref err);
+ uint res = OpenVR.Overlay.GetOverlayName(m_OverlayHandle, sb, 255, ref err);
return sb.ToString();
}
public void SetOverlayName(string newName)
{
- OpenVR.Overlay.SetOverlayName(m_Handle, newName);
+ OpenVR.Overlay.SetOverlayName(m_OverlayHandle, newName);
m_Name = newName;
}
+
+ public float GetOverlayWidth()
+ {
+ float width = 0f;
+ OpenVR.Overlay.GetOverlayWidthInMeters(m_OverlayHandle, ref width);
+ return width;
+ }
+
+ public void SetOverlayWidth(float width)
+ {
+ OpenVR.Overlay.SetOverlayWidthInMeters(m_OverlayHandle, width);
+ }
}
}
diff --git a/VDash/Utilities/ConsoleManager.cs b/VDash/Utilities/ConsoleManager.cs
new file mode 100644
index 0000000..c68bc99
--- /dev/null
+++ b/VDash/Utilities/ConsoleManager.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+
+[SuppressUnmanagedCodeSecurity]
+public static class ConsoleManager
+{
+ private const string Kernel32_DllName = "kernel32.dll";
+
+ [DllImport(Kernel32_DllName)]
+ private static extern bool AllocConsole();
+
+ [DllImport(Kernel32_DllName)]
+ private static extern bool FreeConsole();
+
+ [DllImport(Kernel32_DllName)]
+ private static extern IntPtr GetConsoleWindow();
+
+ [DllImport(Kernel32_DllName)]
+ private static extern int GetConsoleOutputCP();
+
+ public static bool HasConsole
+ {
+ get { return GetConsoleWindow() != IntPtr.Zero; }
+ }
+
+ ///
+ /// Creates a new console instance if the process is not attached to a console already.
+ ///
+ public static void Show()
+ {
+ //#if DEBUG
+ if (!HasConsole)
+ {
+ AllocConsole();
+ InvalidateOutAndError();
+ }
+ //#endif
+ }
+
+ ///
+ /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
+ ///
+ public static void Hide()
+ {
+ //#if DEBUG
+ if (HasConsole)
+ {
+ SetOutAndErrorNull();
+ FreeConsole();
+ }
+ //#endif
+ }
+
+ public static void Toggle()
+ {
+ if (HasConsole)
+ {
+ Hide();
+ }
+ else
+ {
+ Show();
+ }
+ }
+
+ static void InvalidateOutAndError()
+ {
+ Type type = typeof(System.Console);
+
+ System.Reflection.FieldInfo _out = type.GetField("_out",
+ System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+
+ System.Reflection.FieldInfo _error = type.GetField("_error",
+ System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+
+ System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
+ System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+
+ Debug.Assert(_out != null);
+ Debug.Assert(_error != null);
+
+ Debug.Assert(_InitializeStdOutError != null);
+
+ _out.SetValue(null, null);
+ _error.SetValue(null, null);
+
+ _InitializeStdOutError.Invoke(null, new object[] { true });
+ }
+
+ static void SetOutAndErrorNull()
+ {
+ Console.SetOut(TextWriter.Null);
+ Console.SetError(TextWriter.Null);
+ }
+}
\ No newline at end of file
diff --git a/VDash/Utilities/User32.cs b/VDash/Utilities/User32.cs
new file mode 100644
index 0000000..40a78a3
--- /dev/null
+++ b/VDash/Utilities/User32.cs
@@ -0,0 +1,596 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VDash.Utilities
+{
+
+ public class User32
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Rect
+ {
+ public int left;
+ public int top;
+ public int right;
+ public int bottom;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CURSORINFO
+ {
+ public Int32 cbSize;
+ public Int32 flags;
+ public IntPtr hCursor;
+ public Point ptScreenPos;
+ }
+ public const Int32 CURSOR_SHOWING = 0x00000001;
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetClientRect(IntPtr hWnd, ref Rect lpRect);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetCursorPos(int x, int y);
+
+ [DllImport("user32.dll")]
+ public static extern bool ClientToScreen(IntPtr hwnd, ref System.Drawing.Point lpPoint);
+
+ [DllImport("user32.dll")]
+ public static extern bool ScreenToClient(IntPtr hWnd, ref System.Drawing.Point lpPoint);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetCursorInfo(out CURSORINFO pci);
+
+ [DllImport("user32.dll")]
+ public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
+
+ [DllImport("user32.dll")]
+ public static extern void mouse_event(MouseEventFlags dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
+ public enum MouseEventFlags
+ {
+ MOUSEEVENTF_ABSOLUTE = 0x8000,
+ MOUSEEVENTF_LEFTDOWN = 0x0002,
+ MOUSEEVENTF_LEFTUP = 0x0004,
+ MOUSEEVENTF_MIDDLEDOWN = 0x0020,
+ MOUSEEVENTF_MIDDLEUP = 0x0040,
+ MOUSEEVENTF_MOVE = 0x0001,
+ MOUSEEVENTF_RIGHTDOWN = 0x0008,
+ MOUSEEVENTF_RIGHTUP = 0x0010,
+ MOUSEEVENTF_XDOWN = 0x0080,
+ MOUSEEVENTF_XUP = 0x0100,
+ MOUSEEVENTF_WHEEL = 0x0800,
+ MOUSEEVENTF_HWHEEL = 0x01000
+ }
+
+ public const int GCL_HICONSM = -34;
+ public const int GCL_HICON = -14;
+
+ public const int ICON_SMALL = 0;
+ public const int ICON_BIG = 1;
+ public const int ICON_SMALL2 = 2;
+
+ public const int WM_GETICON = 0x7F;
+
+ public static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
+ {
+ if (IntPtr.Size > 4)
+ return GetClassLongPtr64(hWnd, nIndex);
+ else
+ return new IntPtr(GetClassLongPtr32(hWnd, nIndex));
+ }
+
+ [DllImport("user32.dll", EntryPoint = "GetClassLong")]
+ public static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")]
+ public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
+ static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ public static Icon GetAppIcon(IntPtr hwnd)
+ {
+ IntPtr iconHandle = SendMessage(hwnd, WM_GETICON, ICON_SMALL2, 0);
+ if (iconHandle == IntPtr.Zero)
+ iconHandle = SendMessage(hwnd, WM_GETICON, ICON_SMALL, 0);
+ if (iconHandle == IntPtr.Zero)
+ iconHandle = SendMessage(hwnd, WM_GETICON, ICON_BIG, 0);
+ if (iconHandle == IntPtr.Zero)
+ iconHandle = GetClassLongPtr(hwnd, GCL_HICON);
+ if (iconHandle == IntPtr.Zero)
+ iconHandle = GetClassLongPtr(hwnd, GCL_HICONSM);
+
+ if (iconHandle == IntPtr.Zero)
+ return null;
+
+ Icon icn = Icon.FromHandle(iconHandle);
+
+ return icn;
+ }
+
+ [Flags()]
+ public enum SetWindowPosFlags : uint
+ {
+ /// 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.
+ /// SWP_ASYNCWINDOWPOS
+ AsynchronousWindowPosition = 0x4000,
+ /// Prevents generation of the WM_SYNCPAINT message.
+ /// SWP_DEFERERASE
+ DeferErase = 0x2000,
+ /// Draws a frame (defined in the window's class description) around the window.
+ /// SWP_DRAWFRAME
+ DrawFrame = 0x0020,
+ /// 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.
+ /// SWP_FRAMECHANGED
+ FrameChanged = 0x0020,
+ /// Hides the window.
+ /// SWP_HIDEWINDOW
+ HideWindow = 0x0080,
+ /// 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).
+ /// SWP_NOACTIVATE
+ DoNotActivate = 0x0010,
+ /// 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.
+ /// SWP_NOCOPYBITS
+ DoNotCopyBits = 0x0100,
+ /// Retains the current position (ignores X and Y parameters).
+ /// SWP_NOMOVE
+ IgnoreMove = 0x0002,
+ /// Does not change the owner window's position in the Z order.
+ /// SWP_NOOWNERZORDER
+ DoNotChangeOwnerZOrder = 0x0200,
+ /// 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.
+ /// SWP_NOREDRAW
+ DoNotRedraw = 0x0008,
+ /// Same as the SWP_NOOWNERZORDER flag.
+ /// SWP_NOREPOSITION
+ DoNotReposition = 0x0200,
+ /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
+ /// SWP_NOSENDCHANGING
+ DoNotSendChangingEvent = 0x0400,
+ /// Retains the current size (ignores the cx and cy parameters).
+ /// SWP_NOSIZE
+ IgnoreResize = 0x0001,
+ /// Retains the current Z order (ignores the hWndInsertAfter parameter).
+ /// SWP_NOZORDER
+ IgnoreZOrder = 0x0004,
+ /// Displays the window.
+ /// SWP_SHOWWINDOW
+ ShowWindow = 0x0040,
+ }
+
+ ///
+ /// Changes the size, position, and Z order of a child, pop-up, or top-level window. These windows are ordered
+ /// according to their appearance on the screen. The topmost window receives the highest rank and is the first window
+ /// in the Z order.
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633545%28v=vs.85%29.aspx for more information.
+ ///
+ /// C++ ( hWnd [in]. Type: HWND )
A handle to the window.
+ ///
+ /// C++ ( hWndInsertAfter [in, optional]. Type: HWND )
A handle to the window to precede the positioned window in
+ /// the Z order. This parameter must be a window handle or one of the following values.
+ ///
+ ///
+ /// HWND placementWindow to precede placement
+ ///
+ /// -
+ /// HWND_BOTTOM ((HWND)1)
+ ///
+ /// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost
+ /// window, the window loses its topmost status and is placed at the bottom of all other windows.
+ ///
+ ///
+ /// -
+ /// HWND_NOTOPMOST ((HWND)-2)
+ ///
+ /// Places the window above all non-topmost windows (that is, behind all topmost windows). This
+ /// flag has no effect if the window is already a non-topmost window.
+ ///
+ ///
+ /// -
+ /// HWND_TOP ((HWND)0)Places the window at the top of the Z order.
+ ///
+ /// -
+ /// HWND_TOPMOST ((HWND)-1)
+ ///
+ /// Places the window above all non-topmost windows. The window maintains its topmost position
+ /// even when it is deactivated.
+ ///
+ ///
+ ///
+ /// For more information about how this parameter is used, see the following Remarks section.
+ ///
+ /// C++ ( X [in]. Type: int )
The new position of the left side of the window, in client coordinates.
+ /// C++ ( Y [in]. Type: int )
The new position of the top of the window, in client coordinates.
+ /// C++ ( cx [in]. Type: int )
The new width of the window, in pixels.
+ /// C++ ( cy [in]. Type: int )
The new height of the window, in pixels.
+ ///
+ /// C++ ( uFlags [in]. Type: UINT )
The window sizing and positioning flags. This parameter can be a combination
+ /// of the following values.
+ ///
+ ///
+ /// HWND sizing and positioning flags
+ /// Where to place and size window. Can be a combination of any
+ ///
+ /// -
+ /// SWP_ASYNCWINDOWPOS (0x4000)
+ ///
+ /// 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.
+ ///
+ ///
+ /// -
+ /// SWP_DEFERERASE (0x2000)
+ /// Prevents generation of the WM_SYNCPAINT message.
+ ///
+ /// -
+ /// SWP_DRAWFRAME (0x0020)
+ /// Draws a frame (defined in the window's class description) around the window.
+ ///
+ /// -
+ /// SWP_FRAMECHANGED (0x0020)
+ ///
+ /// 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
+ ///
+ ///
+ /// -
+ /// SWP_HIDEWINDOW (0x0080)Hides the window.
+ ///
+ /// -
+ /// SWP_NOACTIVATE (0x0010)
+ ///
+ /// 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).
+ ///
+ ///
+ /// -
+ /// SWP_NOCOPYBITS (0x0100)
+ ///
+ /// 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.
+ ///
+ ///
+ /// -
+ /// SWP_NOMOVE (0x0002)
+ /// Retains the current position (ignores X and Y parameters).
+ ///
+ /// -
+ /// SWP_NOOWNERZORDER (0x0200)
+ /// Does not change the owner window's position in the Z order.
+ ///
+ /// -
+ /// SWP_NOREDRAW (0x0008)
+ ///
+ /// 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.
+ ///
+ ///
+ /// -
+ /// SWP_NOREPOSITION (0x0200)Same as the SWP_NOOWNERZORDER flag.
+ ///
+ /// -
+ /// SWP_NOSENDCHANGING (0x0400)
+ /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
+ ///
+ /// -
+ /// SWP_NOSIZE (0x0001)
+ /// Retains the current size (ignores the cx and cy parameters).
+ ///
+ /// -
+ /// SWP_NOZORDER (0x0004)
+ /// Retains the current Z order (ignores the hWndInsertAfter parameter).
+ ///
+ /// -
+ /// SWP_SHOWWINDOW (0x0040)Displays the window.
+ ///
+ ///
+ ///
+ /// true or nonzero if the function succeeds, false or zero otherwise or if function fails.
+ ///
+ ///
+ /// As part of the Vista re-architecture, all services were moved off the interactive desktop into Session 0.
+ /// hwnd and window manager operations are only effective inside a session and cross-session attempts to manipulate
+ /// the hwnd will fail. For more information, see The Windows Vista Developer Story: Application Compatibility
+ /// Cookbook.
+ ///
+ ///
+ /// If you have changed certain window data using SetWindowLong, you must call SetWindowPos for the changes to
+ /// take effect. Use the following combination for uFlags: SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
+ /// SWP_FRAMECHANGED.
+ ///
+ ///
+ /// A window can be made a topmost window either by setting the hWndInsertAfter parameter to HWND_TOPMOST and
+ /// ensuring that the SWP_NOZORDER flag is not set, or by setting a window's position in the Z order so that it is
+ /// above any existing topmost windows. When a non-topmost window is made topmost, its owned windows are also made
+ /// topmost. Its owners, however, are not changed.
+ ///
+ ///
+ /// If neither the SWP_NOACTIVATE nor SWP_NOZORDER flag is specified (that is, when the application requests that
+ /// a window be simultaneously activated and its position in the Z order changed), the value specified in
+ /// hWndInsertAfter is used only in the following circumstances.
+ ///
+ ///
+ /// - Neither the HWND_TOPMOST nor HWND_NOTOPMOST flag is specified in hWndInsertAfter.
+ /// - The window identified by hWnd is not the active window.
+ ///
+ ///
+ /// An application cannot activate an inactive window without also bringing it to the top of the Z order.
+ /// Applications can change an activated window's position in the Z order without restrictions, or it can activate
+ /// a window and then move it to the top of the topmost or non-topmost windows.
+ ///
+ ///
+ /// If a topmost window is repositioned to the bottom (HWND_BOTTOM) of the Z order or after any non-topmost
+ /// window, it is no longer topmost. When a topmost window is made non-topmost, its owners and its owned windows
+ /// are also made non-topmost windows.
+ ///
+ ///
+ /// A non-topmost window can own a topmost window, but the reverse cannot occur. Any window (for example, a
+ /// dialog box) owned by a topmost window is itself made a topmost window, to ensure that all owned windows stay
+ /// above their owner.
+ ///
+ ///
+ /// If an application is not in the foreground, and should be in the foreground, it must call the
+ /// SetForegroundWindow function.
+ ///
+ ///
+ /// To use SetWindowPos to bring a window to the top, the process that owns the window must have
+ /// SetForegroundWindow permission.
+ ///
+ ///
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
+
+ ///
+ /// Brings the thread that created the specified window into the foreground and activates the window. Keyboard input is
+ /// directed to the window, and various visual cues are changed for the user. The system assigns a slightly higher
+ /// priority to the thread that created the foreground window than it does to other threads.
+ /// See for https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539%28v=vs.85%29.aspx more information.
+ ///
+ ///
+ /// C++ ( hWnd [in]. Type: HWND )
A handle to the window that should be activated and brought to the foreground.
+ ///
+ ///
+ /// true or nonzero if the window was brought to the foreground, false or zero If the window was not
+ /// brought to the foreground.
+ ///
+ ///
+ /// The system restricts which processes can set the foreground window. A process can set the foreground window only if
+ /// one of the following conditions is true:
+ ///
+ ///
+ /// Conditions
+ ///
+ /// - The process is the foreground process.
+ /// - The process was started by the foreground process.
+ /// - The process received the last input event.
+ /// - There is no foreground process.
+ /// - The process is being debugged.
+ /// - The foreground process is not a Modern Application or the Start Screen.
+ /// - The foreground is not locked (see LockSetForegroundWindow).
+ /// - The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
+ /// - No menus are active.
+ ///
+ ///
+ /// An application cannot force a window to the foreground while the user is working with another window.
+ /// Instead, Windows flashes the taskbar button of the window to notify the user.
+ ///
+ ///
+ /// A process that can set the foreground window can enable another process to set the foreground window by
+ /// calling the AllowSetForegroundWindow function. The process specified by dwProcessId loses the ability to set
+ /// the foreground window the next time the user generates input, unless the input is directed at that process, or
+ /// the next time a process calls AllowSetForegroundWindow, unless that process is specified.
+ ///
+ ///
+ /// The foreground process can disable calls to SetForegroundWindow by calling the LockSetForegroundWindow
+ /// function.
+ ///
+ ///
+ // For Windows Mobile, replace user32.dll with coredll.dll
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ ///
+ /// Retrieves the show state and the restored, minimized, and maximized positions of the specified window.
+ ///
+ ///
+ /// A handle to the window.
+ ///
+ ///
+ /// A pointer to the WINDOWPLACEMENT structure that receives the show state and position information.
+ ///
+ /// Before calling GetWindowPlacement, set the length member to sizeof(WINDOWPLACEMENT). GetWindowPlacement fails if lpwndpl-> length is not set correctly.
+ ///
+ ///
+ ///
+ /// If the function succeeds, the return value is nonzero.
+ ///
+ /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
+ ///
+ ///
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
+
+ ///
+ /// Sets the show state and the restored, minimized, and maximized positions of the specified window.
+ ///
+ ///
+ /// A handle to the window.
+ ///
+ ///
+ /// A pointer to a WINDOWPLACEMENT structure that specifies the new show state and window positions.
+ ///
+ /// Before calling SetWindowPlacement, set the length member of the WINDOWPLACEMENT structure to sizeof(WINDOWPLACEMENT). SetWindowPlacement fails if the length member is not set correctly.
+ ///
+ ///
+ ///
+ /// If the function succeeds, the return value is nonzero.
+ ///
+ /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
+ ///
+ ///
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
+
+ ///
+ /// Contains information about the placement of a window on the screen.
+ ///
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WINDOWPLACEMENT
+ {
+ ///
+ /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
+ ///
+ /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly.
+ ///
+ ///
+ public int Length;
+
+ ///
+ /// Specifies flags that control the position of the minimized window and the method by which the window is restored.
+ ///
+ public int Flags;
+
+ ///
+ /// The current show state of the window.
+ ///
+ public ShowWindowCommands ShowCmd;
+
+ ///
+ /// The coordinates of the window's upper-left corner when the window is minimized.
+ ///
+ public Point MinPosition;
+
+ ///
+ /// The coordinates of the window's upper-left corner when the window is maximized.
+ ///
+ public Point MaxPosition;
+
+ ///
+ /// The window's coordinates when the window is in the restored position.
+ ///
+ public Rect NormalPosition;
+
+ ///
+ /// Gets the default (empty) value.
+ ///
+ public static WINDOWPLACEMENT Default
+ {
+ get
+ {
+ WINDOWPLACEMENT result = new WINDOWPLACEMENT();
+ result.Length = Marshal.SizeOf(result);
+ return result;
+ }
+ }
+ }
+
+ public enum ShowWindowCommands
+ {
+ ///
+ /// Hides the window and activates another window.
+ ///
+ Hide = 0,
+ ///
+ /// Activates and displays a window. If the window is minimized or
+ /// maximized, the system restores it to its original size and position.
+ /// An application should specify this flag when displaying the window
+ /// for the first time.
+ ///
+ Normal = 1,
+ ///
+ /// Activates the window and displays it as a minimized window.
+ ///
+ ShowMinimized = 2,
+ ///
+ /// Maximizes the specified window.
+ ///
+ Maximize = 3, // is this the right value?
+ ///
+ /// Activates the window and displays it as a maximized window.
+ ///
+ ShowMaximized = 3,
+ ///
+ /// Displays a window in its most recent size and position. This value
+ /// is similar to , except
+ /// the window is not activated.
+ ///
+ ShowNoActivate = 4,
+ ///
+ /// Activates the window and displays it in its current size and position.
+ ///
+ Show = 5,
+ ///
+ /// Minimizes the specified window and activates the next top-level
+ /// window in the Z order.
+ ///
+ Minimize = 6,
+ ///
+ /// Displays the window as a minimized window. This value is similar to
+ /// , except the
+ /// window is not activated.
+ ///
+ ShowMinNoActive = 7,
+ ///
+ /// Displays the window in its current size and position. This value is
+ /// similar to , except the
+ /// window is not activated.
+ ///
+ ShowNA = 8,
+ ///
+ /// Activates and displays the window. If the window is minimized or
+ /// maximized, the system restores it to its original size and position.
+ /// An application should specify this flag when restoring a minimized window.
+ ///
+ Restore = 9,
+ ///
+ /// Sets the show state based on the SW_* value specified in the
+ /// STARTUPINFO structure passed to the CreateProcess function by the
+ /// program that started the application.
+ ///
+ ShowDefault = 10,
+ ///
+ /// Windows 2000/XP: Minimizes a window, even if the thread
+ /// that owns the window is not responding. This flag should only be
+ /// used when minimizing windows from a different thread.
+ ///
+ ForceMinimize = 11
+ }
+
+ [DllImport("user32.dll")]
+ public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool IsWindow(IntPtr hWnd);
+ }
+}
diff --git a/VDash/VDash.csproj b/VDash/VDash.csproj
index e222d45..ba4a362 100644
--- a/VDash/VDash.csproj
+++ b/VDash/VDash.csproj
@@ -66,6 +66,7 @@
+
@@ -84,7 +85,13 @@
MSBuild:Compile
Designer
+
+
+
+
+
+
@@ -96,7 +103,7 @@
App.xaml
Code
-
+
MainWindow.xaml
Code
diff --git a/VDash/WindowSelectors/FromProcessName.cs b/VDash/WindowSelectors/FromProcessName.cs
index 1685e90..abc0290 100644
--- a/VDash/WindowSelectors/FromProcessName.cs
+++ b/VDash/WindowSelectors/FromProcessName.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Linq;
+using VDash.Utilities;
namespace VDash.WindowSelectors
{
@@ -17,9 +18,15 @@ namespace VDash.WindowSelectors
m_ProcName = procName;
}
+ Process[] processes = null;
public Process[] GetProcesses()
{
- return Process.GetProcessesByName(m_ProcName);
+ if(processes == null)
+ {
+ processes = Process.GetProcessesByName(m_ProcName);
+ }
+
+ return processes;
}
public Process GetProcess()
@@ -46,5 +53,10 @@ namespace VDash.WindowSelectors
}
return ret;
}
+
+ public bool WindowExists()
+ {
+ return User32.IsWindow(GetWindowHandle());
+ }
}
}
diff --git a/VDash/WindowSelectors/FromWindowName.cs b/VDash/WindowSelectors/FromWindowName.cs
index 39af695..dba5b31 100644
--- a/VDash/WindowSelectors/FromWindowName.cs
+++ b/VDash/WindowSelectors/FromWindowName.cs
@@ -80,6 +80,19 @@ namespace VDash.WindowSelectors
return Window;
}
+ public bool WindowExists()
+ {
+ try
+ {
+ var window = Window;
+ }
+ catch (WindowNotFoundException)
+ {
+ return false;
+ }
+ return true;
+ }
+
public enum TitleSearchType
{
Contains = 0,
diff --git a/VDash/WindowSelectors/IWindowSelector.cs b/VDash/WindowSelectors/IWindowSelector.cs
index b38dc8e..31cb161 100644
--- a/VDash/WindowSelectors/IWindowSelector.cs
+++ b/VDash/WindowSelectors/IWindowSelector.cs
@@ -9,6 +9,7 @@ namespace VDash.WindowSelectors
{
IntPtr GetWindowHandle();
WindowInfo GetWindowInfo();
+ bool WindowExists();
}
public class WindowNotFoundException : Exception
@@ -53,6 +54,10 @@ namespace VDash.WindowSelectors
return Title;
}
+ ///
+ /// Gets a list of all windows in the current desktop session.
+ ///
+ ///
public static WindowInfo[] GetWindowList()
{
List windows = new List();