1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "device/vr/openxr/openxr_input_helper.h"
6 
7 #include "base/stl_util.h"
8 #include "device/gamepad/public/cpp/gamepad.h"
9 #include "device/vr/openxr/openxr_util.h"
10 #include "device/vr/util/xr_standard_gamepad_builder.h"
11 
12 namespace device {
13 
14 namespace {
GetAxisButtonData(OpenXrAxisType openxr_button_type,base::Optional<GamepadButton> button_data,std::vector<double> axis)15 base::Optional<GamepadBuilder::ButtonData> GetAxisButtonData(
16     OpenXrAxisType openxr_button_type,
17     base::Optional<GamepadButton> button_data,
18     std::vector<double> axis) {
19   GamepadBuilder::ButtonData data;
20   if (!button_data || axis.size() != 2) {
21     return base::nullopt;
22   }
23 
24   switch (openxr_button_type) {
25     case OpenXrAxisType::kThumbstick:
26       data.type = GamepadBuilder::ButtonData::Type::kThumbstick;
27       break;
28     case OpenXrAxisType::kTrackpad:
29       data.type = GamepadBuilder::ButtonData::Type::kTouchpad;
30       break;
31   }
32   data.touched = button_data->touched;
33   data.pressed = button_data->pressed;
34   data.value = button_data->value;
35   // Invert the y axis because -1 is up in the Gamepad API, but down in
36   // OpenXR.
37   data.x_axis = axis.at(0);
38   data.y_axis = -axis.at(1);
39   return data;
40 }
41 
GetXrStandardGamepad(const OpenXrController & controller)42 base::Optional<Gamepad> GetXrStandardGamepad(
43     const OpenXrController& controller) {
44   XRStandardGamepadBuilder builder(controller.GetHandness());
45 
46   base::Optional<GamepadButton> trigger_button =
47       controller.GetButton(OpenXrButtonType::kTrigger);
48   if (!trigger_button)
49     return base::nullopt;
50   builder.SetPrimaryButton(trigger_button.value());
51 
52   base::Optional<GamepadButton> squeeze_button =
53       controller.GetButton(OpenXrButtonType::kSqueeze);
54   if (squeeze_button)
55     builder.SetSecondaryButton(squeeze_button.value());
56 
57   base::Optional<GamepadButton> trackpad_button =
58       controller.GetButton(OpenXrButtonType::kTrackpad);
59   std::vector<double> trackpad_axis =
60       controller.GetAxis(OpenXrAxisType::kTrackpad);
61   base::Optional<GamepadBuilder::ButtonData> trackpad_button_data =
62       GetAxisButtonData(OpenXrAxisType::kTrackpad, trackpad_button,
63                         trackpad_axis);
64   if (trackpad_button_data)
65     builder.SetTouchpadData(trackpad_button_data.value());
66 
67   base::Optional<GamepadButton> thumbstick_button =
68       controller.GetButton(OpenXrButtonType::kThumbstick);
69   std::vector<double> thumbstick_axis =
70       controller.GetAxis(OpenXrAxisType::kThumbstick);
71   base::Optional<GamepadBuilder::ButtonData> thumbstick_button_data =
72       GetAxisButtonData(OpenXrAxisType::kThumbstick, thumbstick_button,
73                         thumbstick_axis);
74   if (thumbstick_button_data)
75     builder.SetThumbstickData(thumbstick_button_data.value());
76 
77   base::Optional<GamepadButton> x_button =
78       controller.GetButton(OpenXrButtonType::kButton1);
79   if (x_button)
80     builder.AddOptionalButtonData(x_button.value());
81 
82   base::Optional<GamepadButton> y_button =
83       controller.GetButton(OpenXrButtonType::kButton2);
84   if (y_button)
85     builder.AddOptionalButtonData(y_button.value());
86 
87   base::Optional<GamepadButton> thumbrest_button =
88       controller.GetButton(OpenXrButtonType::kThumbrest);
89   if (thumbrest_button)
90     builder.AddOptionalButtonData(thumbrest_button.value());
91 
92   return builder.GetGamepad();
93 }
94 
95 }  // namespace
96 
CreateOpenXRInputHelper(XrInstance instance,XrSession session,XrSpace local_space,std::unique_ptr<OpenXRInputHelper> * helper)97 XrResult OpenXRInputHelper::CreateOpenXRInputHelper(
98     XrInstance instance,
99     XrSession session,
100     XrSpace local_space,
101     std::unique_ptr<OpenXRInputHelper>* helper) {
102   std::unique_ptr<OpenXRInputHelper> new_helper =
103       std::make_unique<OpenXRInputHelper>(session, local_space);
104 
105   RETURN_IF_XR_FAILED(new_helper->Initialize(instance));
106   *helper = std::move(new_helper);
107   return XR_SUCCESS;
108 }
109 
OpenXRInputHelper(XrSession session,XrSpace local_space)110 OpenXRInputHelper::OpenXRInputHelper(XrSession session, XrSpace local_space)
111     : session_(session),
112       local_space_(local_space),
113       path_helper_(std::make_unique<OpenXRPathHelper>()) {}
114 
115 OpenXRInputHelper::~OpenXRInputHelper() = default;
116 
Initialize(XrInstance instance)117 XrResult OpenXRInputHelper::Initialize(XrInstance instance) {
118   RETURN_IF_XR_FAILED(path_helper_->Initialize(instance));
119 
120   // This map is used to store bindings for different kinds of interaction
121   // profiles. This allows the runtime to choose a different input sources based
122   // on availability.
123   std::map<XrPath, std::vector<XrActionSuggestedBinding>> bindings;
124 
125   for (size_t i = 0; i < controller_states_.size(); i++) {
126     RETURN_IF_XR_FAILED(controller_states_[i].controller.Initialize(
127         static_cast<OpenXrHandednessType>(i), instance, session_,
128         path_helper_.get(), &bindings));
129     controller_states_[i].primary_button_pressed = false;
130   }
131 
132   for (auto it = bindings.begin(); it != bindings.end(); it++) {
133     XrInteractionProfileSuggestedBinding profile_suggested_bindings = {
134         XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
135     profile_suggested_bindings.interactionProfile = it->first;
136     profile_suggested_bindings.suggestedBindings = it->second.data();
137     profile_suggested_bindings.countSuggestedBindings = it->second.size();
138 
139     RETURN_IF_XR_FAILED(xrSuggestInteractionProfileBindings(
140         instance, &profile_suggested_bindings));
141   }
142 
143   std::vector<XrActionSet> action_sets(controller_states_.size());
144   for (size_t i = 0; i < controller_states_.size(); i++) {
145     action_sets[i] = controller_states_[i].controller.action_set();
146   }
147 
148   XrSessionActionSetsAttachInfo attach_info = {
149       XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO};
150   attach_info.countActionSets = action_sets.size();
151   attach_info.actionSets = action_sets.data();
152   RETURN_IF_XR_FAILED(xrAttachSessionActionSets(session_, &attach_info));
153 
154   return XR_SUCCESS;
155 }
156 
GetInputState(XrTime predicted_display_time)157 std::vector<mojom::XRInputSourceStatePtr> OpenXRInputHelper::GetInputState(
158     XrTime predicted_display_time) {
159   std::vector<mojom::XRInputSourceStatePtr> input_states;
160   if (XR_FAILED(SyncActions(predicted_display_time))) {
161     for (OpenXrControllerState& state : controller_states_) {
162       state.primary_button_pressed = false;
163     }
164     return input_states;
165   }
166 
167   for (uint32_t i = 0; i < controller_states_.size(); i++) {
168     device::OpenXrController* controller = &controller_states_[i].controller;
169 
170     base::Optional<GamepadButton> primary_button =
171         controller->GetButton(OpenXrButtonType::kTrigger);
172 
173     // Having a trigger button is the minimum for an webxr input.
174     // No trigger button indicates input is not connected.
175     if (!primary_button) {
176       continue;
177     }
178 
179     device::mojom::XRInputSourceStatePtr state =
180         device::mojom::XRInputSourceState::New();
181 
182     // ID 0 will cause a DCHECK in the hash table used on the blink side.
183     // To ensure that we don't have any collisions with other ids, increment
184     // all of the ids by one.
185     state->source_id = i + 1;
186     state->description = controller->GetDescription(predicted_display_time);
187     if (!state->description) {
188       continue;
189     }
190 
191     state->mojo_from_input = controller->GetMojoFromGripTransform(
192         predicted_display_time, local_space_, &state->emulated_position);
193     state->primary_input_pressed = primary_button.value().pressed;
194     state->primary_input_clicked =
195         controller_states_[i].primary_button_pressed &&
196         !state->primary_input_pressed;
197     controller_states_[i].primary_button_pressed = state->primary_input_pressed;
198     state->gamepad = GetWebXRGamepad(*controller);
199     input_states.push_back(std::move(state));
200   }
201 
202   return input_states;
203 }
204 
OnInteractionProfileChanged(XrResult * xr_result)205 void OpenXRInputHelper::OnInteractionProfileChanged(XrResult* xr_result) {
206   for (OpenXrControllerState& controller_state : controller_states_) {
207     *xr_result = controller_state.controller.UpdateInteractionProfile();
208     if (XR_FAILED(*xr_result)) {
209       return;
210     }
211   }
212 }
213 
GetWeakPtr()214 base::WeakPtr<OpenXRInputHelper> OpenXRInputHelper::GetWeakPtr() {
215   return weak_ptr_factory_.GetWeakPtr();
216 }
217 
GetWebXRGamepad(const OpenXrController & controller)218 base::Optional<Gamepad> OpenXRInputHelper ::GetWebXRGamepad(
219     const OpenXrController& controller) {
220   OpenXrInteractionProfileType cur_type = controller.interaction_profile();
221   for (auto& it : kOpenXrControllerInteractionProfiles) {
222     if (it.type == cur_type) {
223       if (it.mapping == GamepadMapping::kXrStandard) {
224         return GetXrStandardGamepad(controller);
225       } else {
226         // if mapping is kNone
227         return base::nullopt;
228       }
229     }
230   }
231 
232   return base::nullopt;
233 }
234 
SyncActions(XrTime predicted_display_time)235 XrResult OpenXRInputHelper::SyncActions(XrTime predicted_display_time) {
236   std::vector<XrActiveActionSet> active_action_sets(controller_states_.size());
237 
238   for (size_t i = 0; i < controller_states_.size(); i++) {
239     active_action_sets[i].actionSet =
240         controller_states_[i].controller.action_set();
241     active_action_sets[i].subactionPath = XR_NULL_PATH;
242   }
243 
244   XrActionsSyncInfo sync_info = {XR_TYPE_ACTIONS_SYNC_INFO};
245   sync_info.countActiveActionSets = active_action_sets.size();
246   sync_info.activeActionSets = active_action_sets.data();
247   return xrSyncActions(session_, &sync_info);
248 }
249 
250 }  // namespace device
251