Generic interfaces implemented for both overlays and WindowSelectors.
This commit is contained in:
parent
0c220316e8
commit
ddbde07f92
|
|
@ -35,16 +35,13 @@ namespace VDash
|
||||||
throw new Exception($"Error connecting to VR: {initError.ToString()}");
|
throw new Exception($"Error connecting to VR: {initError.ToString()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay.WindowOverlay overlay = new Overlay.WindowOverlay("TestKey", "Window Dash");
|
var discordSelector = new WindowSelectors.FromWindowTitle("Discord", WindowSelectors.FromWindowTitle.TitleSearchType.Contains);
|
||||||
overlay.Create();
|
Overlay.WindowOverlay discordOverlay = new Overlay.WindowOverlay("discord", "Discord", discordSelector, true);
|
||||||
|
discordOverlay.Create();
|
||||||
|
|
||||||
|
Overlay.WindowOverlay obsOverlay = new Overlay.WindowOverlay("obs", "OBS", new WindowSelectors.FromProcessName("obs64"), false);
|
||||||
|
obsOverlay.Create();
|
||||||
|
|
||||||
//var screenStateLogger = new ScreenStateLogger();
|
|
||||||
//screenStateLogger.ScreenRefreshed += (sender, data) =>
|
|
||||||
//{
|
|
||||||
// Console.WriteLine("Got screen data!");
|
|
||||||
//};
|
|
||||||
//screenStateLogger.Start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Valve.VR;
|
|
||||||
|
|
||||||
namespace VDash.Overlay
|
|
||||||
{
|
|
||||||
public class DashOverlay
|
|
||||||
{
|
|
||||||
internal ulong m_Handle;
|
|
||||||
public ulong Handle
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return m_Handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string m_Key;
|
|
||||||
public string Key { get { return m_Key; } }
|
|
||||||
|
|
||||||
internal string m_Name;
|
|
||||||
public string Name { get { return m_Name; } }
|
|
||||||
|
|
||||||
|
|
||||||
public DashOverlay(string key, string name)
|
|
||||||
{
|
|
||||||
m_Key = key;
|
|
||||||
m_Name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VDash.Overlay
|
||||||
|
{
|
||||||
|
public interface IOverlay : IDisposable
|
||||||
|
{
|
||||||
|
event EventHandler OverlayShown;
|
||||||
|
event EventHandler OverlayHidden;
|
||||||
|
|
||||||
|
ulong Handle { get; }
|
||||||
|
string Name { get; }
|
||||||
|
string Key { get; }
|
||||||
|
|
||||||
|
void Create();
|
||||||
|
void Destroy();
|
||||||
|
string GetOverlayName();
|
||||||
|
void SetOverlayName(string newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,34 +1,57 @@
|
||||||
using SharpDX;
|
using SharpDX.Direct3D11;
|
||||||
using SharpDX.Direct3D11;
|
|
||||||
using SharpDX.DXGI;
|
using SharpDX.DXGI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Valve.VR;
|
using Valve.VR;
|
||||||
|
using VDash.WindowSelectors;
|
||||||
|
|
||||||
namespace VDash.Overlay
|
namespace VDash.Overlay
|
||||||
{
|
{
|
||||||
public class WindowOverlay : DashOverlay,IDisposable
|
public class WindowOverlay : IOverlay
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public event EventHandler OverlayShown;
|
||||||
|
public event EventHandler OverlayHidden;
|
||||||
|
|
||||||
public CVRSystem VRSystem { get; private set; }
|
public CVRSystem VRSystem { get; private set; }
|
||||||
private Task m_EventPumpTask = null;
|
private Task m_EventPumpTask = null;
|
||||||
bool m_Created = false;
|
bool m_Created = false;
|
||||||
bool m_Running = true;
|
bool m_Running = true;
|
||||||
|
public bool UpdateDashboardName { get; set; } = false;
|
||||||
|
|
||||||
public WindowOverlay(string key, string name) : base(key, name)
|
internal ulong m_Handle;
|
||||||
|
public ulong Handle
|
||||||
{
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_Handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string m_Key;
|
||||||
|
public string Key { get { return m_Key; } }
|
||||||
|
|
||||||
|
internal string m_Name;
|
||||||
|
public string Name { get { return m_Name; } }
|
||||||
|
|
||||||
|
IWindowSelector m_Selector = null;
|
||||||
|
|
||||||
|
public WindowOverlay(string key, string name, IWindowSelector selector, bool updateDashboardName)
|
||||||
|
{
|
||||||
|
m_Key = key;
|
||||||
|
m_Name = name;
|
||||||
|
m_Selector = selector;
|
||||||
|
UpdateDashboardName = updateDashboardName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Create()
|
public void Create()
|
||||||
{
|
{
|
||||||
|
if (m_Created) { throw new Exception("This overlay has already been created."); }
|
||||||
ConsoleManager.Show();
|
ConsoleManager.Show();
|
||||||
ulong thumbHandle = OpenVR.k_ulOverlayHandleInvalid;
|
ulong thumbHandle = OpenVR.k_ulOverlayHandleInvalid;
|
||||||
|
|
||||||
|
|
@ -43,35 +66,72 @@ namespace VDash.Overlay
|
||||||
|
|
||||||
overlay.SetOverlayWidthInMeters(m_Handle, 2.5f);
|
overlay.SetOverlayWidthInMeters(m_Handle, 2.5f);
|
||||||
overlay.SetOverlayInputMethod(m_Handle, VROverlayInputMethod.Mouse);
|
overlay.SetOverlayInputMethod(m_Handle, VROverlayInputMethod.Mouse);
|
||||||
|
|
||||||
|
m_Running = true;
|
||||||
m_EventPumpTask = Task.Factory.StartNew(() => { EventPump(); });
|
m_EventPumpTask = Task.Factory.StartNew(() => { EventPump(); });
|
||||||
m_Created = true;
|
m_Created = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
if (!m_Created) { throw new Exception("This object is not created."); }
|
||||||
|
m_Created = false;
|
||||||
|
|
||||||
|
//Signal render thread to exit
|
||||||
|
m_Running = false;
|
||||||
|
|
||||||
|
//Wait for render thread to exit
|
||||||
|
m_EventPumpTask.Wait();
|
||||||
|
|
||||||
|
var err = OpenVR.Overlay.DestroyOverlay(m_Handle);
|
||||||
|
|
||||||
|
if( err != EVROverlayError.None)
|
||||||
|
{
|
||||||
|
throw new Exception($"Error destroying overlay: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void EventPump()
|
private void EventPump()
|
||||||
{
|
{
|
||||||
#region Rendering setup
|
#region Rendering setup
|
||||||
var overlay = OpenVR.Overlay;
|
var overlay = OpenVR.Overlay;
|
||||||
|
|
||||||
//Skip rendering if overlay isn't visible
|
//Partially taken from here: https://stackoverflow.com/questions/6812068/c-sharp-which-is-the-fastest-way-to-take-a-screen-shot
|
||||||
if ((m_Handle == OpenVR.k_ulOverlayHandleInvalid) || overlay == null || (!overlay.IsOverlayVisible(m_Handle)))
|
//Desktop Duplication didn't work well, hence is why we're sticking with GDI. Still this is faster than using SetTextureRaw.
|
||||||
{
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Taken from here: https://stackoverflow.com/questions/6812068/c-sharp-which-is-the-fastest-way-to-take-a-screen-shot
|
|
||||||
|
|
||||||
var factory = new Factory1();
|
var factory = new Factory1();
|
||||||
//Get first adapter
|
//Get first adapter
|
||||||
var adapter = factory.GetAdapter1(0);
|
var adapter = factory.GetAdapter1(0);
|
||||||
//Get device from adapter
|
//Get device from adapter
|
||||||
var device = new SharpDX.Direct3D11.Device(adapter);
|
var device = new SharpDX.Direct3D11.Device(adapter);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
//For FPS counter.
|
||||||
int frames = 0;
|
int frames = 0;
|
||||||
DateTime lastFps = DateTime.MinValue;
|
DateTime lastFps = DateTime.MinValue;
|
||||||
|
DateTime lastNameCheck = DateTime.MinValue;
|
||||||
while (m_Running)
|
while (m_Running)
|
||||||
{
|
{
|
||||||
|
//Update the SteamVR dash name if necessary, only once a second.
|
||||||
|
if (UpdateDashboardName && DateTime.Now.Subtract(lastNameCheck).TotalMilliseconds >= 1000)
|
||||||
|
{
|
||||||
|
var window = m_Selector.GetWindowInfo();
|
||||||
|
if(GetOverlayName() != window.Title)
|
||||||
|
{
|
||||||
|
SetOverlayName(window.Title);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastNameCheck = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Skip rendering if overlay isn't visible
|
||||||
|
//if ((m_Handle == OpenVR.k_ulOverlayHandleInvalid) || overlay == null || (!overlay.IsOverlayVisible(m_Handle)))
|
||||||
|
//{
|
||||||
|
// Thread.Sleep(100);
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
#region VR Events
|
#region VR Events
|
||||||
VREvent_t vrEvent = new VREvent_t();
|
VREvent_t vrEvent = new VREvent_t();
|
||||||
|
|
||||||
|
|
@ -99,7 +159,12 @@ namespace VDash.Overlay
|
||||||
}
|
}
|
||||||
case EVREventType.VREvent_OverlayShown:
|
case EVREventType.VREvent_OverlayShown:
|
||||||
{
|
{
|
||||||
Console.WriteLine("Overlay Shown!");
|
OverlayShown?.Invoke(this, new EventArgs());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EVREventType.VREvent_OverlayHidden:
|
||||||
|
{
|
||||||
|
OverlayHidden?.Invoke(this, new EventArgs());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,10 +173,20 @@ namespace VDash.Overlay
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Acutally Render
|
#region Acutally Render
|
||||||
|
IntPtr windowHandle = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
windowHandle = m_Selector.GetWindowHandle();
|
||||||
|
}
|
||||||
|
catch (WindowNotFoundException)
|
||||||
|
{
|
||||||
|
//Console.WriteLine($"[{m_Key}] {m_Name}: Window not found.");
|
||||||
|
}
|
||||||
|
|
||||||
var proc = Process.GetProcessesByName("discord")[1];
|
if (windowHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
var rect = new User32.Rect();
|
var rect = new User32.Rect();
|
||||||
User32.GetWindowRect(proc.MainWindowHandle, ref rect);
|
User32.GetWindowRect(windowHandle, ref rect);
|
||||||
|
|
||||||
int width = rect.right - rect.left;
|
int width = rect.right - rect.left;
|
||||||
int height = rect.bottom - rect.top;
|
int height = rect.bottom - rect.top;
|
||||||
|
|
@ -124,6 +199,7 @@ namespace VDash.Overlay
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
//Render using graphics card, in DirectX context.
|
||||||
// Create Staging texture CPU-accessible
|
// Create Staging texture CPU-accessible
|
||||||
var textureDesc = new Texture2DDescription
|
var textureDesc = new Texture2DDescription
|
||||||
{
|
{
|
||||||
|
|
@ -140,7 +216,6 @@ namespace VDash.Overlay
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var screenTexture = new Texture2D(device, textureDesc, new SharpDX.DataRectangle(data.Scan0, data.Stride)))
|
using (var screenTexture = new Texture2D(device, textureDesc, new SharpDX.DataRectangle(data.Scan0, data.Stride)))
|
||||||
//using (var screenTexture = new Texture2D(device, textureDesc))
|
|
||||||
{
|
{
|
||||||
var texture = new Texture_t();
|
var texture = new Texture_t();
|
||||||
texture.handle = screenTexture.NativePointer;
|
texture.handle = screenTexture.NativePointer;
|
||||||
|
|
@ -161,9 +236,10 @@ namespace VDash.Overlay
|
||||||
device.ImmediateContext.Flush();
|
device.ImmediateContext.Flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(20);
|
||||||
|
|
||||||
if (DateTime.Now.Subtract(lastFps).TotalMilliseconds > 1000)
|
if (DateTime.Now.Subtract(lastFps).TotalMilliseconds > 1000)
|
||||||
{
|
{
|
||||||
|
|
@ -172,6 +248,11 @@ namespace VDash.Overlay
|
||||||
lastFps = DateTime.Now;
|
lastFps = DateTime.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Clean up after we exit while loop.
|
||||||
|
device.Dispose();
|
||||||
|
adapter.Dispose();
|
||||||
|
factory.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class User32
|
private class User32
|
||||||
|
|
@ -191,7 +272,21 @@ namespace VDash.Overlay
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
//FIXME: Actually implement this...
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetOverlayName()
|
||||||
|
{
|
||||||
|
EVROverlayError err = EVROverlayError.None;
|
||||||
|
StringBuilder sb = new StringBuilder(255);
|
||||||
|
uint res = OpenVR.Overlay.GetOverlayName(m_Handle, sb, 255, ref err);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetOverlayName(string newName)
|
||||||
|
{
|
||||||
|
OpenVR.Overlay.SetOverlayName(m_Handle, newName);
|
||||||
|
m_Name = newName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
using SharpDX;
|
|
||||||
using SharpDX.Direct3D11;
|
|
||||||
using SharpDX.DXGI;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace VDash
|
|
||||||
{
|
|
||||||
public class ScreenStateLogger
|
|
||||||
{
|
|
||||||
private byte[] _previousScreen;
|
|
||||||
private bool _run, _init;
|
|
||||||
|
|
||||||
public int Size { get; private set; }
|
|
||||||
public ScreenStateLogger()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
_run = true;
|
|
||||||
var factory = new Factory1();
|
|
||||||
//Get first adapter
|
|
||||||
var adapter = factory.GetAdapter1(0);
|
|
||||||
//Get device from adapter
|
|
||||||
var device = new SharpDX.Direct3D11.Device(adapter);
|
|
||||||
//Get front buffer of the adapter
|
|
||||||
var output = adapter.GetOutput(0);
|
|
||||||
var output1 = output.QueryInterface<Output1>();
|
|
||||||
|
|
||||||
// Width/Height of desktop to capture
|
|
||||||
int width = output.Description.DesktopBounds.Right;
|
|
||||||
int height = output.Description.DesktopBounds.Bottom;
|
|
||||||
|
|
||||||
// Create Staging texture CPU-accessible
|
|
||||||
var textureDesc = new Texture2DDescription
|
|
||||||
{
|
|
||||||
CpuAccessFlags = CpuAccessFlags.Read,
|
|
||||||
BindFlags = BindFlags.None,
|
|
||||||
Format = Format.B8G8R8A8_UNorm,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
OptionFlags = ResourceOptionFlags.None,
|
|
||||||
MipLevels = 1,
|
|
||||||
ArraySize = 1,
|
|
||||||
SampleDescription = { Count = 1, Quality = 0 },
|
|
||||||
Usage = ResourceUsage.Staging
|
|
||||||
};
|
|
||||||
var screenTexture = new Texture2D(device, textureDesc);
|
|
||||||
|
|
||||||
Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
// Duplicate the output
|
|
||||||
using (var duplicatedOutput = output1.DuplicateOutput(device))
|
|
||||||
{
|
|
||||||
while (_run)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SharpDX.DXGI.Resource screenResource;
|
|
||||||
OutputDuplicateFrameInformation duplicateFrameInformation;
|
|
||||||
|
|
||||||
// Try to get duplicated frame within given time is ms
|
|
||||||
duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource);
|
|
||||||
|
|
||||||
// copy resource into memory that can be accessed by the CPU
|
|
||||||
using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
|
|
||||||
device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
|
|
||||||
|
|
||||||
// Get the desktop capture texture
|
|
||||||
var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
|
|
||||||
|
|
||||||
// Create Drawing.Bitmap
|
|
||||||
using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
|
|
||||||
{
|
|
||||||
var boundsRect = new Rectangle(0, 0, width, height);
|
|
||||||
|
|
||||||
// Copy pixels from screen capture Texture to GDI bitmap
|
|
||||||
var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
|
|
||||||
var sourcePtr = mapSource.DataPointer;
|
|
||||||
var destPtr = mapDest.Scan0;
|
|
||||||
for (int y = 0; y < height; y++)
|
|
||||||
{
|
|
||||||
// Copy a single line
|
|
||||||
Utilities.CopyMemory(destPtr, sourcePtr, width * 4);
|
|
||||||
|
|
||||||
// Advance pointers
|
|
||||||
sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
|
|
||||||
destPtr = IntPtr.Add(destPtr, mapDest.Stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release source and dest locks
|
|
||||||
bitmap.UnlockBits(mapDest);
|
|
||||||
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
|
|
||||||
|
|
||||||
using (var ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
bitmap.Save(ms, ImageFormat.Bmp);
|
|
||||||
if (duplicateFrameInformation.AccumulatedFrames > 0)
|
|
||||||
{
|
|
||||||
ScreenRefreshed?.Invoke(this, ms.ToArray());
|
|
||||||
}
|
|
||||||
_init = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
screenResource.Dispose();
|
|
||||||
duplicatedOutput.ReleaseFrame();
|
|
||||||
}
|
|
||||||
catch (SharpDXException e)
|
|
||||||
{
|
|
||||||
if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
|
|
||||||
{
|
|
||||||
Trace.TraceError(e.Message);
|
|
||||||
Trace.TraceError(e.StackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
while (!_init) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_run = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EventHandler<byte[]> ScreenRefreshed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -84,7 +84,10 @@
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</ApplicationDefinition>
|
</ApplicationDefinition>
|
||||||
<Compile Include="ScreenStateLogger.cs" />
|
<Compile Include="Overlay\IOverlay.cs" />
|
||||||
|
<Compile Include="WindowSelectors\FromProcessName.cs" />
|
||||||
|
<Compile Include="WindowSelectors\FromWindowName.cs" />
|
||||||
|
<Compile Include="WindowSelectors\IWindowSelector.cs" />
|
||||||
<Page Include="MainWindow.xaml">
|
<Page Include="MainWindow.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
|
@ -101,7 +104,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="OpenVR\openvr_api.cs" />
|
<Compile Include="OpenVR\openvr_api.cs" />
|
||||||
<Compile Include="Overlay\DashOverlay.cs" />
|
|
||||||
<Compile Include="Overlay\WindowOverlay.cs" />
|
<Compile Include="Overlay\WindowOverlay.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace VDash.WindowSelectors
|
||||||
|
{
|
||||||
|
public class FromProcessName : IWindowSelector
|
||||||
|
{
|
||||||
|
string m_ProcName = "";
|
||||||
|
public FromProcessName(string procName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(procName))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("procName", "procName must not be null, whitespace, or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ProcName = procName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Process[] GetProcesses()
|
||||||
|
{
|
||||||
|
return Process.GetProcessesByName(m_ProcName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Process GetProcess()
|
||||||
|
{
|
||||||
|
var procs = GetProcesses();
|
||||||
|
|
||||||
|
if (procs.Count() <= 0)
|
||||||
|
{
|
||||||
|
throw new WindowNotFoundException($"No process found with name {m_ProcName}.");
|
||||||
|
}
|
||||||
|
return procs.First();
|
||||||
|
}
|
||||||
|
public IntPtr GetWindowHandle()
|
||||||
|
{
|
||||||
|
return GetProcess().MainWindowHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowInfo GetWindowInfo()
|
||||||
|
{
|
||||||
|
var ret = WindowInfo.GetWindowList().SingleOrDefault(x => x.hWnd == GetWindowHandle());
|
||||||
|
if(ret == null)
|
||||||
|
{
|
||||||
|
throw new WindowNotFoundException($"Unable to locate window for process {m_ProcName}.");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace VDash.WindowSelectors
|
||||||
|
{
|
||||||
|
public class FromWindowTitle : IWindowSelector
|
||||||
|
{
|
||||||
|
WindowInfo m_Window = null;
|
||||||
|
public bool RefreshOnGet { get; set; } = false;
|
||||||
|
public string TitleFilter { get; set; } = "";
|
||||||
|
public TitleSearchType SearchType { get; set; } = TitleSearchType.Contains;
|
||||||
|
|
||||||
|
public WindowInfo Window
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (RefreshOnGet)
|
||||||
|
{
|
||||||
|
m_Window = GetWindowUsingFilter();
|
||||||
|
return m_Window;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_Window == null)
|
||||||
|
{
|
||||||
|
m_Window = GetWindowUsingFilter();
|
||||||
|
}
|
||||||
|
return m_Window;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceWindowRefresh()
|
||||||
|
{
|
||||||
|
m_Window = GetWindowUsingFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FromWindowTitle(string windowFilter, TitleSearchType searchType)
|
||||||
|
{
|
||||||
|
TitleFilter = windowFilter;
|
||||||
|
SearchType = searchType;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal WindowInfo GetWindowUsingFilter()
|
||||||
|
{
|
||||||
|
var windows = WindowInfo.GetWindowList();
|
||||||
|
if (SearchType == TitleSearchType.Contains)
|
||||||
|
{
|
||||||
|
var results = windows.Where(x => x.Title.ToLower().Contains(TitleFilter.ToLower()));
|
||||||
|
if(results.Count() == 0)
|
||||||
|
{
|
||||||
|
throw new WindowNotFoundException("No windows match the filter settings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.First();
|
||||||
|
}
|
||||||
|
if(SearchType == TitleSearchType.Regex)
|
||||||
|
{
|
||||||
|
var results = windows.Where(x => Regex.IsMatch(x.Title, TitleFilter));
|
||||||
|
|
||||||
|
if (results.Count() == 0)
|
||||||
|
{
|
||||||
|
throw new WindowNotFoundException("No windows match the filter settings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new WindowNotFoundException("Error fetching matching window.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr GetWindowHandle()
|
||||||
|
{
|
||||||
|
return Window.hWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowInfo GetWindowInfo()
|
||||||
|
{
|
||||||
|
return Window;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TitleSearchType
|
||||||
|
{
|
||||||
|
Contains = 0,
|
||||||
|
Regex = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VDash.WindowSelectors
|
||||||
|
{
|
||||||
|
public interface IWindowSelector
|
||||||
|
{
|
||||||
|
IntPtr GetWindowHandle();
|
||||||
|
WindowInfo GetWindowInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WindowNotFoundException : Exception
|
||||||
|
{
|
||||||
|
public WindowNotFoundException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WindowInfo
|
||||||
|
{
|
||||||
|
#region P/Invokes
|
||||||
|
private delegate bool EnumWindowsProc(IntPtr hWnd, int lPar);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int EnumWindows(EnumWindowsProc callPtr, int lPar);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsProc lpfn, int lPar);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool IsWindowVisible(IntPtr hWnd);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
public string Title { get; private set; }
|
||||||
|
public IntPtr hWnd { get; private set; }
|
||||||
|
public bool IsVisible { get; private set; }
|
||||||
|
|
||||||
|
public WindowInfo(string title, IntPtr hWnd, bool isVisible)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
this.hWnd = hWnd;
|
||||||
|
this.IsVisible = isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WindowInfo[] GetWindowList()
|
||||||
|
{
|
||||||
|
List<WindowInfo> windows = new List<WindowInfo>();
|
||||||
|
|
||||||
|
EnumWindowsProc del = ((IntPtr hWnd, int lPar) =>
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(255);
|
||||||
|
|
||||||
|
GetWindowText(hWnd, sb, 255);
|
||||||
|
bool isVisible = IsWindowVisible(hWnd);
|
||||||
|
|
||||||
|
var windowInfo = new WindowInfo(sb.ToString(), hWnd, isVisible);
|
||||||
|
|
||||||
|
windows.Add(windowInfo);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
EnumDesktopWindows(IntPtr.Zero, del, 0);
|
||||||
|
|
||||||
|
return windows.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue