1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "OSVRSession.h"
8 #include "prenv.h"
9 #include "nsString.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/StaticPrefs_dom.h"
12 #include "mozilla/SharedLibrary.h"
13 #include "mozilla/gfx/Quaternion.h"
14 
15 #if defined(XP_WIN)
16 #  include <d3d11.h>
17 #  include "mozilla/gfx/DeviceManagerDx.h"
18 #endif  // defined(XP_WIN)
19 
20 #ifndef M_PI
21 #  define M_PI 3.14159265358979323846
22 #endif
23 
24 using namespace mozilla;
25 using namespace mozilla::gfx;
26 
27 namespace {
28 // need to typedef functions that will be used in the code below
29 extern "C" {
30 typedef OSVR_ClientContext (*pfn_osvrClientInit)(
31     const char applicationIdentifier[], uint32_t flags);
32 typedef OSVR_ReturnCode (*pfn_osvrClientShutdown)(OSVR_ClientContext ctx);
33 typedef OSVR_ReturnCode (*pfn_osvrClientUpdate)(OSVR_ClientContext ctx);
34 typedef OSVR_ReturnCode (*pfn_osvrClientCheckStatus)(OSVR_ClientContext ctx);
35 typedef OSVR_ReturnCode (*pfn_osvrClientGetInterface)(
36     OSVR_ClientContext ctx, const char path[], OSVR_ClientInterface* iface);
37 typedef OSVR_ReturnCode (*pfn_osvrClientFreeInterface)(
38     OSVR_ClientContext ctx, OSVR_ClientInterface iface);
39 typedef OSVR_ReturnCode (*pfn_osvrGetOrientationState)(
40     OSVR_ClientInterface iface, OSVR_TimeValue* timestamp,
41     OSVR_OrientationState* state);
42 typedef OSVR_ReturnCode (*pfn_osvrGetPositionState)(OSVR_ClientInterface iface,
43                                                     OSVR_TimeValue* timestamp,
44                                                     OSVR_PositionState* state);
45 typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplay)(OSVR_ClientContext ctx,
46                                                     OSVR_DisplayConfig* disp);
47 typedef OSVR_ReturnCode (*pfn_osvrClientFreeDisplay)(OSVR_DisplayConfig disp);
48 typedef OSVR_ReturnCode (*pfn_osvrClientGetNumEyesForViewer)(
49     OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount* eyes);
50 typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyePose)(
51     OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
52     OSVR_Pose3* pose);
53 typedef OSVR_ReturnCode (*pfn_osvrClientGetDisplayDimensions)(
54     OSVR_DisplayConfig disp, OSVR_DisplayInputCount displayInputIndex,
55     OSVR_DisplayDimension* width, OSVR_DisplayDimension* height);
56 typedef OSVR_ReturnCode (
57     *pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes)(
58     OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
59     OSVR_SurfaceCount surface, double* left, double* right, double* bottom,
60     double* top);
61 typedef OSVR_ReturnCode (*pfn_osvrClientGetRelativeViewportForViewerEyeSurface)(
62     OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
63     OSVR_SurfaceCount surface, OSVR_ViewportDimension* left,
64     OSVR_ViewportDimension* bottom, OSVR_ViewportDimension* width,
65     OSVR_ViewportDimension* height);
66 typedef OSVR_ReturnCode (*pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf)(
67     OSVR_DisplayConfig disp, OSVR_ViewerCount viewer, OSVR_EyeCount eye,
68     OSVR_SurfaceCount surface, float near, float far,
69     OSVR_MatrixConventions flags, float* matrix);
70 typedef OSVR_ReturnCode (*pfn_osvrClientCheckDisplayStartup)(
71     OSVR_DisplayConfig disp);
72 typedef OSVR_ReturnCode (*pfn_osvrClientSetRoomRotationUsingHead)(
73     OSVR_ClientContext ctx);
74 }
75 
76 static pfn_osvrClientInit osvr_ClientInit = nullptr;
77 static pfn_osvrClientShutdown osvr_ClientShutdown = nullptr;
78 static pfn_osvrClientUpdate osvr_ClientUpdate = nullptr;
79 static pfn_osvrClientCheckStatus osvr_ClientCheckStatus = nullptr;
80 static pfn_osvrClientGetInterface osvr_ClientGetInterface = nullptr;
81 static pfn_osvrClientFreeInterface osvr_ClientFreeInterface = nullptr;
82 static pfn_osvrGetOrientationState osvr_GetOrientationState = nullptr;
83 static pfn_osvrGetPositionState osvr_GetPositionState = nullptr;
84 static pfn_osvrClientGetDisplay osvr_ClientGetDisplay = nullptr;
85 static pfn_osvrClientFreeDisplay osvr_ClientFreeDisplay = nullptr;
86 static pfn_osvrClientGetNumEyesForViewer osvr_ClientGetNumEyesForViewer =
87     nullptr;
88 static pfn_osvrClientGetViewerEyePose osvr_ClientGetViewerEyePose = nullptr;
89 static pfn_osvrClientGetDisplayDimensions osvr_ClientGetDisplayDimensions =
90     nullptr;
91 static pfn_osvrClientGetViewerEyeSurfaceProjectionClippingPlanes
92     osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes = nullptr;
93 static pfn_osvrClientGetRelativeViewportForViewerEyeSurface
94     osvr_ClientGetRelativeViewportForViewerEyeSurface = nullptr;
95 static pfn_osvrClientGetViewerEyeSurfaceProjectionMatrixf
96     osvr_ClientGetViewerEyeSurfaceProjectionMatrixf = nullptr;
97 static pfn_osvrClientCheckDisplayStartup osvr_ClientCheckDisplayStartup =
98     nullptr;
99 static pfn_osvrClientSetRoomRotationUsingHead
100     osvr_ClientSetRoomRotationUsingHead = nullptr;
101 
LoadOSVRRuntime()102 bool LoadOSVRRuntime() {
103   static PRLibrary* osvrUtilLib = nullptr;
104   static PRLibrary* osvrCommonLib = nullptr;
105   static PRLibrary* osvrClientLib = nullptr;
106   static PRLibrary* osvrClientKitLib = nullptr;
107   // this looks up the path in the about:config setting, from greprefs.js or
108   // modules\libpref\init\all.js we need all the libs to be valid
109 #ifdef XP_WIN
110   constexpr static auto* pfnGetPathStringPref = mozilla::Preferences::GetString;
111   nsAutoString osvrUtilPath, osvrCommonPath, osvrClientPath, osvrClientKitPath;
112 #else
113   constexpr static auto* pfnGetPathStringPref =
114       mozilla::Preferences::GetCString;
115   nsAutoCString osvrUtilPath, osvrCommonPath, osvrClientPath, osvrClientKitPath;
116 #endif
117   if (NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.utilLibPath", osvrUtilPath,
118                                      PrefValueKind::User)) ||
119       NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.commonLibPath",
120                                      osvrCommonPath, PrefValueKind::User)) ||
121       NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.clientLibPath",
122                                      osvrClientPath, PrefValueKind::User)) ||
123       NS_FAILED(pfnGetPathStringPref("gfx.vr.osvr.clientKitLibPath",
124                                      osvrClientKitPath, PrefValueKind::User))) {
125     return false;
126   }
127 
128   osvrUtilLib = LoadLibraryWithFlags(osvrUtilPath.get());
129   osvrCommonLib = LoadLibraryWithFlags(osvrCommonPath.get());
130   osvrClientLib = LoadLibraryWithFlags(osvrClientPath.get());
131   osvrClientKitLib = LoadLibraryWithFlags(osvrClientKitPath.get());
132 
133   if (!osvrUtilLib) {
134     printf_stderr("[OSVR] Failed to load OSVR Util library!\n");
135     return false;
136   }
137   if (!osvrCommonLib) {
138     printf_stderr("[OSVR] Failed to load OSVR Common library!\n");
139     return false;
140   }
141   if (!osvrClientLib) {
142     printf_stderr("[OSVR] Failed to load OSVR Client library!\n");
143     return false;
144   }
145   if (!osvrClientKitLib) {
146     printf_stderr("[OSVR] Failed to load OSVR ClientKit library!\n");
147     return false;
148   }
149 
150 // make sure all functions that we'll be using are available
151 #define REQUIRE_FUNCTION(_x)                                                  \
152   do {                                                                        \
153     *(void**)&osvr_##_x = (void*)PR_FindSymbol(osvrClientKitLib, "osvr" #_x); \
154     if (!osvr_##_x) {                                                         \
155       printf_stderr("osvr" #_x " symbol missing\n");                          \
156       goto fail;                                                              \
157     }                                                                         \
158   } while (0)
159 
160   REQUIRE_FUNCTION(ClientInit);
161   REQUIRE_FUNCTION(ClientShutdown);
162   REQUIRE_FUNCTION(ClientUpdate);
163   REQUIRE_FUNCTION(ClientCheckStatus);
164   REQUIRE_FUNCTION(ClientGetInterface);
165   REQUIRE_FUNCTION(ClientFreeInterface);
166   REQUIRE_FUNCTION(GetOrientationState);
167   REQUIRE_FUNCTION(GetPositionState);
168   REQUIRE_FUNCTION(ClientGetDisplay);
169   REQUIRE_FUNCTION(ClientFreeDisplay);
170   REQUIRE_FUNCTION(ClientGetNumEyesForViewer);
171   REQUIRE_FUNCTION(ClientGetViewerEyePose);
172   REQUIRE_FUNCTION(ClientGetDisplayDimensions);
173   REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionClippingPlanes);
174   REQUIRE_FUNCTION(ClientGetRelativeViewportForViewerEyeSurface);
175   REQUIRE_FUNCTION(ClientGetViewerEyeSurfaceProjectionMatrixf);
176   REQUIRE_FUNCTION(ClientCheckDisplayStartup);
177   REQUIRE_FUNCTION(ClientSetRoomRotationUsingHead);
178 
179 #undef REQUIRE_FUNCTION
180 
181   return true;
182 
183 fail:
184   return false;
185 }
186 
187 }  // namespace
188 
SetFromTanRadians(double left,double right,double bottom,double top)189 mozilla::gfx::VRFieldOfView SetFromTanRadians(double left, double right,
190                                               double bottom, double top) {
191   mozilla::gfx::VRFieldOfView fovInfo;
192   fovInfo.leftDegrees = atan(left) * 180.0 / M_PI;
193   fovInfo.rightDegrees = atan(right) * 180.0 / M_PI;
194   fovInfo.upDegrees = atan(top) * 180.0 / M_PI;
195   fovInfo.downDegrees = atan(bottom) * 180.0 / M_PI;
196   return fovInfo;
197 }
198 
OSVRSession()199 OSVRSession::OSVRSession()
200     : VRSession(),
201       mRuntimeLoaded(false),
202       mOSVRInitialized(false),
203       mClientContextInitialized(false),
204       mDisplayConfigInitialized(false),
205       mInterfaceInitialized(false),
206       m_ctx(nullptr),
207       m_iface(nullptr),
208       m_display(nullptr) {}
~OSVRSession()209 OSVRSession::~OSVRSession() { Shutdown(); }
210 
Initialize(mozilla::gfx::VRSystemState & aSystemState,bool aDetectRuntimesOnly)211 bool OSVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
212                              bool aDetectRuntimesOnly) {
213   if (StaticPrefs::dom_vr_puppet_enabled()) {
214     // Ensure that tests using the VR Puppet do not find real hardware
215     return false;
216   }
217   if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_osvr_enabled()) {
218     return false;
219   }
220   if (mOSVRInitialized) {
221     return true;
222   }
223   if (!LoadOSVRRuntime()) {
224     return false;
225   }
226   mRuntimeLoaded = true;
227 
228   if (aDetectRuntimesOnly) {
229     aSystemState.displayState.capabilityFlags |=
230         VRDisplayCapabilityFlags::Cap_ImmersiveVR;
231     return false;
232   }
233 
234   // initialize client context
235   InitializeClientContext();
236   // try to initialize interface
237   InitializeInterface();
238   // try to initialize display object
239   InitializeDisplay();
240   // verify all components are initialized
241   CheckOSVRStatus();
242 
243   if (!mOSVRInitialized) {
244     return false;
245   }
246 
247   if (!InitState(aSystemState)) {
248     return false;
249   }
250 
251   return true;
252 }
253 
CheckOSVRStatus()254 void OSVRSession::CheckOSVRStatus() {
255   if (mOSVRInitialized) {
256     return;
257   }
258 
259   // client context must be initialized first
260   InitializeClientContext();
261 
262   // update client context
263   osvr_ClientUpdate(m_ctx);
264 
265   // initialize interface and display if they are not initialized yet
266   InitializeInterface();
267   InitializeDisplay();
268 
269   // OSVR is fully initialized now
270   if (mClientContextInitialized && mDisplayConfigInitialized &&
271       mInterfaceInitialized) {
272     mOSVRInitialized = true;
273   }
274 }
275 
InitializeClientContext()276 void OSVRSession::InitializeClientContext() {
277   // already initialized
278   if (mClientContextInitialized) {
279     return;
280   }
281 
282   // first time creating
283   if (!m_ctx) {
284     // get client context
285     m_ctx = osvr_ClientInit("com.osvr.webvr", 0);
286     // update context
287     osvr_ClientUpdate(m_ctx);
288     // verify we are connected
289     if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) {
290       mClientContextInitialized = true;
291     }
292   }
293   // client context exists but not up and running yet
294   else {
295     // update context
296     osvr_ClientUpdate(m_ctx);
297     if (OSVR_RETURN_SUCCESS == osvr_ClientCheckStatus(m_ctx)) {
298       mClientContextInitialized = true;
299     }
300   }
301 }
302 
InitializeInterface()303 void OSVRSession::InitializeInterface() {
304   // already initialized
305   if (mInterfaceInitialized) {
306     return;
307   }
308   // Client context must be initialized before getting interface
309   if (mClientContextInitialized) {
310     // m_iface will remain nullptr if no interface is returned
311     if (OSVR_RETURN_SUCCESS ==
312         osvr_ClientGetInterface(m_ctx, "/me/head", &m_iface)) {
313       mInterfaceInitialized = true;
314     }
315   }
316 }
317 
InitializeDisplay()318 void OSVRSession::InitializeDisplay() {
319   // display is fully configured
320   if (mDisplayConfigInitialized) {
321     return;
322   }
323 
324   // Client context must be initialized before getting interface
325   if (mClientContextInitialized) {
326     // first time creating display object
327     if (m_display == nullptr) {
328       OSVR_ReturnCode ret = osvr_ClientGetDisplay(m_ctx, &m_display);
329 
330       if (ret == OSVR_RETURN_SUCCESS) {
331         osvr_ClientUpdate(m_ctx);
332         // display object may have been created but not fully startup
333         if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) {
334           mDisplayConfigInitialized = true;
335         }
336       }
337 
338       // Typically once we get Display object, pose data is available after
339       // clientUpdate but sometimes it takes ~ 200 ms to get
340       // a succesfull connection, so we might have to run a few update cycles
341     } else {
342       if (OSVR_RETURN_SUCCESS == osvr_ClientCheckDisplayStartup(m_display)) {
343         mDisplayConfigInitialized = true;
344       }
345     }
346   }
347 }
348 
InitState(mozilla::gfx::VRSystemState & aSystemState)349 bool OSVRSession::InitState(mozilla::gfx::VRSystemState& aSystemState) {
350   VRDisplayState& state = aSystemState.displayState;
351   strncpy(state.displayName, "OSVR HMD", kVRDisplayNameMaxLen);
352   state.eightCC = GFX_VR_EIGHTCC('O', 'S', 'V', 'R', ' ', ' ', ' ', ' ');
353   state.isConnected = true;
354   state.isMounted = false;
355   state.capabilityFlags =
356       (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None |
357                                  (int)
358                                      VRDisplayCapabilityFlags::Cap_Orientation |
359                                  (int)VRDisplayCapabilityFlags::Cap_Position |
360                                  (int)VRDisplayCapabilityFlags::Cap_External |
361                                  (int)VRDisplayCapabilityFlags::Cap_Present |
362                                  (int)
363                                      VRDisplayCapabilityFlags::Cap_ImmersiveVR);
364   state.blendMode = VRDisplayBlendMode::Opaque;
365   state.reportsDroppedFrames = false;
366 
367   // XXX OSVR display topology allows for more than one viewer
368   // will assume only one viewer for now (most likely stay that way)
369 
370   OSVR_EyeCount numEyes;
371   osvr_ClientGetNumEyesForViewer(m_display, 0, &numEyes);
372 
373   for (uint8_t eye = 0; eye < numEyes; eye++) {
374     double left, right, bottom, top;
375     // XXX for now there is only one surface per eye
376     osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes(
377         m_display, 0, eye, 0, &left, &right, &bottom, &top);
378     state.eyeFOV[eye] = SetFromTanRadians(-left, right, -bottom, top);
379   }
380 
381   // XXX Assuming there is only one display input for now
382   // however, it's possible to have more than one (dSight with 2 HDMI inputs)
383   OSVR_DisplayDimension width, height;
384   osvr_ClientGetDisplayDimensions(m_display, 0, &width, &height);
385 
386   for (uint8_t eye = 0; eye < numEyes; eye++) {
387     OSVR_ViewportDimension l, b, w, h;
388     osvr_ClientGetRelativeViewportForViewerEyeSurface(m_display, 0, eye, 0, &l,
389                                                       &b, &w, &h);
390     state.eyeResolution.width = w;
391     state.eyeResolution.height = h;
392     OSVR_Pose3 eyePose;
393     // Viewer eye pose may not be immediately available, update client context
394     // until we get it
395     OSVR_ReturnCode ret =
396         osvr_ClientGetViewerEyePose(m_display, 0, eye, &eyePose);
397     while (ret != OSVR_RETURN_SUCCESS) {
398       osvr_ClientUpdate(m_ctx);
399       ret = osvr_ClientGetViewerEyePose(m_display, 0, eye, &eyePose);
400     }
401     state.eyeTranslation[eye].x = eyePose.translation.data[0];
402     state.eyeTranslation[eye].y = eyePose.translation.data[1];
403     state.eyeTranslation[eye].z = eyePose.translation.data[2];
404 
405     Matrix4x4 pose;
406     pose.SetRotationFromQuaternion(gfx::Quaternion(
407         osvrQuatGetX(&eyePose.rotation), osvrQuatGetY(&eyePose.rotation),
408         osvrQuatGetZ(&eyePose.rotation), osvrQuatGetW(&eyePose.rotation)));
409     pose.PreTranslate(eyePose.translation.data[0], eyePose.translation.data[1],
410                       eyePose.translation.data[2]);
411     pose.Invert();
412     mHeadToEye[eye] = pose;
413   }
414 
415   // default to an identity quaternion
416   VRHMDSensorState& sensorState = aSystemState.sensorState;
417   sensorState.flags =
418       (VRDisplayCapabilityFlags)((int)
419                                      VRDisplayCapabilityFlags::Cap_Orientation |
420                                  (int)VRDisplayCapabilityFlags::Cap_Position);
421   sensorState.pose.orientation[3] = 1.0f;  // Default to an identity quaternion
422 
423   return true;
424 }
425 
Shutdown()426 void OSVRSession::Shutdown() {
427   if (!mRuntimeLoaded) {
428     return;
429   }
430   mOSVRInitialized = false;
431   // client context may not have been initialized
432   if (m_ctx) {
433     osvr_ClientFreeDisplay(m_display);
434   }
435   // osvr checks that m_ctx or m_iface are not null
436   osvr_ClientFreeInterface(m_ctx, m_iface);
437   osvr_ClientShutdown(m_ctx);
438 }
439 
ProcessEvents(mozilla::gfx::VRSystemState & aSystemState)440 void OSVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {}
441 
StartFrame(mozilla::gfx::VRSystemState & aSystemState)442 void OSVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
443   UpdateHeadsetPose(aSystemState);
444 }
445 
UpdateHeadsetPose(mozilla::gfx::VRSystemState & aState)446 void OSVRSession::UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState) {
447   // Update client context before anything
448   // this usually goes into app's mainloop
449   osvr_ClientUpdate(m_ctx);
450 
451   VRHMDSensorState result{};
452   OSVR_TimeValue timestamp;
453 
454   OSVR_OrientationState orientation;
455 
456   OSVR_ReturnCode ret =
457       osvr_GetOrientationState(m_iface, &timestamp, &orientation);
458 
459   aState.sensorState.timestamp = timestamp.seconds;
460 
461   if (ret == OSVR_RETURN_SUCCESS) {
462     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
463     result.pose.orientation[0] = orientation.data[1];
464     result.pose.orientation[1] = orientation.data[2];
465     result.pose.orientation[2] = orientation.data[3];
466     result.pose.orientation[3] = orientation.data[0];
467   } else {
468     // default to an identity quaternion
469     result.pose.orientation[3] = 1.0f;
470   }
471 
472   OSVR_PositionState position;
473   ret = osvr_GetPositionState(m_iface, &timestamp, &position);
474   if (ret == OSVR_RETURN_SUCCESS) {
475     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
476     result.pose.position[0] = position.data[0];
477     result.pose.position[1] = position.data[1];
478     result.pose.position[2] = position.data[2];
479   }
480 
481   result.CalcViewMatrices(mHeadToEye);
482 }
483 
StartPresentation()484 bool OSVRSession::StartPresentation() {
485   return false;
486   // TODO Implement
487 }
488 
StopPresentation()489 void OSVRSession::StopPresentation() {
490   // TODO Implement
491 }
492 
493 #if defined(XP_WIN)
SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive & aLayer,ID3D11Texture2D * aTexture)494 bool OSVRSession::SubmitFrame(
495     const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
496     ID3D11Texture2D* aTexture) {
497   return false;
498   // TODO Implement
499 }
500 #elif defined(XP_MACOSX)
SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive & aLayer,const VRLayerTextureHandle & aTexture)501 bool OSVRSession::SubmitFrame(
502     const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
503     const VRLayerTextureHandle& aTexture) {
504   return false;
505   // TODO Implement
506 }
507 #endif
508 
VibrateHaptic(uint32_t aControllerIdx,uint32_t aHapticIndex,float aIntensity,float aDuration)509 void OSVRSession::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
510                                 float aIntensity, float aDuration) {}
511 
StopVibrateHaptic(uint32_t aControllerIdx)512 void OSVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {}
513 
StopAllHaptics()514 void OSVRSession::StopAllHaptics() {}
515