tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

OpenVRSession.cpp (56005B)


      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 <fstream>
      8 #include "mozilla/JSONStringWriteFuncs.h"
      9 #include "mozilla/ClearOnShutdown.h"
     10 #include "nsIThread.h"
     11 #include "nsString.h"
     12 
     13 #include "OpenVRSession.h"
     14 #include "mozilla/StaticPrefs_dom.h"
     15 
     16 #if defined(XP_WIN)
     17 #  include <d3d11.h>
     18 #  include "mozilla/gfx/DeviceManagerDx.h"
     19 #elif defined(XP_MACOSX)
     20 #  include "mozilla/gfx/MacIOSurface.h"
     21 #endif
     22 
     23 #if !defined(XP_WIN)
     24 #  include <sys/stat.h>  // for umask()
     25 #endif
     26 
     27 #include "mozilla/dom/GamepadEventTypes.h"
     28 #include "mozilla/dom/GamepadBinding.h"
     29 #include "binding/OpenVRCosmosBinding.h"
     30 #include "binding/OpenVRKnucklesBinding.h"
     31 #include "binding/OpenVRViveBinding.h"
     32 #include "OpenVRCosmosMapper.h"
     33 #include "OpenVRDefaultMapper.h"
     34 #include "OpenVRKnucklesMapper.h"
     35 #include "OpenVRViveMapper.h"
     36 #if defined(XP_WIN)  // Windows Mixed Reality is only available in Windows.
     37 #  include "OpenVRWMRMapper.h"
     38 #  include "binding/OpenVRWMRBinding.h"
     39 #endif
     40 
     41 #include "VRParent.h"
     42 #include "VRProcessChild.h"
     43 #include "VRThread.h"
     44 
     45 #if !defined(M_PI)
     46 #  define M_PI 3.14159265358979323846264338327950288
     47 #endif
     48 
     49 #define BTN_MASK_FROM_ID(_id) ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
     50 
     51 // Haptic feedback is updated every 5ms, as this is
     52 // the minimum period between new haptic pulse requests.
     53 // Effectively, this results in a pulse width modulation
     54 // with an interval of 5ms.  Through experimentation, the
     55 // maximum duty cycle was found to be about 3.9ms
     56 const uint32_t kVRHapticUpdateInterval = 5;
     57 
     58 using namespace mozilla::gfx;
     59 
     60 namespace mozilla::gfx {
     61 
     62 namespace {
     63 
     64 class ControllerManifestFile {
     65  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ControllerManifestFile)
     66 
     67 public:
     68  static already_AddRefed<ControllerManifestFile> CreateManifest() {
     69    RefPtr<ControllerManifestFile> manifest = new ControllerManifestFile();
     70    return manifest.forget();
     71  }
     72 
     73  bool IsExisting() {
     74    if (mFileName.IsEmpty() ||
     75        !std::ifstream(mFileName.BeginReading()).good()) {
     76      return false;
     77    }
     78    return true;
     79  }
     80 
     81  void SetFileName(const char* aName) { mFileName = aName; }
     82 
     83  const char* GetFileName() const { return mFileName.BeginReading(); }
     84 
     85 private:
     86  ControllerManifestFile() = default;
     87 
     88  ~ControllerManifestFile() {
     89    if (!mFileName.IsEmpty() && remove(mFileName.BeginReading()) != 0) {
     90      MOZ_ASSERT(false, "Delete controller manifest file failed.");
     91    }
     92    mFileName = "";
     93  }
     94 
     95  nsCString mFileName;
     96 };
     97 
     98 // We wanna keep these temporary files existing
     99 // until Firefox is closed instead of following OpenVRSession's lifetime.
    100 StaticRefPtr<ControllerManifestFile> sCosmosBindingFile;
    101 StaticRefPtr<ControllerManifestFile> sKnucklesBindingFile;
    102 StaticRefPtr<ControllerManifestFile> sViveBindingFile;
    103 #if defined(XP_WIN)
    104 StaticRefPtr<ControllerManifestFile> sWMRBindingFile;
    105 #endif
    106 StaticRefPtr<ControllerManifestFile> sControllerActionFile;
    107 
    108 dom::GamepadHand GetControllerHandFromControllerRole(OpenVRHand aRole) {
    109  dom::GamepadHand hand;
    110  switch (aRole) {
    111    case OpenVRHand::None:
    112      hand = dom::GamepadHand::_empty;
    113      break;
    114    case OpenVRHand::Left:
    115      hand = dom::GamepadHand::Left;
    116      break;
    117    case OpenVRHand::Right:
    118      hand = dom::GamepadHand::Right;
    119      break;
    120    default:
    121      hand = dom::GamepadHand::_empty;
    122      MOZ_ASSERT(false);
    123      break;
    124  }
    125 
    126  return hand;
    127 }
    128 
    129 bool FileIsExisting(const nsCString& aPath) {
    130  if (aPath.IsEmpty() || !std::ifstream(aPath.BeginReading()).good()) {
    131    return false;
    132  }
    133  return true;
    134 }
    135 
    136 };  // anonymous namespace
    137 
    138 #if defined(XP_WIN)
    139 bool GenerateTempFileName(nsCString& aPath) {
    140  TCHAR tempPathBuffer[MAX_PATH];
    141  TCHAR tempFileName[MAX_PATH];
    142 
    143  // Gets the temp path env string (no guarantee it's a valid path).
    144  DWORD dwRetVal = GetTempPath(MAX_PATH, tempPathBuffer);
    145  if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
    146    NS_WARNING("OpenVR - Creating temp path failed.");
    147    return false;
    148  }
    149 
    150  // Generates a temporary file name.
    151  UINT uRetVal = GetTempFileName(tempPathBuffer,  // directory for tmp files
    152                                 TEXT("mozvr"),   // temp file name prefix
    153                                 0,               // create unique name
    154                                 tempFileName);   // buffer for name
    155  if (uRetVal == 0) {
    156    NS_WARNING("OpenVR - Creating temp file failed.");
    157    return false;
    158  }
    159 
    160  aPath.Assign(NS_ConvertUTF16toUTF8(tempFileName));
    161  return true;
    162 }
    163 #else
    164 bool GenerateTempFileName(nsCString& aPath) {
    165  const char tmp[] = "/tmp/mozvrXXXXXX";
    166  char fileName[PATH_MAX];
    167 
    168  strcpy(fileName, tmp);
    169  const mode_t prevMask = umask(S_IXUSR | S_IRWXO | S_IRWXG);
    170  const int fd = mkstemp(fileName);
    171  umask(prevMask);
    172  if (fd == -1) {
    173    NS_WARNING(nsPrintfCString("OpenVR - Creating temp file failed: %s",
    174                               strerror(errno))
    175                   .get());
    176    return false;
    177  }
    178  close(fd);
    179 
    180  aPath.Assign(fileName);
    181  return true;
    182 }
    183 #endif  // defined(XP_WIN)
    184 
    185 OpenVRSession::OpenVRSession()
    186    : mVRSystem(nullptr),
    187      mVRChaperone(nullptr),
    188      mVRCompositor(nullptr),
    189      mHapticPulseRemaining{},
    190      mHapticPulseIntensity{},
    191      mIsWindowsMR(false),
    192      mControllerHapticStateMutex(
    193          "OpenVRSession::mControllerHapticStateMutex") {
    194  std::fill_n(mControllerDeviceIndex, kVRControllerMaxCount, OpenVRHand::None);
    195 }
    196 
    197 OpenVRSession::~OpenVRSession() {
    198  mActionsetFirefox = ::vr::k_ulInvalidActionSetHandle;
    199  Shutdown();
    200 }
    201 
    202 bool OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState,
    203                               bool aDetectRuntimesOnly) {
    204  if (StaticPrefs::dom_vr_puppet_enabled()) {
    205    // Ensure that tests using the VR Puppet do not find real hardware
    206    return false;
    207  }
    208  if (!StaticPrefs::dom_vr_enabled() || !StaticPrefs::dom_vr_openvr_enabled()) {
    209    return false;
    210  }
    211  if (mVRSystem != nullptr) {
    212    // Already initialized
    213    return true;
    214  }
    215  if (!::vr::VR_IsRuntimeInstalled()) {
    216    return false;
    217  }
    218  if (aDetectRuntimesOnly) {
    219    aSystemState.displayState.capabilityFlags |=
    220        VRDisplayCapabilityFlags::Cap_ImmersiveVR;
    221    return false;
    222  }
    223  if (!::vr::VR_IsHmdPresent()) {
    224    return false;
    225  }
    226 
    227  ::vr::HmdError err;
    228 
    229  ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
    230  if (err) {
    231    return false;
    232  }
    233 
    234  mVRSystem = (::vr::IVRSystem*)::vr::VR_GetGenericInterface(
    235      ::vr::IVRSystem_Version, &err);
    236  if (err || !mVRSystem) {
    237    Shutdown();
    238    return false;
    239  }
    240  mVRChaperone = (::vr::IVRChaperone*)::vr::VR_GetGenericInterface(
    241      ::vr::IVRChaperone_Version, &err);
    242  if (err || !mVRChaperone) {
    243    Shutdown();
    244    return false;
    245  }
    246  mVRCompositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(
    247      ::vr::IVRCompositor_Version, &err);
    248  if (err || !mVRCompositor) {
    249    Shutdown();
    250    return false;
    251  }
    252 
    253 #if defined(XP_WIN)
    254  if (!CreateD3DObjects()) {
    255    Shutdown();
    256    return false;
    257  }
    258 
    259 #endif
    260 
    261  // Configure coordinate system
    262  mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
    263 
    264  if (!InitState(aSystemState)) {
    265    Shutdown();
    266    return false;
    267  }
    268  if (!SetupContollerActions()) {
    269    return false;
    270  }
    271 
    272  // Succeeded
    273  return true;
    274 }
    275 
    276 // "actions": [] Action paths must take the form: "/actions/<action
    277 // set>/in|out/<action>"
    278 #define CreateControllerAction(hand, name, type) \
    279  ControllerAction("/actions/firefox/in/" #hand "Hand_" #name, #type)
    280 #define CreateControllerOutAction(hand, name, type) \
    281  ControllerAction("/actions/firefox/out/" #hand "Hand_" #name, #type)
    282 
    283 bool OpenVRSession::SetupContollerActions() {
    284  if (!vr::VRInput()) {
    285    NS_WARNING("OpenVR - vr::VRInput() is null.");
    286    return false;
    287  }
    288 
    289  // Check if this device binding file has been created.
    290  // If it didn't exist yet, create a new temp file.
    291  nsCString controllerAction;
    292  nsCString viveManifest;
    293  nsCString WMRManifest;
    294  nsCString knucklesManifest;
    295  nsCString cosmosManifest;
    296 
    297  // Getting / Generating manifest file paths.
    298  if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
    299    VRParent* vrParent = VRProcessChild::GetVRParent();
    300    nsCString output;
    301 
    302    if (vrParent->GetOpenVRControllerActionPath(&output)) {
    303      controllerAction = output;
    304    }
    305 
    306    if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::HTCVive,
    307                                                  &output)) {
    308      viveManifest = output;
    309    }
    310    if (!viveManifest.Length() || !FileIsExisting(viveManifest)) {
    311      if (!GenerateTempFileName(viveManifest)) {
    312        return false;
    313      }
    314      OpenVRViveBinding viveBinding;
    315      std::ofstream viveBindingFile(viveManifest.BeginReading());
    316      if (viveBindingFile.is_open()) {
    317        viveBindingFile << viveBinding.binding;
    318        viveBindingFile.close();
    319      }
    320    }
    321 
    322 #if defined(XP_WIN)
    323    if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::MSMR,
    324                                                  &output)) {
    325      WMRManifest = output;
    326    }
    327    if (!WMRManifest.Length() || !FileIsExisting(WMRManifest)) {
    328      if (!GenerateTempFileName(WMRManifest)) {
    329        return false;
    330      }
    331      OpenVRWMRBinding WMRBinding;
    332      std::ofstream WMRBindingFile(WMRManifest.BeginReading());
    333      if (WMRBindingFile.is_open()) {
    334        WMRBindingFile << WMRBinding.binding;
    335        WMRBindingFile.close();
    336      }
    337    }
    338 #endif
    339    if (vrParent->GetOpenVRControllerManifestPath(VRControllerType::ValveIndex,
    340                                                  &output)) {
    341      knucklesManifest = output;
    342    }
    343    if (!knucklesManifest.Length() || !FileIsExisting(knucklesManifest)) {
    344      if (!GenerateTempFileName(knucklesManifest)) {
    345        return false;
    346      }
    347      OpenVRKnucklesBinding knucklesBinding;
    348      std::ofstream knucklesBindingFile(knucklesManifest.BeginReading());
    349      if (knucklesBindingFile.is_open()) {
    350        knucklesBindingFile << knucklesBinding.binding;
    351        knucklesBindingFile.close();
    352      }
    353    }
    354    if (vrParent->GetOpenVRControllerManifestPath(
    355            VRControllerType::HTCViveCosmos, &output)) {
    356      cosmosManifest = output;
    357    }
    358    if (!cosmosManifest.Length() || !FileIsExisting(cosmosManifest)) {
    359      if (!GenerateTempFileName(cosmosManifest)) {
    360        return false;
    361      }
    362      OpenVRCosmosBinding cosmosBinding;
    363      std::ofstream cosmosBindingFile(cosmosManifest.BeginReading());
    364      if (cosmosBindingFile.is_open()) {
    365        cosmosBindingFile << cosmosBinding.binding;
    366        cosmosBindingFile.close();
    367      }
    368    }
    369  } else {
    370    // Without using VR process
    371    if (!sControllerActionFile) {
    372      sControllerActionFile = ControllerManifestFile::CreateManifest();
    373      NS_DispatchToMainThread(NS_NewRunnableFunction(
    374          "ClearOnShutdown ControllerManifestFile",
    375          []() { ClearOnShutdown(&sControllerActionFile); }));
    376    }
    377    controllerAction = sControllerActionFile->GetFileName();
    378 
    379    if (!sViveBindingFile) {
    380      sViveBindingFile = ControllerManifestFile::CreateManifest();
    381      NS_DispatchToMainThread(
    382          NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
    383                                 []() { ClearOnShutdown(&sViveBindingFile); }));
    384    }
    385    if (!sViveBindingFile->IsExisting()) {
    386      nsCString viveBindingPath;
    387      if (!GenerateTempFileName(viveBindingPath)) {
    388        return false;
    389      }
    390      sViveBindingFile->SetFileName(viveBindingPath.BeginReading());
    391      OpenVRViveBinding viveBinding;
    392      std::ofstream viveBindingFile(sViveBindingFile->GetFileName());
    393      if (viveBindingFile.is_open()) {
    394        viveBindingFile << viveBinding.binding;
    395        viveBindingFile.close();
    396      }
    397    }
    398    viveManifest = sViveBindingFile->GetFileName();
    399 
    400    if (!sKnucklesBindingFile) {
    401      sKnucklesBindingFile = ControllerManifestFile::CreateManifest();
    402      NS_DispatchToMainThread(NS_NewRunnableFunction(
    403          "ClearOnShutdown ControllerManifestFile",
    404          []() { ClearOnShutdown(&sKnucklesBindingFile); }));
    405    }
    406    if (!sKnucklesBindingFile->IsExisting()) {
    407      nsCString knucklesBindingPath;
    408      if (!GenerateTempFileName(knucklesBindingPath)) {
    409        return false;
    410      }
    411      sKnucklesBindingFile->SetFileName(knucklesBindingPath.BeginReading());
    412      OpenVRKnucklesBinding knucklesBinding;
    413      std::ofstream knucklesBindingFile(sKnucklesBindingFile->GetFileName());
    414      if (knucklesBindingFile.is_open()) {
    415        knucklesBindingFile << knucklesBinding.binding;
    416        knucklesBindingFile.close();
    417      }
    418    }
    419    knucklesManifest = sKnucklesBindingFile->GetFileName();
    420 
    421    if (!sCosmosBindingFile) {
    422      sCosmosBindingFile = ControllerManifestFile::CreateManifest();
    423      NS_DispatchToMainThread(NS_NewRunnableFunction(
    424          "ClearOnShutdown ControllerManifestFile",
    425          []() { ClearOnShutdown(&sCosmosBindingFile); }));
    426    }
    427    if (!sCosmosBindingFile->IsExisting()) {
    428      nsCString cosmosBindingPath;
    429      if (!GenerateTempFileName(cosmosBindingPath)) {
    430        return false;
    431      }
    432      sCosmosBindingFile->SetFileName(cosmosBindingPath.BeginReading());
    433      OpenVRCosmosBinding cosmosBinding;
    434      std::ofstream cosmosBindingFile(sCosmosBindingFile->GetFileName());
    435      if (cosmosBindingFile.is_open()) {
    436        cosmosBindingFile << cosmosBinding.binding;
    437        cosmosBindingFile.close();
    438      }
    439    }
    440    cosmosManifest = sCosmosBindingFile->GetFileName();
    441 #if defined(XP_WIN)
    442    if (!sWMRBindingFile) {
    443      sWMRBindingFile = ControllerManifestFile::CreateManifest();
    444      NS_DispatchToMainThread(
    445          NS_NewRunnableFunction("ClearOnShutdown ControllerManifestFile",
    446                                 []() { ClearOnShutdown(&sWMRBindingFile); }));
    447    }
    448    if (!sWMRBindingFile->IsExisting()) {
    449      nsCString WMRBindingPath;
    450      if (!GenerateTempFileName(WMRBindingPath)) {
    451        return false;
    452      }
    453      sWMRBindingFile->SetFileName(WMRBindingPath.BeginReading());
    454      OpenVRWMRBinding WMRBinding;
    455      std::ofstream WMRBindingFile(sWMRBindingFile->GetFileName());
    456      if (WMRBindingFile.is_open()) {
    457        WMRBindingFile << WMRBinding.binding;
    458        WMRBindingFile.close();
    459      }
    460    }
    461    WMRManifest = sWMRBindingFile->GetFileName();
    462 #endif
    463  }
    464  // End of Getting / Generating manifest file paths.
    465 
    466  // Setup controller actions.
    467  ControllerInfo leftContollerInfo;
    468  leftContollerInfo.mActionPose = CreateControllerAction(L, pose, pose);
    469  leftContollerInfo.mActionTrackpad_Analog =
    470      CreateControllerAction(L, trackpad_analog, vector2);
    471  leftContollerInfo.mActionTrackpad_Pressed =
    472      CreateControllerAction(L, trackpad_pressed, boolean);
    473  leftContollerInfo.mActionTrackpad_Touched =
    474      CreateControllerAction(L, trackpad_touched, boolean);
    475  leftContollerInfo.mActionTrigger_Value =
    476      CreateControllerAction(L, trigger_value, vector1);
    477  leftContollerInfo.mActionGrip_Pressed =
    478      CreateControllerAction(L, grip_pressed, boolean);
    479  leftContollerInfo.mActionGrip_Touched =
    480      CreateControllerAction(L, grip_touched, boolean);
    481  leftContollerInfo.mActionMenu_Pressed =
    482      CreateControllerAction(L, menu_pressed, boolean);
    483  leftContollerInfo.mActionMenu_Touched =
    484      CreateControllerAction(L, menu_touched, boolean);
    485  leftContollerInfo.mActionSystem_Pressed =
    486      CreateControllerAction(L, system_pressed, boolean);
    487  leftContollerInfo.mActionSystem_Touched =
    488      CreateControllerAction(L, system_touched, boolean);
    489  leftContollerInfo.mActionA_Pressed =
    490      CreateControllerAction(L, A_pressed, boolean);
    491  leftContollerInfo.mActionA_Touched =
    492      CreateControllerAction(L, A_touched, boolean);
    493  leftContollerInfo.mActionB_Pressed =
    494      CreateControllerAction(L, B_pressed, boolean);
    495  leftContollerInfo.mActionB_Touched =
    496      CreateControllerAction(L, B_touched, boolean);
    497  leftContollerInfo.mActionThumbstick_Analog =
    498      CreateControllerAction(L, thumbstick_analog, vector2);
    499  leftContollerInfo.mActionThumbstick_Pressed =
    500      CreateControllerAction(L, thumbstick_pressed, boolean);
    501  leftContollerInfo.mActionThumbstick_Touched =
    502      CreateControllerAction(L, thumbstick_touched, boolean);
    503  leftContollerInfo.mActionFingerIndex_Value =
    504      CreateControllerAction(L, finger_index_value, vector1);
    505  leftContollerInfo.mActionFingerMiddle_Value =
    506      CreateControllerAction(L, finger_middle_value, vector1);
    507  leftContollerInfo.mActionFingerRing_Value =
    508      CreateControllerAction(L, finger_ring_value, vector1);
    509  leftContollerInfo.mActionFingerPinky_Value =
    510      CreateControllerAction(L, finger_pinky_value, vector1);
    511  leftContollerInfo.mActionBumper_Pressed =
    512      CreateControllerAction(L, bumper_pressed, boolean);
    513  leftContollerInfo.mActionHaptic =
    514      CreateControllerOutAction(L, haptic, vibration);
    515 
    516  ControllerInfo rightContollerInfo;
    517  rightContollerInfo.mActionPose = CreateControllerAction(R, pose, pose);
    518  rightContollerInfo.mActionTrackpad_Analog =
    519      CreateControllerAction(R, trackpad_analog, vector2);
    520  rightContollerInfo.mActionTrackpad_Pressed =
    521      CreateControllerAction(R, trackpad_pressed, boolean);
    522  rightContollerInfo.mActionTrackpad_Touched =
    523      CreateControllerAction(R, trackpad_touched, boolean);
    524  rightContollerInfo.mActionTrigger_Value =
    525      CreateControllerAction(R, trigger_value, vector1);
    526  rightContollerInfo.mActionGrip_Pressed =
    527      CreateControllerAction(R, grip_pressed, boolean);
    528  rightContollerInfo.mActionGrip_Touched =
    529      CreateControllerAction(R, grip_touched, boolean);
    530  rightContollerInfo.mActionMenu_Pressed =
    531      CreateControllerAction(R, menu_pressed, boolean);
    532  rightContollerInfo.mActionMenu_Touched =
    533      CreateControllerAction(R, menu_touched, boolean);
    534  rightContollerInfo.mActionSystem_Pressed =
    535      CreateControllerAction(R, system_pressed, boolean);
    536  rightContollerInfo.mActionSystem_Touched =
    537      CreateControllerAction(R, system_touched, boolean);
    538  rightContollerInfo.mActionA_Pressed =
    539      CreateControllerAction(R, A_pressed, boolean);
    540  rightContollerInfo.mActionA_Touched =
    541      CreateControllerAction(R, A_touched, boolean);
    542  rightContollerInfo.mActionB_Pressed =
    543      CreateControllerAction(R, B_pressed, boolean);
    544  rightContollerInfo.mActionB_Touched =
    545      CreateControllerAction(R, B_touched, boolean);
    546  rightContollerInfo.mActionThumbstick_Analog =
    547      CreateControllerAction(R, thumbstick_analog, vector2);
    548  rightContollerInfo.mActionThumbstick_Pressed =
    549      CreateControllerAction(R, thumbstick_pressed, boolean);
    550  rightContollerInfo.mActionThumbstick_Touched =
    551      CreateControllerAction(R, thumbstick_touched, boolean);
    552  rightContollerInfo.mActionFingerIndex_Value =
    553      CreateControllerAction(R, finger_index_value, vector1);
    554  rightContollerInfo.mActionFingerMiddle_Value =
    555      CreateControllerAction(R, finger_middle_value, vector1);
    556  rightContollerInfo.mActionFingerRing_Value =
    557      CreateControllerAction(R, finger_ring_value, vector1);
    558  rightContollerInfo.mActionFingerPinky_Value =
    559      CreateControllerAction(R, finger_pinky_value, vector1);
    560  rightContollerInfo.mActionBumper_Pressed =
    561      CreateControllerAction(R, bumper_pressed, boolean);
    562  rightContollerInfo.mActionHaptic =
    563      CreateControllerOutAction(R, haptic, vibration);
    564 
    565  mControllerHand[OpenVRHand::Left] = leftContollerInfo;
    566  mControllerHand[OpenVRHand::Right] = rightContollerInfo;
    567 
    568  if (!controllerAction.Length() || !FileIsExisting(controllerAction)) {
    569    if (!GenerateTempFileName(controllerAction)) {
    570      return false;
    571    }
    572    JSONStringWriteFunc<nsCString> actionData;
    573    JSONWriter actionWriter(actionData);
    574    actionWriter.Start();
    575 
    576    actionWriter.StringProperty("version",
    577                                "0.1.0");  // TODO: adding a version check.
    578    // "default_bindings": []
    579    actionWriter.StartArrayProperty("default_bindings");
    580 
    581    auto SetupActionWriterByControllerType = [&](const char* aType,
    582                                                 const nsCString& aManifest) {
    583      actionWriter.StartObjectElement();
    584      actionWriter.StringProperty("controller_type", MakeStringSpan(aType));
    585      actionWriter.StringProperty("binding_url", aManifest);
    586      actionWriter.EndObject();
    587    };
    588    SetupActionWriterByControllerType("vive_controller", viveManifest);
    589    SetupActionWriterByControllerType("knuckles", knucklesManifest);
    590    SetupActionWriterByControllerType("vive_cosmos_controller", cosmosManifest);
    591 #if defined(XP_WIN)
    592    SetupActionWriterByControllerType("holographic_controller", WMRManifest);
    593 #endif
    594    actionWriter.EndArray();  // End "default_bindings": []
    595 
    596    actionWriter.StartArrayProperty("actions");
    597 
    598    for (auto& controller : mControllerHand) {
    599      auto SetActionsToWriter = [&](const ControllerAction& aAction) {
    600        actionWriter.StartObjectElement();
    601        actionWriter.StringProperty("name", aAction.name);
    602        actionWriter.StringProperty("type", aAction.type);
    603        actionWriter.EndObject();
    604      };
    605 
    606      SetActionsToWriter(controller.mActionPose);
    607      SetActionsToWriter(controller.mActionTrackpad_Analog);
    608      SetActionsToWriter(controller.mActionTrackpad_Pressed);
    609      SetActionsToWriter(controller.mActionTrackpad_Touched);
    610      SetActionsToWriter(controller.mActionTrigger_Value);
    611      SetActionsToWriter(controller.mActionGrip_Pressed);
    612      SetActionsToWriter(controller.mActionGrip_Touched);
    613      SetActionsToWriter(controller.mActionMenu_Pressed);
    614      SetActionsToWriter(controller.mActionMenu_Touched);
    615      SetActionsToWriter(controller.mActionSystem_Pressed);
    616      SetActionsToWriter(controller.mActionSystem_Touched);
    617      SetActionsToWriter(controller.mActionA_Pressed);
    618      SetActionsToWriter(controller.mActionA_Touched);
    619      SetActionsToWriter(controller.mActionB_Pressed);
    620      SetActionsToWriter(controller.mActionB_Touched);
    621      SetActionsToWriter(controller.mActionThumbstick_Analog);
    622      SetActionsToWriter(controller.mActionThumbstick_Pressed);
    623      SetActionsToWriter(controller.mActionThumbstick_Touched);
    624      SetActionsToWriter(controller.mActionFingerIndex_Value);
    625      SetActionsToWriter(controller.mActionFingerMiddle_Value);
    626      SetActionsToWriter(controller.mActionFingerRing_Value);
    627      SetActionsToWriter(controller.mActionFingerPinky_Value);
    628      SetActionsToWriter(controller.mActionBumper_Pressed);
    629      SetActionsToWriter(controller.mActionHaptic);
    630    }
    631    actionWriter.EndArray();  // End "actions": []
    632    actionWriter.End();
    633 
    634    std::ofstream actionfile(controllerAction.BeginReading());
    635    if (actionfile.is_open()) {
    636      actionfile << actionData.StringCRef().get();
    637      actionfile.close();
    638    }
    639  }
    640 
    641  vr::EVRInputError err =
    642      vr::VRInput()->SetActionManifestPath(controllerAction.BeginReading());
    643  if (err != vr::VRInputError_None) {
    644    NS_WARNING("OpenVR - SetActionManifestPath failed.");
    645    return false;
    646  }
    647  // End of setup controller actions.
    648 
    649  // Notify the parent process these manifest files are already been recorded.
    650  if (StaticPrefs::dom_vr_process_enabled_AtStartup()) {
    651    NS_DispatchToMainThread(NS_NewRunnableFunction(
    652        "SendOpenVRControllerActionPathToParent",
    653        [controllerAction, viveManifest, WMRManifest, knucklesManifest,
    654         cosmosManifest]() {
    655          VRParent* vrParent = VRProcessChild::GetVRParent();
    656          (void)vrParent->SendOpenVRControllerActionPathToParent(
    657              controllerAction);
    658          (void)vrParent->SendOpenVRControllerManifestPathToParent(
    659              VRControllerType::HTCVive, viveManifest);
    660          (void)vrParent->SendOpenVRControllerManifestPathToParent(
    661              VRControllerType::MSMR, WMRManifest);
    662          (void)vrParent->SendOpenVRControllerManifestPathToParent(
    663              VRControllerType::ValveIndex, knucklesManifest);
    664          (void)vrParent->SendOpenVRControllerManifestPathToParent(
    665              VRControllerType::HTCViveCosmos, cosmosManifest);
    666        }));
    667  } else {
    668    sControllerActionFile->SetFileName(controllerAction.BeginReading());
    669  }
    670 
    671  return true;
    672 }
    673 
    674 #if defined(XP_WIN)
    675 bool OpenVRSession::CreateD3DObjects() {
    676  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetVRDevice();
    677  if (!device) {
    678    return false;
    679  }
    680  if (!CreateD3DContext(device)) {
    681    return false;
    682  }
    683  return true;
    684 }
    685 #endif
    686 
    687 void OpenVRSession::Shutdown() {
    688  StopHapticTimer();
    689  StopHapticThread();
    690  if (mVRSystem || mVRCompositor || mVRChaperone) {
    691    ::vr::VR_Shutdown();
    692    mVRCompositor = nullptr;
    693    mVRChaperone = nullptr;
    694    mVRSystem = nullptr;
    695  }
    696 }
    697 
    698 bool OpenVRSession::InitState(VRSystemState& aSystemState) {
    699  VRDisplayState& state = aSystemState.displayState;
    700  strncpy(state.displayName.data(), "OpenVR HMD", kVRDisplayNameMaxLen);
    701  state.eightCC = GFX_VR_EIGHTCC('O', 'p', 'e', 'n', 'V', 'R', ' ', ' ');
    702  state.isConnected =
    703      mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
    704  state.isMounted = false;
    705  state.capabilityFlags =
    706      (VRDisplayCapabilityFlags)((int)VRDisplayCapabilityFlags::Cap_None |
    707                                 (int)
    708                                     VRDisplayCapabilityFlags::Cap_Orientation |
    709                                 (int)VRDisplayCapabilityFlags::Cap_Position |
    710                                 (int)VRDisplayCapabilityFlags::Cap_External |
    711                                 (int)VRDisplayCapabilityFlags::Cap_Present |
    712                                 (int)VRDisplayCapabilityFlags::
    713                                     Cap_StageParameters |
    714                                 (int)
    715                                     VRDisplayCapabilityFlags::Cap_ImmersiveVR);
    716  state.blendMode = VRDisplayBlendMode::Opaque;
    717  state.reportsDroppedFrames = true;
    718 
    719  ::vr::ETrackedPropertyError err;
    720  bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(
    721      ::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool,
    722      &err);
    723  if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
    724    state.capabilityFlags =
    725        (VRDisplayCapabilityFlags)((int)state.capabilityFlags |
    726                                   (int)VRDisplayCapabilityFlags::
    727                                       Cap_MountDetection);
    728  }
    729 
    730  uint32_t w, h;
    731  mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
    732  state.eyeResolution.width = w;
    733  state.eyeResolution.height = h;
    734  state.nativeFramebufferScaleFactor = 1.0f;
    735 
    736  // default to an identity quaternion
    737  aSystemState.sensorState.pose.orientation[3] = 1.0f;
    738 
    739  UpdateStageParameters(state);
    740  UpdateEyeParameters(aSystemState);
    741 
    742  VRHMDSensorState& sensorState = aSystemState.sensorState;
    743  sensorState.flags =
    744      (VRDisplayCapabilityFlags)((int)
    745                                     VRDisplayCapabilityFlags::Cap_Orientation |
    746                                 (int)VRDisplayCapabilityFlags::Cap_Position);
    747  sensorState.pose.orientation[3] = 1.0f;  // Default to an identity quaternion
    748 
    749  return true;
    750 }
    751 
    752 void OpenVRSession::UpdateStageParameters(VRDisplayState& aState) {
    753  float sizeX = 0.0f;
    754  float sizeZ = 0.0f;
    755  if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
    756    ::vr::HmdMatrix34_t t =
    757        mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
    758    aState.stageSize.width = sizeX;
    759    aState.stageSize.height = sizeZ;
    760 
    761    aState.sittingToStandingTransform[0] = t.m[0][0];
    762    aState.sittingToStandingTransform[1] = t.m[1][0];
    763    aState.sittingToStandingTransform[2] = t.m[2][0];
    764    aState.sittingToStandingTransform[3] = 0.0f;
    765 
    766    aState.sittingToStandingTransform[4] = t.m[0][1];
    767    aState.sittingToStandingTransform[5] = t.m[1][1];
    768    aState.sittingToStandingTransform[6] = t.m[2][1];
    769    aState.sittingToStandingTransform[7] = 0.0f;
    770 
    771    aState.sittingToStandingTransform[8] = t.m[0][2];
    772    aState.sittingToStandingTransform[9] = t.m[1][2];
    773    aState.sittingToStandingTransform[10] = t.m[2][2];
    774    aState.sittingToStandingTransform[11] = 0.0f;
    775 
    776    aState.sittingToStandingTransform[12] = t.m[0][3];
    777    aState.sittingToStandingTransform[13] = t.m[1][3];
    778    aState.sittingToStandingTransform[14] = t.m[2][3];
    779    aState.sittingToStandingTransform[15] = 1.0f;
    780  } else {
    781    // If we fail, fall back to reasonable defaults.
    782    // 1m x 1m space, 0.75m high in seated position
    783    aState.stageSize.width = 1.0f;
    784    aState.stageSize.height = 1.0f;
    785 
    786    aState.sittingToStandingTransform[0] = 1.0f;
    787    aState.sittingToStandingTransform[1] = 0.0f;
    788    aState.sittingToStandingTransform[2] = 0.0f;
    789    aState.sittingToStandingTransform[3] = 0.0f;
    790 
    791    aState.sittingToStandingTransform[4] = 0.0f;
    792    aState.sittingToStandingTransform[5] = 1.0f;
    793    aState.sittingToStandingTransform[6] = 0.0f;
    794    aState.sittingToStandingTransform[7] = 0.0f;
    795 
    796    aState.sittingToStandingTransform[8] = 0.0f;
    797    aState.sittingToStandingTransform[9] = 0.0f;
    798    aState.sittingToStandingTransform[10] = 1.0f;
    799    aState.sittingToStandingTransform[11] = 0.0f;
    800 
    801    aState.sittingToStandingTransform[12] = 0.0f;
    802    aState.sittingToStandingTransform[13] = 0.75f;
    803    aState.sittingToStandingTransform[14] = 0.0f;
    804    aState.sittingToStandingTransform[15] = 1.0f;
    805  }
    806 }
    807 
    808 void OpenVRSession::UpdateEyeParameters(VRSystemState& aState) {
    809  // This must be called every frame in order to
    810  // account for continuous adjustments to ipd.
    811  gfx::Matrix4x4 headToEyeTransforms[2];
    812 
    813  for (uint32_t eye = 0; eye < 2; ++eye) {
    814    ::vr::HmdMatrix34_t eyeToHead =
    815        mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
    816    aState.displayState.eyeTranslation[eye].x = eyeToHead.m[0][3];
    817    aState.displayState.eyeTranslation[eye].y = eyeToHead.m[1][3];
    818    aState.displayState.eyeTranslation[eye].z = eyeToHead.m[2][3];
    819 
    820    float left, right, up, down;
    821    mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right,
    822                                &up, &down);
    823    aState.displayState.eyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
    824    aState.displayState.eyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
    825    aState.displayState.eyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
    826    aState.displayState.eyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
    827 
    828    Matrix4x4 pose;
    829    // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
    830    // because of its arrangement, we can copy the 12 elements in and
    831    // then transpose them to the right place.
    832    memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
    833    pose.Transpose();
    834    pose.Invert();
    835    headToEyeTransforms[eye] = pose;
    836  }
    837  aState.sensorState.CalcViewMatrices(headToEyeTransforms);
    838 }
    839 
    840 void OpenVRSession::UpdateHeadsetPose(VRSystemState& aState) {
    841  const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
    842  ::vr::TrackedDevicePose_t poses[posesSize];
    843  // Note: We *must* call WaitGetPoses in order for any rendering to happen at
    844  // all.
    845  mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
    846 
    847  ::vr::Compositor_FrameTiming timing;
    848  timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
    849  if (mVRCompositor->GetFrameTiming(&timing)) {
    850    aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
    851  } else {
    852    // This should not happen, but log it just in case
    853    fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
    854  }
    855 
    856  if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
    857      poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
    858      poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult ==
    859          ::vr::TrackingResult_Running_OK) {
    860    const ::vr::TrackedDevicePose_t& pose =
    861        poses[::vr::k_unTrackedDeviceIndex_Hmd];
    862 
    863    gfx::Matrix4x4 m;
    864    // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
    865    // because of its arrangement, we can copy the 12 elements in and
    866    // then transpose them to the right place.  We do this so we can
    867    // pull out a Quaternion.
    868    memcpy(&m._11, &pose.mDeviceToAbsoluteTracking,
    869           sizeof(pose.mDeviceToAbsoluteTracking));
    870    m.Transpose();
    871 
    872    gfx::Quaternion rot;
    873    rot.SetFromRotationMatrix(m);
    874 
    875    aState.sensorState.flags =
    876        (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
    877                                   (int)VRDisplayCapabilityFlags::
    878                                       Cap_Orientation);
    879    aState.sensorState.pose.orientation[0] = rot.x;
    880    aState.sensorState.pose.orientation[1] = rot.y;
    881    aState.sensorState.pose.orientation[2] = rot.z;
    882    aState.sensorState.pose.orientation[3] = rot.w;
    883    aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
    884    aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
    885    aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
    886 
    887    aState.sensorState.flags =
    888        (VRDisplayCapabilityFlags)((int)aState.sensorState.flags |
    889                                   (int)VRDisplayCapabilityFlags::Cap_Position);
    890    aState.sensorState.pose.position[0] = m._41;
    891    aState.sensorState.pose.position[1] = m._42;
    892    aState.sensorState.pose.position[2] = m._43;
    893    aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
    894    aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
    895    aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
    896  }
    897 }
    898 
    899 void OpenVRSession::EnumerateControllers(VRSystemState& aState) {
    900  MOZ_ASSERT(mVRSystem);
    901 
    902  MutexAutoLock lock(mControllerHapticStateMutex);
    903 
    904  bool controllerPresent[kVRControllerMaxCount] = {false};
    905  uint32_t stateIndex = 0;
    906  mActionsetFirefox = vr::k_ulInvalidActionSetHandle;
    907  VRControllerType controllerType = VRControllerType::_empty;
    908 
    909  if (vr::VRInput()->GetActionSetHandle(
    910          "/actions/firefox", &mActionsetFirefox) != vr::VRInputError_None) {
    911    return;
    912  }
    913 
    914  for (int8_t handIndex = 0; handIndex < OpenVRHand::Total; ++handIndex) {
    915    if (handIndex == OpenVRHand::Left) {
    916      if (vr::VRInput()->GetInputSourceHandle(
    917              "/user/hand/left", &mControllerHand[OpenVRHand::Left].mSource) !=
    918          vr::VRInputError_None) {
    919        continue;
    920      }
    921    } else if (handIndex == OpenVRHand::Right) {
    922      if (vr::VRInput()->GetInputSourceHandle(
    923              "/user/hand/right",
    924              &mControllerHand[OpenVRHand::Right].mSource) !=
    925          vr::VRInputError_None) {
    926        continue;
    927      }
    928    } else {
    929      MOZ_ASSERT(false, "Unknown OpenVR hand type.");
    930    }
    931 
    932    vr::InputOriginInfo_t originInfo;
    933    if (vr::VRInput()->GetOriginTrackedDeviceInfo(
    934            mControllerHand[handIndex].mSource, &originInfo,
    935            sizeof(originInfo)) == vr::VRInputError_None &&
    936        originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid &&
    937        mVRSystem->IsTrackedDeviceConnected(originInfo.trackedDeviceIndex)) {
    938      const ::vr::ETrackedDeviceClass deviceType =
    939          mVRSystem->GetTrackedDeviceClass(originInfo.trackedDeviceIndex);
    940      if (deviceType != ::vr::TrackedDeviceClass_Controller &&
    941          deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
    942        continue;
    943      }
    944 
    945      if (mControllerDeviceIndex[stateIndex] != handIndex) {
    946        VRControllerState& controllerState = aState.controllerState[stateIndex];
    947 
    948        // Get controllers' action handles.
    949        auto SetActionsToWriter = [&](ControllerAction& aAction) {
    950          vr::VRInput()->GetActionHandle(aAction.name.BeginReading(),
    951                                         &aAction.handle);
    952        };
    953 
    954        SetActionsToWriter(mControllerHand[handIndex].mActionPose);
    955        SetActionsToWriter(mControllerHand[handIndex].mActionHaptic);
    956        SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Analog);
    957        SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Pressed);
    958        SetActionsToWriter(mControllerHand[handIndex].mActionTrackpad_Touched);
    959        SetActionsToWriter(mControllerHand[handIndex].mActionTrigger_Value);
    960        SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Pressed);
    961        SetActionsToWriter(mControllerHand[handIndex].mActionGrip_Touched);
    962        SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Pressed);
    963        SetActionsToWriter(mControllerHand[handIndex].mActionMenu_Touched);
    964        SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Pressed);
    965        SetActionsToWriter(mControllerHand[handIndex].mActionSystem_Touched);
    966        SetActionsToWriter(mControllerHand[handIndex].mActionA_Pressed);
    967        SetActionsToWriter(mControllerHand[handIndex].mActionA_Touched);
    968        SetActionsToWriter(mControllerHand[handIndex].mActionB_Pressed);
    969        SetActionsToWriter(mControllerHand[handIndex].mActionB_Touched);
    970        SetActionsToWriter(mControllerHand[handIndex].mActionThumbstick_Analog);
    971        SetActionsToWriter(
    972            mControllerHand[handIndex].mActionThumbstick_Pressed);
    973        SetActionsToWriter(
    974            mControllerHand[handIndex].mActionThumbstick_Touched);
    975        SetActionsToWriter(mControllerHand[handIndex].mActionFingerIndex_Value);
    976        SetActionsToWriter(
    977            mControllerHand[handIndex].mActionFingerMiddle_Value);
    978        SetActionsToWriter(mControllerHand[handIndex].mActionFingerRing_Value);
    979        SetActionsToWriter(mControllerHand[handIndex].mActionFingerPinky_Value);
    980        SetActionsToWriter(mControllerHand[handIndex].mActionBumper_Pressed);
    981 
    982        nsCString deviceId;
    983        VRControllerType contrlType = VRControllerType::_empty;
    984        GetControllerDeviceId(deviceType, originInfo.trackedDeviceIndex,
    985                              deviceId, contrlType);
    986        // Controllers should be the same type with one VR display.
    987        MOZ_ASSERT(controllerType == contrlType ||
    988                   controllerType == VRControllerType::_empty);
    989        controllerType = contrlType;
    990        strncpy(controllerState.controllerName.data(), deviceId.BeginReading(),
    991                controllerState.controllerName.size());
    992        controllerState.numHaptics = kNumOpenVRHaptics;
    993        controllerState.targetRayMode = gfx::TargetRayMode::TrackedPointer;
    994        controllerState.type = controllerType;
    995      }
    996      controllerPresent[stateIndex] = true;
    997      mControllerDeviceIndex[stateIndex] = static_cast<OpenVRHand>(handIndex);
    998      ++stateIndex;
    999    }
   1000  }
   1001 
   1002  // Clear out entries for disconnected controllers
   1003  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
   1004       stateIndex++) {
   1005    if (!controllerPresent[stateIndex] &&
   1006        mControllerDeviceIndex[stateIndex] != OpenVRHand::None) {
   1007      mControllerDeviceIndex[stateIndex] = OpenVRHand::None;
   1008      memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
   1009    }
   1010  }
   1011 
   1012  // Create controller mapper
   1013  if (controllerType != VRControllerType::_empty) {
   1014    switch (controllerType) {
   1015      case VRControllerType::HTCVive:
   1016        mControllerMapper = MakeUnique<OpenVRViveMapper>();
   1017        break;
   1018      case VRControllerType::HTCViveCosmos:
   1019        mControllerMapper = MakeUnique<OpenVRCosmosMapper>();
   1020        break;
   1021 #if defined(XP_WIN)
   1022      case VRControllerType::MSMR:
   1023        mControllerMapper = MakeUnique<OpenVRWMRMapper>();
   1024        break;
   1025 #endif
   1026      case VRControllerType::ValveIndex:
   1027        mControllerMapper = MakeUnique<OpenVRKnucklesMapper>();
   1028        break;
   1029      default:
   1030        mControllerMapper = MakeUnique<OpenVRDefaultMapper>();
   1031        NS_WARNING("Undefined controller type");
   1032        break;
   1033    }
   1034  }
   1035 }
   1036 
   1037 void OpenVRSession::UpdateControllerButtons(VRSystemState& aState) {
   1038  MOZ_ASSERT(mVRSystem);
   1039 
   1040  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
   1041       ++stateIndex) {
   1042    const OpenVRHand role = mControllerDeviceIndex[stateIndex];
   1043    if (role == OpenVRHand::None) {
   1044      continue;
   1045    }
   1046    VRControllerState& controllerState = aState.controllerState[stateIndex];
   1047    controllerState.hand = GetControllerHandFromControllerRole(role);
   1048    mControllerMapper->UpdateButtons(controllerState, mControllerHand[role]);
   1049    SetControllerSelectionAndSqueezeFrameId(
   1050        controllerState, aState.displayState.lastSubmittedFrameId);
   1051  }
   1052 }
   1053 
   1054 void OpenVRSession::UpdateControllerPoses(VRSystemState& aState) {
   1055  MOZ_ASSERT(mVRSystem);
   1056 
   1057  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
   1058       ++stateIndex) {
   1059    const OpenVRHand role = mControllerDeviceIndex[stateIndex];
   1060    if (role == OpenVRHand::None) {
   1061      continue;
   1062    }
   1063    VRControllerState& controllerState = aState.controllerState[stateIndex];
   1064    vr::InputPoseActionData_t poseData;
   1065    if (vr::VRInput()->GetPoseActionDataRelativeToNow(
   1066            mControllerHand[role].mActionPose.handle,
   1067            vr::TrackingUniverseSeated, 0, &poseData, sizeof(poseData),
   1068            vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None ||
   1069        !poseData.bActive || !poseData.pose.bPoseIsValid) {
   1070      controllerState.isOrientationValid = false;
   1071      controllerState.isPositionValid = false;
   1072    } else {
   1073      const ::vr::TrackedDevicePose_t& pose = poseData.pose;
   1074      if (pose.bDeviceIsConnected) {
   1075        controllerState.flags =
   1076            (dom::GamepadCapabilityFlags::Cap_Orientation |
   1077             dom::GamepadCapabilityFlags::Cap_Position |
   1078             dom::GamepadCapabilityFlags::Cap_GripSpacePosition);
   1079      } else {
   1080        controllerState.flags = dom::GamepadCapabilityFlags::Cap_None;
   1081      }
   1082      if (pose.bPoseIsValid &&
   1083          pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
   1084        gfx::Matrix4x4 m;
   1085 
   1086        // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
   1087        // because of its arrangement, we can copy the 12 elements in and
   1088        // then transpose them to the right place.  We do this so we can
   1089        // pull out a Quaternion.
   1090        memcpy(&m.components, &pose.mDeviceToAbsoluteTracking,
   1091               sizeof(pose.mDeviceToAbsoluteTracking));
   1092        m.Transpose();
   1093 
   1094        gfx::Quaternion rot;
   1095        rot.SetFromRotationMatrix(m);
   1096 
   1097        controllerState.pose.orientation[0] = rot.x;
   1098        controllerState.pose.orientation[1] = rot.y;
   1099        controllerState.pose.orientation[2] = rot.z;
   1100        controllerState.pose.orientation[3] = rot.w;
   1101        controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
   1102        controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
   1103        controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
   1104        controllerState.pose.angularAcceleration[0] = 0.0f;
   1105        controllerState.pose.angularAcceleration[1] = 0.0f;
   1106        controllerState.pose.angularAcceleration[2] = 0.0f;
   1107        controllerState.isOrientationValid = true;
   1108 
   1109        controllerState.pose.position[0] = m._41;
   1110        controllerState.pose.position[1] = m._42;
   1111        controllerState.pose.position[2] = m._43;
   1112        controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
   1113        controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
   1114        controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
   1115        controllerState.pose.linearAcceleration[0] = 0.0f;
   1116        controllerState.pose.linearAcceleration[1] = 0.0f;
   1117        controllerState.pose.linearAcceleration[2] = 0.0f;
   1118        controllerState.isPositionValid = true;
   1119 
   1120        // Calculate its target ray space by shifting degrees in x-axis
   1121        // for ergonomic.
   1122        const float kPointerAngleDegrees = -0.698;  // 40 degrees.
   1123        gfx::Matrix4x4 rayMtx(m);
   1124        rayMtx.RotateX(kPointerAngleDegrees);
   1125        gfx::Quaternion rayRot;
   1126        rayRot.SetFromRotationMatrix(rayMtx);
   1127 
   1128        controllerState.targetRayPose = controllerState.pose;
   1129        controllerState.targetRayPose.orientation[0] = rayRot.x;
   1130        controllerState.targetRayPose.orientation[1] = rayRot.y;
   1131        controllerState.targetRayPose.orientation[2] = rayRot.z;
   1132        controllerState.targetRayPose.orientation[3] = rayRot.w;
   1133        controllerState.targetRayPose.position[0] = rayMtx._41;
   1134        controllerState.targetRayPose.position[1] = rayMtx._42;
   1135        controllerState.targetRayPose.position[2] = rayMtx._43;
   1136      }
   1137    }
   1138  }
   1139 }
   1140 
   1141 void OpenVRSession::GetControllerDeviceId(
   1142    ::vr::ETrackedDeviceClass aDeviceType,
   1143    ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId,
   1144    VRControllerType& aControllerType) {
   1145  switch (aDeviceType) {
   1146    case ::vr::TrackedDeviceClass_Controller: {
   1147      ::vr::ETrackedPropertyError err;
   1148      uint32_t requiredBufferLen;
   1149      bool isFound = false;
   1150      char charBuf[128];
   1151      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
   1152          aDeviceIndex, ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
   1153      if (requiredBufferLen > 128) {
   1154        MOZ_CRASH("Larger than the buffer size.");
   1155      }
   1156      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
   1157      nsCString deviceId(charBuf);
   1158      if (deviceId.Find("vr_controller_vive") != kNotFound) {
   1159        aId.AssignLiteral("OpenVR Gamepad");
   1160        isFound = true;
   1161        aControllerType = VRControllerType::HTCVive;
   1162      } else if (deviceId.Find("knuckles") != kNotFound ||
   1163                 deviceId.Find("valve_controller_knu") != kNotFound) {
   1164        aId.AssignLiteral("OpenVR Knuckles");
   1165        isFound = true;
   1166        aControllerType = VRControllerType::ValveIndex;
   1167      } else if (deviceId.Find("vive_cosmos_controller") != kNotFound) {
   1168        aId.AssignLiteral("OpenVR Cosmos");
   1169        isFound = true;
   1170        aControllerType = VRControllerType::HTCViveCosmos;
   1171      }
   1172      if (!isFound) {
   1173        requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(
   1174            aDeviceIndex, ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
   1175        if (requiredBufferLen > 128) {
   1176          MOZ_CRASH("Larger than the buffer size.");
   1177        }
   1178        MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
   1179        deviceId.Assign(charBuf);
   1180        if (deviceId.Find("MRSOURCE") != kNotFound) {
   1181          aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
   1182          mIsWindowsMR = true;
   1183          isFound = true;
   1184          aControllerType = VRControllerType::MSMR;
   1185        }
   1186      }
   1187      if (!isFound) {
   1188        aId.AssignLiteral("OpenVR Undefined");
   1189        aControllerType = VRControllerType::_empty;
   1190      }
   1191      break;
   1192    }
   1193    case ::vr::TrackedDeviceClass_GenericTracker: {
   1194      aId.AssignLiteral("OpenVR Tracker");
   1195      aControllerType = VRControllerType::_empty;
   1196      break;
   1197    }
   1198    default:
   1199      MOZ_ASSERT(false);
   1200      break;
   1201  }
   1202 }
   1203 
   1204 void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) {
   1205  UpdateHeadsetPose(aSystemState);
   1206  UpdateEyeParameters(aSystemState);
   1207  EnumerateControllers(aSystemState);
   1208 
   1209  vr::VRActiveActionSet_t actionSet = {0};
   1210  actionSet.ulActionSet = mActionsetFirefox;
   1211  vr::VRInput()->UpdateActionState(&actionSet, sizeof(actionSet), 1);
   1212  UpdateControllerButtons(aSystemState);
   1213  UpdateControllerPoses(aSystemState);
   1214  UpdateTelemetry(aSystemState);
   1215 }
   1216 
   1217 void OpenVRSession::ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) {
   1218  bool isHmdPresent = ::vr::VR_IsHmdPresent();
   1219  if (!isHmdPresent) {
   1220    mShouldQuit = true;
   1221  }
   1222 
   1223  ::vr::VREvent_t event;
   1224  while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
   1225    switch (event.eventType) {
   1226      case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
   1227        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
   1228          aSystemState.displayState.isMounted = true;
   1229        }
   1230        break;
   1231      case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
   1232        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
   1233          aSystemState.displayState.isMounted = false;
   1234        }
   1235        break;
   1236      case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
   1237        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
   1238          aSystemState.displayState.isConnected = true;
   1239        }
   1240        break;
   1241      case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
   1242        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
   1243          aSystemState.displayState.isConnected = false;
   1244        }
   1245        break;
   1246      case ::vr::EVREventType::VREvent_DriverRequestedQuit:
   1247      case ::vr::EVREventType::VREvent_Quit:
   1248      // When SteamVR runtime haven't been launched before viewing VR,
   1249      // SteamVR will send a VREvent_ProcessQuit event. It will tell the parent
   1250      // process to shutdown the VR process, and we need to avoid it.
   1251      // case ::vr::EVREventType::VREvent_ProcessQuit:
   1252      case ::vr::EVREventType::VREvent_QuitAcknowledged:
   1253        mShouldQuit = true;
   1254        break;
   1255      default:
   1256        // ignore
   1257        break;
   1258    }
   1259  }
   1260 }
   1261 
   1262 #if defined(XP_WIN)
   1263 bool OpenVRSession::SubmitFrame(
   1264    const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
   1265    ID3D11Texture2D* aTexture) {
   1266  return SubmitFrame((void*)aTexture, ::vr::ETextureType::TextureType_DirectX,
   1267                     aLayer.leftEyeRect, aLayer.rightEyeRect);
   1268 }
   1269 #elif defined(XP_MACOSX)
   1270 bool OpenVRSession::SubmitFrame(
   1271    const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer,
   1272    const VRLayerTextureHandle& aTexture) {
   1273  return SubmitFrame(aTexture, ::vr::ETextureType::TextureType_IOSurface,
   1274                     aLayer.leftEyeRect, aLayer.rightEyeRect);
   1275 }
   1276 #endif
   1277 
   1278 bool OpenVRSession::SubmitFrame(const VRLayerTextureHandle& aTextureHandle,
   1279                                ::vr::ETextureType aTextureType,
   1280                                const VRLayerEyeRect& aLeftEyeRect,
   1281                                const VRLayerEyeRect& aRightEyeRect) {
   1282  ::vr::Texture_t tex;
   1283 #if defined(XP_MACOSX)
   1284  // We get aTextureHandle from get_SurfaceDescriptorMacIOSurface() at
   1285  // VRDisplayExternal. scaleFactor and opaque are skipped because they always
   1286  // are 1.0 and false.
   1287  RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(aTextureHandle);
   1288  if (!surf) {
   1289    NS_WARNING("OpenVRSession::SubmitFrame failed to get a MacIOSurface");
   1290    return false;
   1291  }
   1292 
   1293  CFTypeRefPtr<IOSurfaceRef> ioSurface = surf->GetIOSurfaceRef();
   1294  tex.handle = (void*)ioSurface.get();
   1295 #else
   1296  tex.handle = aTextureHandle;
   1297 #endif
   1298  tex.eType = aTextureType;
   1299  tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
   1300 
   1301  ::vr::VRTextureBounds_t bounds;
   1302  bounds.uMin = aLeftEyeRect.x;
   1303  bounds.vMin = 1.0 - aLeftEyeRect.y;
   1304  bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
   1305  bounds.vMax = 1.0 - (aLeftEyeRect.y + aLeftEyeRect.height);
   1306 
   1307  ::vr::EVRCompositorError err;
   1308  err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
   1309  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
   1310    printf_stderr("OpenVR Compositor Submit() failed.\n");
   1311  }
   1312 
   1313  bounds.uMin = aRightEyeRect.x;
   1314  bounds.vMin = 1.0 - aRightEyeRect.y;
   1315  bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
   1316  bounds.vMax = 1.0 - (aRightEyeRect.y + aRightEyeRect.height);
   1317 
   1318  err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
   1319  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
   1320    printf_stderr("OpenVR Compositor Submit() failed.\n");
   1321  }
   1322 
   1323  mVRCompositor->PostPresentHandoff();
   1324  return true;
   1325 }
   1326 
   1327 void OpenVRSession::StopPresentation() {
   1328  mVRCompositor->ClearLastSubmittedFrame();
   1329 
   1330  ::vr::Compositor_CumulativeStats stats;
   1331  mVRCompositor->GetCumulativeStats(&stats,
   1332                                    sizeof(::vr::Compositor_CumulativeStats));
   1333 }
   1334 
   1335 bool OpenVRSession::StartPresentation() { return true; }
   1336 
   1337 void OpenVRSession::VibrateHaptic(uint32_t aControllerIdx,
   1338                                  uint32_t aHapticIndex, float aIntensity,
   1339                                  float aDuration) {
   1340  MutexAutoLock lock(mControllerHapticStateMutex);
   1341 
   1342  // Initilize the haptic thread when the first time to do vibration.
   1343  if (!mHapticThread) {
   1344    NS_DispatchToMainThread(NS_NewRunnableFunction(
   1345        "OpenVRSession::StartHapticThread", [this]() { StartHapticThread(); }));
   1346  }
   1347  if (aHapticIndex >= kNumOpenVRHaptics ||
   1348      aControllerIdx >= kVRControllerMaxCount) {
   1349    return;
   1350  }
   1351 
   1352  const OpenVRHand role = mControllerDeviceIndex[aControllerIdx];
   1353  if (role == OpenVRHand::None) {
   1354    return;
   1355  }
   1356  mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
   1357  mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
   1358 }
   1359 
   1360 void OpenVRSession::StartHapticThread() {
   1361  MOZ_ASSERT(NS_IsMainThread());
   1362  if (!mHapticThread) {
   1363    mHapticThread = new VRThread("VR_OpenVR_Haptics"_ns);
   1364  }
   1365  mHapticThread->Start();
   1366  StartHapticTimer();
   1367 }
   1368 
   1369 void OpenVRSession::StopHapticThread() {
   1370  if (mHapticThread) {
   1371    NS_DispatchToMainThread(NS_NewRunnableFunction(
   1372        "mHapticThread::Shutdown",
   1373        [thread = mHapticThread]() { thread->Shutdown(); }));
   1374    mHapticThread = nullptr;
   1375  }
   1376 }
   1377 
   1378 void OpenVRSession::StartHapticTimer() {
   1379  if (!mHapticTimer && mHapticThread) {
   1380    mLastHapticUpdate = TimeStamp();
   1381    mHapticTimer = NS_NewTimer();
   1382    nsCOMPtr<nsIThread> thread = mHapticThread->GetThread();
   1383    mHapticTimer->SetTarget(thread);
   1384    mHapticTimer->InitWithNamedFuncCallback(
   1385        HapticTimerCallback, this, kVRHapticUpdateInterval,
   1386        nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP,
   1387        "OpenVRSession::HapticTimerCallback"_ns);
   1388  }
   1389 }
   1390 
   1391 void OpenVRSession::StopHapticTimer() {
   1392  if (mHapticTimer) {
   1393    mHapticTimer->Cancel();
   1394    mHapticTimer = nullptr;
   1395  }
   1396 }
   1397 
   1398 /*static*/
   1399 void OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) {
   1400  /**
   1401   * It is safe to use the pointer passed in aClosure to reference the
   1402   * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown,
   1403   * which is called by the OpenVRSession destructor, guaranteeing
   1404   * that this function runs if and only if the VRManager object is valid.
   1405   */
   1406  OpenVRSession* self = static_cast<OpenVRSession*>(aClosure);
   1407  MOZ_ASSERT(self);
   1408  self->UpdateHaptics();
   1409 }
   1410 
   1411 void OpenVRSession::UpdateHaptics() {
   1412  MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread());
   1413  MOZ_ASSERT(mVRSystem);
   1414 
   1415  MutexAutoLock lock(mControllerHapticStateMutex);
   1416 
   1417  TimeStamp now = TimeStamp::Now();
   1418  if (mLastHapticUpdate.IsNull()) {
   1419    mLastHapticUpdate = now;
   1420    return;
   1421  }
   1422  float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds();
   1423  mLastHapticUpdate = now;
   1424 
   1425  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount;
   1426       ++stateIndex) {
   1427    const OpenVRHand role = mControllerDeviceIndex[stateIndex];
   1428    if (role == OpenVRHand::None) {
   1429      continue;
   1430    }
   1431    for (uint32_t hapticIdx = 0; hapticIdx < kNumOpenVRHaptics; hapticIdx++) {
   1432      float intensity = mHapticPulseIntensity[stateIndex][hapticIdx];
   1433      float duration = mHapticPulseRemaining[stateIndex][hapticIdx];
   1434      if (duration <= 0.0f || intensity <= 0.0f) {
   1435        continue;
   1436      }
   1437      vr::VRInput()->TriggerHapticVibrationAction(
   1438          mControllerHand[role].mActionHaptic.handle, 0.0f, deltaTime, 4.0f,
   1439          intensity > 1.0f ? 1.0f : intensity, vr::k_ulInvalidInputValueHandle);
   1440 
   1441      duration -= deltaTime;
   1442      if (duration < 0.0f) {
   1443        duration = 0.0f;
   1444      }
   1445      mHapticPulseRemaining[stateIndex][hapticIdx] = duration;
   1446    }
   1447  }
   1448 }
   1449 
   1450 void OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) {
   1451  MutexAutoLock lock(mControllerHapticStateMutex);
   1452  if (aControllerIdx >= kVRControllerMaxCount) {
   1453    return;
   1454  }
   1455  for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) {
   1456    mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
   1457  }
   1458 }
   1459 
   1460 void OpenVRSession::StopAllHaptics() {
   1461  MutexAutoLock lock(mControllerHapticStateMutex);
   1462  for (auto& controller : mHapticPulseRemaining) {
   1463    for (auto& haptic : controller) {
   1464      haptic = 0.0f;
   1465    }
   1466  }
   1467 }
   1468 
   1469 void OpenVRSession::UpdateTelemetry(VRSystemState& aSystemState) {
   1470  ::vr::Compositor_CumulativeStats stats;
   1471  mVRCompositor->GetCumulativeStats(&stats,
   1472                                    sizeof(::vr::Compositor_CumulativeStats));
   1473  aSystemState.displayState.droppedFrameCount = stats.m_nNumReprojectedFrames;
   1474 }
   1475 
   1476 }  // namespace mozilla::gfx