Closed zezba9000 closed 6 years ago
@TheDeveloperGuy That wont work. This isn't for a OpenVR "driver". This is for an OpenVR utility which uses the other header file that doesn't include that method.
Do you want to see a neat trick? start openvr and click here:
@MHDante Interesting. However this isn't working on my PC.
I get this result:
{
"jsonid" : "vr_console_command",
"sError" : "Connection Broken"
}
Make a 'hook' driver and hook the DebugRequest() method for any HMD that gets Activate() called successfully and send the driver shutdown event from your DebugRequest() override.
@MHDante Your info did come in handy but it was missing some stuff. You need to first create a WebSocket. The method below can be ported to C# or any lang that has WebSocket clients. (method below works)
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="http://localhost:8998/shared/js/jquery-3.1.1.min.js"></script>
</head>
<body>
<script>
function OnWebSocketOpen() {
console.log("Open");
m_wsWebSocketToServer.send( "console_open" );
$.ajax({
type: "GET",
url: 'http://localhost:8998/console_command.action?sCommand=quit',
data:{},
async:true,
dataType : 'jsonp', //you may use jsonp for cross origin request
crossDomain:true,
success: function(data, status, xhr) {
alert(xhr.getResponseHeader('Location'));
},
complete: function( jqXHR, sTextStatus ) {
console.log("Close");
m_wsWebSocketToServer.send( "console_close" );
}
});
}
function OnWebSocketMessage() {
}
// connect
console.log("Starting");
var m_wsWebSocketToServer = new WebSocket( "ws://localhost:8998" );
m_wsWebSocketToServer.addEventListener( "open", OnWebSocketOpen );
m_wsWebSocketToServer.addEventListener( "message", OnWebSocketMessage );
console.log("End");
</script>
</body>
</html>
Hi @zezba9000
How do I use this code
I mean for now I have stored this code in HTML file and starting it by Process.
is there any other convenient way? I am using Windows Forms (C#) Thanks in Advance.
@vinayak-vc To test out that code copy and put it in a .html file.
BUT they have added in the ability to send a normal close window event to the vrmonitor window. Use that instead.
Code below is a C# example of how to determine what the main window is:
private static BOOL IsMainWindow(HWND handle)
{
bool valid = GetWindow(handle, GW_OWNER) == HANDLE.Zero && IsWindowVisible(handle) != FALSE;
return valid ? TRUE : FALSE;
}
Just look up how to close the main Window from C++ for more info. NOTE: Getting the process in C# and calling CloseMainWindow probably wont work as your C# process probably didn't open vrmonitor directly.
@zezba9000
Can you tell me how do I run that HTML file without browser I mean is that possible to run an HTML code in the command window and kill it after some time?
Here in my case, I am developing one lobby where you can start a game in VR so when the game is closed directly with the Oculus Menu button then the steamVR is losing focus so even if I start my lobby again the lobby will start but the Oculus won't show the game window in it. So I am checking if the game is closed directly then steamvr will be killed and start again.
So when I kill the steamvr using that HTML file it works like charm but also causing distraction as it opens in a browser so I need an alternative to that
Thanks for listing to me.
Yes you can use C# with that HTTP method.
For .NET Framework use: https://github.com/sta/websocket-sharp Then you can use this method below (Just replace 'HTTPExtensions.MakeRequestAsync' with your standard C# HttpWebRequest logic)
public static bool SendCommand(string command)
{
try
{
using (var ws = new WebSocket("ws://localhost:8998"))
{
bool openWait = true;
ws.OnOpen += async delegate (object sender, EventArgs e)
{
ws.Send("console_open");
await HTTPExtensions.MakeRequestAsync("http://localhost:8998/console_command.action?sCommand=" + command, "GET");
};
ws.Connect();
int waitCount = 3;
while (openWait && waitCount != 0)
{
Thread.Sleep(1000);
--waitCount;
}
if (ws.IsAlive) ws.Send("console_close");
}
}
catch (Exception e)
{
Console.WriteLine("Failed to send VRMonitor close event: " + e.Message);
return false;
}
return true;
}
Then invoke the method like so: "SendCommand("quit");"
Fair warning: it's entirely possible that the HTTP protocol between the web console and vrserver will change down the line and break anything that tries to use it that isn't the web console. At the very least, we should probably be using POST instead of GET.
@JoeLudwig Yep, thats why I suggested just sending a normal Windows close event as this is the correct approach from an external process. This WebSocket hack was originally done when VRMonitor didn't respect standard windows close events. Now it does thank god ;)
Can you please post some code snippets which I can follow for "find window" and close them and also find relative windows to a similar process. I mean what I have to do?
Thanks.
Look at the answer from "Konstantin Spirin" in that StackOverflow link.
The reason you need to close all windows is because if there are sub windows (aka non-main windows) telling just the main window to close may not actually close the application.
@zezba9000
The solution worked like charm.
Thanks for your support. I have managed to complete the task that I wanted to do.
Thanks Again.
here is the final code
class CloseSTeamVRcs
{
public static void CloseSteam()
{
try
{
foreach (var handle in EnumerateProcessWindowHandles(
Process.GetProcessesByName("vrmonitor").First().Id))
{
StringBuilder message = new StringBuilder(1000);
SendMessage(handle, WM_CLOSE, message.Capacity, message);
Debug.WriteLine(message);
}
}
catch (System.Exception) { }
}
private const uint WM_CLOSE = 0x10;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam,
StringBuilder lParam);
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processId)
{
try
{
var handles = new List<IntPtr>();
foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
EnumThreadWindows(thread.Id,
(hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
return handles;
}
catch (System.Exception) { return null; }
}
}
Hiii @zezba9000
I have another question regarding SteamVR
Do you know how to make inractive overlay in SteamVR? I mean several images and buttons on the overlay.
I want to show one windows form as an overlay in steamvr.
Thanks
Here is the C lib code.
#include "stdafx.h"
#include "openvr.h"
#include <stdio.h>
#include <string>
#define EXPORT __declspec(dllexport)
vr::VROverlayHandle_t mainHandle = 0;
std::string lastError;
extern "C"
{
EXPORT int VR_OpenVR_GetLastError(char* errorBuffer, int errorBufferLength)
{
return lastError.copy(errorBuffer, errorBufferLength);
}
EXPORT bool VR_OpenVR_Init(char* overlayKey, char* overlayFriendlyName, float overlayWidthInMeters)
{
auto systemError = vr::VRInitError_None;
vr::IVRSystem *system = vr::VR_Init(&systemError, vr::VRApplication_Overlay);
if (systemError != vr::VRInitError_None)
{
lastError = "VR_Init failed: " + std::to_string(systemError);
return false;
}
auto compositor = vr::VRCompositor();
if (compositor == nullptr)
{
lastError = "Getting VRCompositor failed";
return false;
}
auto overlay = vr::VROverlay();
if (overlay == nullptr)
{
lastError = "Getting VROverlay failed";
return false;
}
vr::VROverlayError overlayError = vr::VROverlay()->CreateOverlay(overlayKey, overlayFriendlyName, &mainHandle);
if (overlayError != vr::VROverlayError_None)
{
lastError = "CreateDashboardOverlay failed: " + std::to_string(overlayError);
return false;
}
overlay->SetOverlayWidthInMeters(mainHandle, overlayWidthInMeters);
overlay->SetOverlayInputMethod(mainHandle, vr::VROverlayInputMethod_Mouse);
overlay->SetOverlayAlpha(mainHandle, 1.0f);
vr::VRTextureBounds_t bounds;
bounds.uMin = 0;
bounds.uMax = 1;
bounds.vMin = 0;
bounds.vMax = 1;
overlay->SetOverlayTextureBounds(mainHandle, &bounds);
return true;
}
EXPORT void VR_OpenVR_Shutdown()
{
if (mainHandle != 0)
{
vr::VROverlay()->DestroyOverlay(mainHandle);
mainHandle = 0;
}
vr::VR_Shutdown();
}
EXPORT bool VR_OpenVR_SetOverlayTexture(void* texturePtr)// ID3D11Resource
{
vr::Texture_t texture = {texturePtr, vr::TextureType_DirectX, vr::ColorSpace_Auto};
auto error = vr::VROverlay()->SetOverlayTexture(mainHandle, &texture);
if (error != vr::VROverlayError_None)
{
lastError = "SetOverlayTexture failed: " + std::to_string(error);
return false;
}
return true;
}
EXPORT bool VR_OpenVR_SetOverlayRaw(void *buffer, uint32_t width, uint32_t height, uint32_t depth)
{
auto error = vr::VROverlay()->SetOverlayRaw(mainHandle, buffer, width, height, depth);
if (error != vr::VROverlayError_None)
{
lastError = "SetOverlayTexture failed: " + std::to_string(error);
return false;
}
return true;
}
EXPORT bool VR_OpenVR_ShowOverlay()
{
auto error = vr::VROverlay()->ShowOverlay(mainHandle);
if (error != vr::VROverlayError_None)
{
lastError = "ShowOverlay failed: " + std::to_string(error);
return false;
}
return true;
}
EXPORT bool VR_OpenVR_HideOverlay()
{
auto error = vr::VROverlay()->HideOverlay(mainHandle);
if (error != vr::VROverlayError_None)
{
lastError = "HideOverlay failed: " + std::to_string(error);
return false;
}
return true;
}
EXPORT bool VR_OpenVR_IsOverlayVisible()
{
return vr::VROverlay()->IsOverlayVisible(mainHandle);
}
EXPORT bool VR_OpenVR_PollEvent(uint32_t* eventType)
{
vr::VREvent_t e;
if (vr::VROverlay()->PollNextOverlayEvent(mainHandle, &e, sizeof(e)))
{
*eventType = e.eventType;
return true;
}
*eventType = 0;
return false;
}
}
Here is the C# wrapper code.
public static class OpenVR_Overlay
{
private const string lib = "OpenVR_Overlay.dll";
[DllImport(lib, CharSet = CharSet.Ansi)]
public static extern int VR_OpenVR_GetLastError(StringBuilder errorBuffer, int errorBufferLength);
[DllImport(lib, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_Init(StringBuilder overlayKey, StringBuilder overlayFriendlyName, float overlayWidthInMeters);
[DllImport(lib)]
public static extern void VR_OpenVR_Shutdown();
[DllImport(lib)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_SetOverlayTexture(IntPtr texturePtr);
[DllImport(lib)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_SetOverlayRaw(IntPtr buffer, uint width, uint height, uint depth);
[DllImport(lib)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_ShowOverlay();
[DllImport(lib)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_HideOverlay();
[DllImport(lib)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_PollEvent(ref uint eventType);
[DllImport(lib)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool VR_OpenVR_IsOverlayVisible();
private static string GetLastError()
{
var value = new StringBuilder(256);
int length = VR_OpenVR_GetLastError(value, value.MaxCapacity);
return value.ToString();
}
public static bool Init()
{
if (!VR_OpenVR_Init(new StringBuilder("VROverlay"), new StringBuilder("VR Overlay"), 1))
{
Console.WriteLine(GetLastError());
return false;
}
return true;
}
public static void Shutdown()
{
VR_OpenVR_Shutdown();
}
/*public static bool SetTexture(RenderTexture texture)
{
if (!OpenVR_Overlay.VR_OpenVR_SetOverlayTexture(texture.GetNativeTexturePtr()))
{
Console.WriteLine(GetLastError());
return false;
}
return true;
}*/
public unsafe static bool SetRawBuffer(byte[] buffer, int width, int height, int bytesPerPixel)
{
fixed (byte* bufferPtr = buffer)
{
if (!VR_OpenVR_SetOverlayRaw(new IntPtr(bufferPtr), (uint)width, (uint)height, (uint)bytesPerPixel))
{
Console.WriteLine(GetLastError());
return false;
}
}
return true;
}
public static bool Show()
{
if (!VR_OpenVR_ShowOverlay())
{
Console.WriteLine(GetLastError());
return false;
}
return true;
}
public static bool Hide()
{
if (!VR_OpenVR_HideOverlay())
{
Console.WriteLine(GetLastError());
return false;
}
return true;
}
public static bool PollEvent(out uint eventType)
{
eventType = 0;
return VR_OpenVR_PollEvent(ref eventType);
}
}
Here is the WPF test renderer.
public partial class MainWindow : Window
{
private ContentUserControl control;
private RenderTargetBitmap renderTarget;
private byte[] pixelBuffer;
private DispatcherTimer timer;
private Stopwatch stopwatch;
private const int bufferWidth = 300, bufferHeight = 300;
public MainWindow()
{
InitializeComponent();
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// create WPF UI element used for rendering
control = new ContentUserControl();
control.Measure(new Size(bufferWidth, bufferHeight));
control.Arrange(new Rect(new Size(bufferWidth, bufferHeight)));
// create WPF render target to render control content into
renderTarget = new RenderTargetBitmap(bufferWidth, bufferHeight, 96, 96, PixelFormats.Pbgra32);
pixelBuffer = new byte[renderTarget.PixelWidth * renderTarget.PixelHeight * 4];
// render initial image
RenderImage();
// init openvr overlay
if (!OpenVR_Overlay.Init()) return;
OpenVR_Overlay.SetRawBuffer(pixelBuffer, renderTarget.PixelWidth, renderTarget.PixelHeight, 4);
// set test/debugging image
testImage.Source = renderTarget;
// start update loop
stopwatch = new Stopwatch();
stopwatch.Start();
timer = new DispatcherTimer(DispatcherPriority.Background, Dispatcher);
timer.Tick += UpdateLoop;
timer.Interval = TimeSpan.FromMilliseconds(1000 / 60);// 60 fps
timer.Start();
}
private void RenderImage()
{
// GPU render image
renderTarget.Render(control);
// copy pixels to CPU buffer
renderTarget.CopyPixels(pixelBuffer, renderTarget.PixelWidth * 4, 0);
// swap R&B color channels for OpenVR texture format
for (int i = 0; i < pixelBuffer.Length; i += 4)
{
byte r = pixelBuffer[i];
pixelBuffer[i] = pixelBuffer[i + 2];
pixelBuffer[i + 2] = r;
}
}
protected override void OnClosing(CancelEventArgs e)
{
timer.Stop();
OpenVR_Overlay.Shutdown();
base.OnClosing(e);
}
private void UpdateLoop(object sender, EventArgs e)
{
while (OpenVR_Overlay.PollEvent(out uint eventType))
{
//Console.WriteLine("EventType: " + eventType);
if ((eventType == 200 || eventType == 1705) && stopwatch.ElapsedMilliseconds >= 250)
{
stopwatch.Restart();
if (!OpenVR_Overlay.VR_OpenVR_IsOverlayVisible())
{
Console.WriteLine("Overlay shown!");
OpenVR_Overlay.Show();
}
else
{
Console.WriteLine("Overlay shown!");
OpenVR_Overlay.Hide();
}
}
}
}
}
For anyone wondering: You can do all of that directly in C# with the provided C# bindings file found in this repo (header directory). No need to marshall back and forth.
I have no issues using SetOverlayTexture
with a DX11 texture either.
@bddckr True this is just test code I had laying around. Also just to point it out you can use the bindings and it probably makes more sense in this situational but using a custom lib that does a bunch of stuff at once in C can be faster vs micro invoking methods from C# that cant be inlined. Not that it matters for this but it can actually make stuff easier to for testing (depending) sometimes when all the docs are in C.
Also WPF is D3D9 not D3D11 so you would have to map the DXGI object which didn't care to play around with in this test.
Hi we have an app that manages some SteamVR features for our systems. Currently we're force quitting vrmonitor.exe and its child processes to manager computer/system states. Sending window close events doesn't work sadly in the vrmonitor.
I would like to do this properly with the OpenVR API but don't see any method to achieve this. Is this possible or am I missing something?