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()}");
|
||||
}
|
||||
|
||||
Overlay.WindowOverlay overlay = new Overlay.WindowOverlay("TestKey", "Window Dash");
|
||||
overlay.Create();
|
||||
var discordSelector = new WindowSelectors.FromWindowTitle("Discord", WindowSelectors.FromWindowTitle.TitleSearchType.Contains);
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Valve.VR;
|
||||
using VDash.WindowSelectors;
|
||||
|
||||
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; }
|
||||
private Task m_EventPumpTask = null;
|
||||
bool m_Created = false;
|
||||
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()
|
||||
{
|
||||
if (m_Created) { throw new Exception("This overlay has already been created."); }
|
||||
ConsoleManager.Show();
|
||||
ulong thumbHandle = OpenVR.k_ulOverlayHandleInvalid;
|
||||
|
||||
|
|
@ -43,35 +66,72 @@ namespace VDash.Overlay
|
|||
|
||||
overlay.SetOverlayWidthInMeters(m_Handle, 2.5f);
|
||||
overlay.SetOverlayInputMethod(m_Handle, VROverlayInputMethod.Mouse);
|
||||
|
||||
m_Running = true;
|
||||
m_EventPumpTask = Task.Factory.StartNew(() => { EventPump(); });
|
||||
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()
|
||||
{
|
||||
#region Rendering setup
|
||||
var overlay = OpenVR.Overlay;
|
||||
|
||||
//Skip rendering if overlay isn't visible
|
||||
if ((m_Handle == OpenVR.k_ulOverlayHandleInvalid) || overlay == null || (!overlay.IsOverlayVisible(m_Handle)))
|
||||
{
|
||||
//return;
|
||||
}
|
||||
|
||||
//Taken from here: https://stackoverflow.com/questions/6812068/c-sharp-which-is-the-fastest-way-to-take-a-screen-shot
|
||||
//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.
|
||||
|
||||
var factory = new Factory1();
|
||||
//Get first adapter
|
||||
var adapter = factory.GetAdapter1(0);
|
||||
//Get device from adapter
|
||||
var device = new SharpDX.Direct3D11.Device(adapter);
|
||||
|
||||
#endregion
|
||||
|
||||
//For FPS counter.
|
||||
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)
|
||||
{
|
||||
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
|
||||
VREvent_t vrEvent = new VREvent_t();
|
||||
|
||||
|
|
@ -99,7 +159,12 @@ namespace VDash.Overlay
|
|||
}
|
||||
case EVREventType.VREvent_OverlayShown:
|
||||
{
|
||||
Console.WriteLine("Overlay Shown!");
|
||||
OverlayShown?.Invoke(this, new EventArgs());
|
||||
break;
|
||||
}
|
||||
case EVREventType.VREvent_OverlayHidden:
|
||||
{
|
||||
OverlayHidden?.Invoke(this, new EventArgs());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -108,62 +173,73 @@ namespace VDash.Overlay
|
|||
#endregion
|
||||
|
||||
#region Acutally Render
|
||||
|
||||
var proc = Process.GetProcessesByName("discord")[1];
|
||||
var rect = new User32.Rect();
|
||||
User32.GetWindowRect(proc.MainWindowHandle, ref rect);
|
||||
|
||||
int width = rect.right - rect.left;
|
||||
int height = rect.bottom - rect.top;
|
||||
|
||||
using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
|
||||
IntPtr windowHandle = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
using (Graphics graphics = Graphics.FromImage(bmp))
|
||||
windowHandle = m_Selector.GetWindowHandle();
|
||||
}
|
||||
catch (WindowNotFoundException)
|
||||
{
|
||||
//Console.WriteLine($"[{m_Key}] {m_Name}: Window not found.");
|
||||
}
|
||||
|
||||
if (windowHandle != IntPtr.Zero)
|
||||
{
|
||||
var rect = new User32.Rect();
|
||||
User32.GetWindowRect(windowHandle, ref rect);
|
||||
|
||||
int width = rect.right - rect.left;
|
||||
int height = rect.bottom - rect.top;
|
||||
|
||||
using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
|
||||
{
|
||||
graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
|
||||
|
||||
var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
// Create Staging texture CPU-accessible
|
||||
var textureDesc = new Texture2DDescription
|
||||
using (Graphics graphics = Graphics.FromImage(bmp))
|
||||
{
|
||||
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
|
||||
};
|
||||
graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
|
||||
|
||||
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();
|
||||
texture.handle = screenTexture.NativePointer;
|
||||
texture.eType = ETextureType.DirectX;
|
||||
texture.eColorSpace = EColorSpace.Auto;
|
||||
EVROverlayError result = overlay.SetOverlayTexture(m_Handle, ref texture);
|
||||
if (result != EVROverlayError.None)
|
||||
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
|
||||
var textureDesc = new Texture2DDescription
|
||||
{
|
||||
Console.WriteLine($"Error rendering: {result}");
|
||||
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
|
||||
};
|
||||
|
||||
using (var screenTexture = new Texture2D(device, textureDesc, new SharpDX.DataRectangle(data.Scan0, data.Stride)))
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
frames++;
|
||||
|
||||
bmp.UnlockBits(data);
|
||||
}
|
||||
|
||||
frames++;
|
||||
|
||||
bmp.UnlockBits(data);
|
||||
//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();
|
||||
}
|
||||
//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();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
Thread.Sleep(10);
|
||||
Thread.Sleep(20);
|
||||
|
||||
if (DateTime.Now.Subtract(lastFps).TotalMilliseconds > 1000)
|
||||
{
|
||||
|
|
@ -172,6 +248,11 @@ namespace VDash.Overlay
|
|||
lastFps = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
//Clean up after we exit while loop.
|
||||
device.Dispose();
|
||||
adapter.Dispose();
|
||||
factory.Dispose();
|
||||
}
|
||||
|
||||
private class User32
|
||||
|
|
@ -191,7 +272,21 @@ namespace VDash.Overlay
|
|||
|
||||
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>
|
||||
<SubType>Designer</SubType>
|
||||
</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">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
@ -101,7 +104,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="OpenVR\openvr_api.cs" />
|
||||
<Compile Include="Overlay\DashOverlay.cs" />
|
||||
<Compile Include="Overlay\WindowOverlay.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<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