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