tor-browser

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

XRInputSource.cpp (14942B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/XRInputSource.h"
      8 
      9 #include "VRDisplayClient.h"
     10 #include "XRInputSpace.h"
     11 #include "XRNativeOriginTracker.h"
     12 #include "XRNativeOriginViewer.h"
     13 #include "mozilla/dom/Gamepad.h"
     14 #include "mozilla/dom/GamepadManager.h"
     15 #include "mozilla/dom/XRInputSourceEvent.h"
     16 
     17 namespace mozilla::dom {
     18 
     19 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRInputSource, mParent, mTargetRaySpace,
     20                                      mGripSpace, mGamepad)
     21 
     22 // Follow the controller profile ids from
     23 // https://github.com/immersive-web/webxr-input-profiles.
     24 nsTArray<nsString> GetInputSourceProfile(gfx::VRControllerType aType) {
     25  nsTArray<nsString> profile;
     26  nsString id;
     27 
     28  switch (aType) {
     29    case gfx::VRControllerType::HTCVive:
     30      id.AssignLiteral("htc-vive");
     31      profile.AppendElement(id);
     32      id.AssignLiteral("generic-trigger-squeeze-touchpad");
     33      profile.AppendElement(id);
     34      break;
     35    case gfx::VRControllerType::HTCViveCosmos:
     36      id.AssignLiteral("htc-vive-cosmos");
     37      profile.AppendElement(id);
     38      id.AssignLiteral("generic-trigger-squeeze-thumbstick");
     39      profile.AppendElement(id);
     40      break;
     41    case gfx::VRControllerType::HTCViveFocus:
     42      id.AssignLiteral("htc-vive-focus");
     43      profile.AppendElement(id);
     44      id.AssignLiteral("generic-trigger-touchpad");
     45      profile.AppendElement(id);
     46      break;
     47    case gfx::VRControllerType::HTCViveFocusPlus:
     48      id.AssignLiteral("htc-vive-focus-plus");
     49      profile.AppendElement(id);
     50      id.AssignLiteral("generic-trigger-squeeze-touchpad");
     51      profile.AppendElement(id);
     52      break;
     53    case gfx::VRControllerType::MSMR:
     54      id.AssignLiteral("microsoft-mixed-reality");
     55      profile.AppendElement(id);
     56      id.AssignLiteral("generic-trigger-squeeze-touchpad-thumbstick");
     57      profile.AppendElement(id);
     58      break;
     59    case gfx::VRControllerType::ValveIndex:
     60      id.AssignLiteral("valve-index");
     61      profile.AppendElement(id);
     62      id.AssignLiteral("generic-trigger-squeeze-touchpad-thumbstick");
     63      profile.AppendElement(id);
     64      break;
     65    case gfx::VRControllerType::OculusGo:
     66      id.AssignLiteral("oculus-go");
     67      profile.AppendElement(id);
     68      id.AssignLiteral("generic-trigger-touchpad");
     69      profile.AppendElement(id);
     70      break;
     71    case gfx::VRControllerType::OculusTouch:
     72      id.AssignLiteral("oculus-touch");
     73      profile.AppendElement(id);
     74      id.AssignLiteral("generic-trigger-squeeze-thumbstick");
     75      profile.AppendElement(id);
     76      break;
     77    case gfx::VRControllerType::OculusTouch2:
     78      id.AssignLiteral("oculus-touch-v2");
     79      profile.AppendElement(id);
     80      id.AssignLiteral("oculus-touch");
     81      profile.AppendElement(id);
     82      id.AssignLiteral("generic-trigger-squeeze-thumbstick");
     83      profile.AppendElement(id);
     84      break;
     85    case gfx::VRControllerType::OculusTouch3:
     86      id.AssignLiteral("oculus-touch-v3");
     87      profile.AppendElement(id);
     88      id.AssignLiteral("oculus-touch-v2");
     89      profile.AppendElement(id);
     90      id.AssignLiteral("oculus-touch");
     91      profile.AppendElement(id);
     92      id.AssignLiteral("generic-trigger-squeeze-thumbstick");
     93      profile.AppendElement(id);
     94      break;
     95    case gfx::VRControllerType::PicoGaze:
     96      id.AssignLiteral("pico-gaze");
     97      profile.AppendElement(id);
     98      id.AssignLiteral("generic-button");
     99      profile.AppendElement(id);
    100      break;
    101    case gfx::VRControllerType::PicoG2:
    102      id.AssignLiteral("pico-g2");
    103      profile.AppendElement(id);
    104      id.AssignLiteral("generic-trigger-touchpad");
    105      profile.AppendElement(id);
    106      break;
    107    case gfx::VRControllerType::PicoNeo2:
    108      id.AssignLiteral("pico-neo2");
    109      profile.AppendElement(id);
    110      id.AssignLiteral("generic-trigger-squeeze-thumbstick");
    111      profile.AppendElement(id);
    112      break;
    113    default:
    114      NS_WARNING("Unsupported XR input source profile.\n");
    115      break;
    116  }
    117  return profile;
    118 }
    119 
    120 XRInputSource::XRInputSource(nsISupports* aParent)
    121    : mParent(aParent),
    122      mGamepad(nullptr),
    123      mIndex(-1),
    124      mSelectAction(ActionState::ActionState_Released),
    125      mSqueezeAction(ActionState::ActionState_Released) {}
    126 
    127 XRInputSource::~XRInputSource() {
    128  mTargetRaySpace = nullptr;
    129  mGripSpace = nullptr;
    130  mGamepad = nullptr;
    131 }
    132 
    133 JSObject* XRInputSource::WrapObject(JSContext* aCx,
    134                                    JS::Handle<JSObject*> aGivenProto) {
    135  return XRInputSource_Binding::Wrap(aCx, this, aGivenProto);
    136 }
    137 
    138 XRHandedness XRInputSource::Handedness() { return mHandedness; }
    139 
    140 XRTargetRayMode XRInputSource::TargetRayMode() { return mTargetRayMode; }
    141 
    142 XRSpace* XRInputSource::TargetRaySpace() { return mTargetRaySpace; }
    143 
    144 XRSpace* XRInputSource::GetGripSpace() { return mGripSpace; }
    145 
    146 void XRInputSource::GetProfiles(nsTArray<nsString>& aResult) {
    147  aResult = mProfiles.Clone();
    148 }
    149 
    150 Gamepad* XRInputSource::GetGamepad() { return mGamepad; }
    151 
    152 void XRInputSource::Setup(XRSession* aSession, uint32_t aIndex) {
    153  MOZ_ASSERT(aSession);
    154  gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient();
    155  if (!displayClient) {
    156    MOZ_ASSERT(displayClient);
    157    return;
    158  }
    159  const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo();
    160  const gfx::VRControllerState& controllerState =
    161      displayInfo.mControllerState[aIndex];
    162  MOZ_ASSERT(controllerState.controllerName[0] != '\0');
    163 
    164  mProfiles = GetInputSourceProfile(controllerState.type);
    165  mHandedness = XRHandedness::None;
    166  switch (controllerState.hand) {
    167    case GamepadHand::_empty:
    168      mHandedness = XRHandedness::None;
    169      break;
    170    case GamepadHand::Left:
    171      mHandedness = XRHandedness::Left;
    172      break;
    173    case GamepadHand::Right:
    174      mHandedness = XRHandedness::Right;
    175      break;
    176    default:
    177      MOZ_ASSERT(false && "Unknown GamepadHand type.");
    178      break;
    179  }
    180 
    181  RefPtr<XRNativeOrigin> nativeOriginTargetRay = nullptr;
    182  mTargetRayMode = XRTargetRayMode::Tracked_pointer;
    183  switch (controllerState.targetRayMode) {
    184    case gfx::TargetRayMode::Gaze:
    185      mTargetRayMode = XRTargetRayMode::Gaze;
    186      nativeOriginTargetRay = new XRNativeOriginViewer(displayClient);
    187      break;
    188    case gfx::TargetRayMode::TrackedPointer:
    189      mTargetRayMode = XRTargetRayMode::Tracked_pointer;
    190      // We use weak pointers of poses in XRNativeOriginTracker to sync their
    191      // data internally.
    192      nativeOriginTargetRay =
    193          new XRNativeOriginTracker(&controllerState.targetRayPose);
    194      break;
    195    case gfx::TargetRayMode::Screen:
    196      mTargetRayMode = XRTargetRayMode::Screen;
    197      break;
    198    default:
    199      MOZ_ASSERT(false && "Undefined TargetRayMode type.");
    200      break;
    201  }
    202 
    203  mTargetRaySpace = new XRInputSpace(aSession->GetParentObject(), aSession,
    204                                     nativeOriginTargetRay, aIndex);
    205 
    206  const uint32_t gamepadHandleValue =
    207      displayInfo.mDisplayID * gfx::kVRControllerMaxCount + aIndex;
    208 
    209  const GamepadHandle gamepadHandle{gamepadHandleValue, GamepadHandleKind::VR};
    210 
    211  mGamepad = new Gamepad(mParent, NS_ConvertASCIItoUTF16(""), -1, gamepadHandle,
    212                         GamepadMappingType::Xr_standard, controllerState.hand,
    213                         controllerState.numButtons, controllerState.numAxes,
    214                         controllerState.numHaptics, 0, 0);
    215  mIndex = aIndex;
    216 
    217  if (!mGripSpace) {
    218    CreateGripSpace(aSession, controllerState);
    219  }
    220 }
    221 
    222 void XRInputSource::SetGamepadIsConnected(bool aConnected,
    223                                          XRSession* aSession) {
    224  mGamepad->SetConnected(aConnected);
    225  MOZ_ASSERT(aSession);
    226 
    227  if (!aConnected) {
    228    if (mSelectAction != ActionState::ActionState_Released) {
    229      DispatchEvent(u"selectend"_ns, aSession);
    230      mSelectAction = ActionState::ActionState_Released;
    231    }
    232    if (mSqueezeAction != ActionState::ActionState_Released) {
    233      DispatchEvent(u"squeezeend"_ns, aSession);
    234      mSqueezeAction = ActionState::ActionState_Released;
    235    }
    236  }
    237 }
    238 
    239 void XRInputSource::Update(XRSession* aSession) {
    240  MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad);
    241 
    242  gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient();
    243  if (!displayClient) {
    244    MOZ_ASSERT(displayClient);
    245    return;
    246  }
    247  const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo();
    248  const gfx::VRControllerState& controllerState =
    249      displayInfo.mControllerState[mIndex];
    250  MOZ_ASSERT(controllerState.controllerName[0] != '\0');
    251 
    252  // OculusVR and OpenVR controllers need to wait until
    253  // update functions to assign GamepadCapabilityFlags::Cap_GripSpacePosition
    254  // flag.
    255  if (!mGripSpace) {
    256    CreateGripSpace(aSession, controllerState);
    257  }
    258 
    259  // Update button values.
    260  nsTArray<RefPtr<GamepadButton>> buttons;
    261  mGamepad->GetButtons(buttons);
    262  for (uint32_t i = 0; i < buttons.Length(); ++i) {
    263    const bool pressed = controllerState.buttonPressed & (1ULL << i);
    264    const bool touched = controllerState.buttonTouched & (1ULL << i);
    265 
    266    if (buttons[i]->Pressed() != pressed || buttons[i]->Touched() != touched ||
    267        buttons[i]->Value() != controllerState.triggerValue[i]) {
    268      mGamepad->SetButton(i, pressed, touched, controllerState.triggerValue[i]);
    269    }
    270  }
    271  // Update axis values.
    272  nsTArray<double> axes;
    273  mGamepad->GetAxes(axes);
    274  for (uint32_t i = 0; i < axes.Length(); ++i) {
    275    if (axes[i] != controllerState.axisValue[i]) {
    276      mGamepad->SetAxis(i, controllerState.axisValue[i]);
    277    }
    278  }
    279 
    280  // We define 0.85f and 0.15f based on our current finding
    281  // for better experience, we can adjust these values if we need.
    282  const float completeThreshold = 0.90f;
    283  const float startThreshold = 0.85f;
    284  const float endThreshold = 0.15f;
    285  const uint32_t selectIndex = 0;
    286  const uint32_t squeezeIndex = 1;
    287 
    288  // Checking selectstart, select, selectend
    289  if (buttons.Length() > selectIndex) {
    290    if (controllerState.selectActionStartFrameId >
    291        controllerState.selectActionStopFrameId) {
    292      if (mSelectAction == ActionState::ActionState_Released &&
    293          controllerState.triggerValue[selectIndex] > endThreshold) {
    294        DispatchEvent(u"selectstart"_ns, aSession);
    295        mSelectAction = ActionState::ActionState_Pressing;
    296      } else if (mSelectAction == ActionState::ActionState_Pressing &&
    297                 controllerState.triggerValue[selectIndex] >
    298                     completeThreshold) {
    299        mSelectAction = ActionState::ActionState_Pressed;
    300      } else if (mSelectAction == ActionState::ActionState_Pressed &&
    301                 controllerState.triggerValue[selectIndex] < startThreshold) {
    302        DispatchEvent(u"select"_ns, aSession);
    303        mSelectAction = ActionState::ActionState_Releasing;
    304      } else if (mSelectAction <= ActionState::ActionState_Releasing &&
    305                 controllerState.triggerValue[selectIndex] < endThreshold) {
    306        // For a select btn which only has pressed and unpressed status.
    307        if (mSelectAction == ActionState::ActionState_Pressed) {
    308          DispatchEvent(u"select"_ns, aSession);
    309        }
    310        DispatchEvent(u"selectend"_ns, aSession);
    311        mSelectAction = ActionState::ActionState_Released;
    312      }
    313    } else if (mSelectAction <= ActionState::ActionState_Releasing) {
    314      // For a select btn which only has pressed and unpressed status.
    315      if (mSelectAction == ActionState::ActionState_Pressed) {
    316        DispatchEvent(u"select"_ns, aSession);
    317      }
    318      DispatchEvent(u"selectend"_ns, aSession);
    319      mSelectAction = ActionState::ActionState_Released;
    320    }
    321  }
    322 
    323  // Checking squeezestart, squeeze, squeezeend
    324  if (buttons.Length() > squeezeIndex) {
    325    if (controllerState.squeezeActionStartFrameId >
    326        controllerState.squeezeActionStopFrameId) {
    327      if (mSqueezeAction == ActionState::ActionState_Released &&
    328          controllerState.triggerValue[squeezeIndex] > endThreshold) {
    329        DispatchEvent(u"squeezestart"_ns, aSession);
    330        mSqueezeAction = ActionState::ActionState_Pressing;
    331      } else if (mSqueezeAction == ActionState::ActionState_Pressing &&
    332                 controllerState.triggerValue[squeezeIndex] >
    333                     completeThreshold) {
    334        mSqueezeAction = ActionState::ActionState_Pressed;
    335      } else if (mSqueezeAction == ActionState::ActionState_Pressed &&
    336                 controllerState.triggerValue[squeezeIndex] < startThreshold) {
    337        DispatchEvent(u"squeeze"_ns, aSession);
    338        mSqueezeAction = ActionState::ActionState_Releasing;
    339      } else if (mSqueezeAction <= ActionState::ActionState_Releasing &&
    340                 controllerState.triggerValue[squeezeIndex] < endThreshold) {
    341        // For a squeeze btn which only has pressed and unpressed status.
    342        if (mSqueezeAction == ActionState::ActionState_Pressed) {
    343          DispatchEvent(u"squeeze"_ns, aSession);
    344        }
    345        DispatchEvent(u"squeezeend"_ns, aSession);
    346        mSqueezeAction = ActionState::ActionState_Released;
    347      }
    348    } else if (mSqueezeAction <= ActionState::ActionState_Releasing) {
    349      // For a squeeze btn which only has pressed and unpressed status.
    350      if (mSqueezeAction == ActionState::ActionState_Pressed) {
    351        DispatchEvent(u"squeeze"_ns, aSession);
    352      }
    353      DispatchEvent(u"squeezeend"_ns, aSession);
    354      mSqueezeAction = ActionState::ActionState_Released;
    355    }
    356  }
    357 }
    358 
    359 int32_t XRInputSource::GetIndex() { return mIndex; }
    360 
    361 void XRInputSource::DispatchEvent(const nsAString& aEvent,
    362                                  XRSession* aSession) {
    363  if (!GetParentObject() || !aSession) {
    364    return;
    365  }
    366  // Create a XRFrame for its callbacks
    367  RefPtr<XRFrame> frame = new XRFrame(GetParentObject(), aSession);
    368  frame->StartInputSourceEvent();
    369 
    370  XRInputSourceEventInit init;
    371  init.mBubbles = false;
    372  init.mCancelable = false;
    373  init.mFrame = frame;
    374  init.mInputSource = this;
    375 
    376  RefPtr<XRInputSourceEvent> event =
    377      XRInputSourceEvent::Constructor(aSession, aEvent, init);
    378 
    379  event->SetTrusted(true);
    380  aSession->DispatchEvent(*event);
    381  frame->EndInputSourceEvent();
    382 }
    383 
    384 void XRInputSource::CreateGripSpace(
    385    XRSession* aSession, const gfx::VRControllerState& controllerState) {
    386  MOZ_ASSERT(!mGripSpace);
    387  MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad);
    388  if (mTargetRayMode == XRTargetRayMode::Tracked_pointer &&
    389      controllerState.flags & GamepadCapabilityFlags::Cap_GripSpacePosition) {
    390    RefPtr<XRNativeOrigin> nativeOriginGrip = nullptr;
    391    nativeOriginGrip = new XRNativeOriginTracker(&controllerState.pose);
    392    mGripSpace = new XRInputSpace(aSession->GetParentObject(), aSession,
    393                                  nativeOriginGrip, mIndex);
    394  } else {
    395    mGripSpace = nullptr;
    396  }
    397 }
    398 
    399 }  // namespace mozilla::dom