The open source OpenXR runtime
at main 492 lines 17 kB view raw
1// Copyright 2020-2021, N Madsen. 2// Copyright 2020-2023, Collabora, Ltd. 3// Copyright 2020-2023, Jan Schmidt 4// SPDX-License-Identifier: BSL-1.0 5/*! 6 * @file 7 * @brief Driver for WMR Controllers. 8 * @author Jan Schmidt <jan@centricular.com> 9 * @ingroup drv_wmr 10 */ 11#include "math/m_api.h" 12 13#include "util/u_device.h" 14#include "util/u_trace_marker.h" 15#include "util/u_var.h" 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20#include <assert.h> 21#include <errno.h> 22 23#include "wmr_common.h" 24#include "wmr_controller.h" 25 26#ifdef XRT_DOXYGEN 27#define WMR_PACKED 28#else 29#define WMR_PACKED __attribute__((packed)) 30#endif 31 32/*! 33 * Indices in input list of each input. 34 */ 35enum wmr_controller_og_input_index 36{ 37 WMR_CONTROLLER_INDEX_MENU_CLICK, 38 WMR_CONTROLLER_INDEX_HOME_CLICK, 39 WMR_CONTROLLER_INDEX_SQUEEZE_CLICK, 40 WMR_CONTROLLER_INDEX_TRIGGER_VALUE, 41 WMR_CONTROLLER_INDEX_THUMBSTICK_CLICK, 42 WMR_CONTROLLER_INDEX_THUMBSTICK, 43 WMR_CONTROLLER_INDEX_TRACKPAD_CLICK, 44 WMR_CONTROLLER_INDEX_TRACKPAD_TOUCH, 45 WMR_CONTROLLER_INDEX_TRACKPAD, 46 WMR_CONTROLLER_INDEX_GRIP_POSE, 47 WMR_CONTROLLER_INDEX_AIM_POSE, 48}; 49 50#define SET_WMR_INPUT(wcb, NAME) (wcb->base.inputs[WMR_CONTROLLER_INDEX_##NAME].name = XRT_INPUT_WMR_##NAME) 51#define SET_ODYSSEY_INPUT(wcb, NAME) \ 52 (wcb->base.inputs[WMR_CONTROLLER_INDEX_##NAME].name = XRT_INPUT_ODYSSEY_CONTROLLER_##NAME) 53 54/* 55 * 56 * Bindings 57 * 58 */ 59 60static struct xrt_binding_input_pair simple_inputs_og[4] = { 61 {XRT_INPUT_SIMPLE_SELECT_CLICK, XRT_INPUT_WMR_TRIGGER_VALUE}, 62 {XRT_INPUT_SIMPLE_MENU_CLICK, XRT_INPUT_WMR_MENU_CLICK}, 63 {XRT_INPUT_SIMPLE_GRIP_POSE, XRT_INPUT_WMR_GRIP_POSE}, 64 {XRT_INPUT_SIMPLE_AIM_POSE, XRT_INPUT_WMR_AIM_POSE}, 65}; 66 67static struct xrt_binding_output_pair simple_outputs_og[1] = { 68 {XRT_OUTPUT_NAME_SIMPLE_VIBRATION, XRT_OUTPUT_NAME_WMR_HAPTIC}, 69}; 70 71static struct xrt_binding_input_pair vive_inputs_og[10] = { 72 {XRT_INPUT_VIVE_SYSTEM_CLICK, XRT_INPUT_WMR_HOME_CLICK}, 73 {XRT_INPUT_VIVE_SQUEEZE_CLICK, XRT_INPUT_WMR_SQUEEZE_CLICK}, 74 {XRT_INPUT_VIVE_MENU_CLICK, XRT_INPUT_WMR_MENU_CLICK}, 75 {XRT_INPUT_VIVE_TRIGGER_CLICK, XRT_INPUT_WMR_TRIGGER_VALUE}, 76 {XRT_INPUT_VIVE_TRIGGER_VALUE, XRT_INPUT_WMR_TRIGGER_VALUE}, 77 {XRT_INPUT_VIVE_TRACKPAD, XRT_INPUT_WMR_TRACKPAD}, 78 {XRT_INPUT_VIVE_TRACKPAD_CLICK, XRT_INPUT_WMR_TRACKPAD_CLICK}, 79 {XRT_INPUT_VIVE_TRACKPAD_TOUCH, XRT_INPUT_WMR_TRACKPAD_TOUCH}, 80 {XRT_INPUT_VIVE_GRIP_POSE, XRT_INPUT_WMR_GRIP_POSE}, 81 {XRT_INPUT_VIVE_AIM_POSE, XRT_INPUT_WMR_AIM_POSE}, 82}; 83 84static struct xrt_binding_output_pair vive_outputs_og[1] = { 85 {XRT_OUTPUT_NAME_VIVE_HAPTIC, XRT_OUTPUT_NAME_WMR_HAPTIC}, 86}; 87 88static struct xrt_binding_profile binding_profiles_og[2] = { 89 { 90 .name = XRT_DEVICE_VIVE_WAND, 91 .inputs = vive_inputs_og, 92 .input_count = ARRAY_SIZE(vive_inputs_og), 93 .outputs = vive_outputs_og, 94 .output_count = ARRAY_SIZE(vive_outputs_og), 95 }, 96 { 97 .name = XRT_DEVICE_SIMPLE_CONTROLLER, 98 .inputs = simple_inputs_og, 99 .input_count = ARRAY_SIZE(simple_inputs_og), 100 .outputs = simple_outputs_og, 101 .output_count = ARRAY_SIZE(simple_outputs_og), 102 }, 103}; 104 105static struct xrt_binding_input_pair simple_inputs_odyssey[4] = { 106 {XRT_INPUT_SIMPLE_SELECT_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_TRIGGER_VALUE}, 107 {XRT_INPUT_SIMPLE_MENU_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_MENU_CLICK}, 108 {XRT_INPUT_SIMPLE_GRIP_POSE, XRT_INPUT_ODYSSEY_CONTROLLER_GRIP_POSE}, 109 {XRT_INPUT_SIMPLE_AIM_POSE, XRT_INPUT_ODYSSEY_CONTROLLER_AIM_POSE}, 110}; 111 112static struct xrt_binding_output_pair simple_outputs_odyssey[1] = { 113 {XRT_OUTPUT_NAME_SIMPLE_VIBRATION, XRT_OUTPUT_NAME_ODYSSEY_CONTROLLER_HAPTIC}, 114}; 115 116static struct xrt_binding_input_pair wmr_inputs_odyssey[11] = { 117 {XRT_INPUT_WMR_MENU_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_MENU_CLICK}, 118 {XRT_INPUT_WMR_SQUEEZE_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_SQUEEZE_CLICK}, 119 {XRT_INPUT_WMR_TRIGGER_VALUE, XRT_INPUT_ODYSSEY_CONTROLLER_TRIGGER_VALUE}, 120 {XRT_INPUT_WMR_THUMBSTICK_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_THUMBSTICK_CLICK}, 121 {XRT_INPUT_WMR_THUMBSTICK, XRT_INPUT_ODYSSEY_CONTROLLER_THUMBSTICK}, 122 {XRT_INPUT_WMR_TRACKPAD_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_TRACKPAD_CLICK}, 123 {XRT_INPUT_WMR_TRACKPAD_TOUCH, XRT_INPUT_ODYSSEY_CONTROLLER_TRACKPAD_TOUCH}, 124 {XRT_INPUT_WMR_TRACKPAD, XRT_INPUT_ODYSSEY_CONTROLLER_TRACKPAD}, 125 {XRT_INPUT_WMR_GRIP_POSE, XRT_INPUT_ODYSSEY_CONTROLLER_GRIP_POSE}, 126 {XRT_INPUT_WMR_AIM_POSE, XRT_INPUT_ODYSSEY_CONTROLLER_AIM_POSE}, 127 {XRT_INPUT_WMR_HOME_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_HOME_CLICK}, 128}; 129 130static struct xrt_binding_output_pair wmr_outputs_odyssey[1] = { 131 {XRT_OUTPUT_NAME_WMR_HAPTIC, XRT_OUTPUT_NAME_ODYSSEY_CONTROLLER_HAPTIC}, 132}; 133 134static struct xrt_binding_input_pair vive_inputs_odyssey[10] = { 135 {XRT_INPUT_VIVE_SYSTEM_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_HOME_CLICK}, 136 {XRT_INPUT_VIVE_SQUEEZE_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_SQUEEZE_CLICK}, 137 {XRT_INPUT_VIVE_MENU_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_MENU_CLICK}, 138 {XRT_INPUT_VIVE_TRIGGER_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_TRIGGER_VALUE}, 139 {XRT_INPUT_VIVE_TRIGGER_VALUE, XRT_INPUT_ODYSSEY_CONTROLLER_TRIGGER_VALUE}, 140 {XRT_INPUT_VIVE_TRACKPAD, XRT_INPUT_ODYSSEY_CONTROLLER_TRACKPAD}, 141 {XRT_INPUT_VIVE_TRACKPAD_CLICK, XRT_INPUT_ODYSSEY_CONTROLLER_TRACKPAD_CLICK}, 142 {XRT_INPUT_VIVE_TRACKPAD_TOUCH, XRT_INPUT_ODYSSEY_CONTROLLER_TRACKPAD_TOUCH}, 143 {XRT_INPUT_VIVE_GRIP_POSE, XRT_INPUT_ODYSSEY_CONTROLLER_GRIP_POSE}, 144 {XRT_INPUT_VIVE_AIM_POSE, XRT_INPUT_ODYSSEY_CONTROLLER_AIM_POSE}, 145}; 146 147static struct xrt_binding_output_pair vive_outputs_odyssey[1] = { 148 {XRT_OUTPUT_NAME_VIVE_HAPTIC, XRT_OUTPUT_NAME_ODYSSEY_CONTROLLER_HAPTIC}, 149}; 150 151static struct xrt_binding_profile binding_profiles_odyssey[3] = { 152 { 153 .name = XRT_DEVICE_WMR_CONTROLLER, 154 .inputs = wmr_inputs_odyssey, 155 .input_count = ARRAY_SIZE(wmr_inputs_odyssey), 156 .outputs = wmr_outputs_odyssey, 157 .output_count = ARRAY_SIZE(wmr_outputs_odyssey), 158 }, 159 { 160 .name = XRT_DEVICE_VIVE_WAND, 161 .inputs = vive_inputs_odyssey, 162 .input_count = ARRAY_SIZE(vive_inputs_odyssey), 163 .outputs = vive_outputs_odyssey, 164 .output_count = ARRAY_SIZE(vive_outputs_odyssey), 165 }, 166 { 167 .name = XRT_DEVICE_SIMPLE_CONTROLLER, 168 .inputs = simple_inputs_odyssey, 169 .input_count = ARRAY_SIZE(simple_inputs_odyssey), 170 .outputs = simple_outputs_odyssey, 171 .output_count = ARRAY_SIZE(simple_outputs_odyssey), 172 }, 173}; 174 175 176/* OG WMR Controller inputs struct */ 177struct wmr_controller_og_input 178{ 179 // buttons clicked 180 bool menu; 181 bool home; 182 bool bt_pairing; 183 bool squeeze; // Actually a "squeeze" click 184 185 float trigger; 186 187 struct 188 { 189 bool click; 190 struct xrt_vec2 values; 191 } thumbstick; 192 struct 193 { 194 bool click; 195 bool touch; 196 struct xrt_vec2 values; 197 } trackpad; 198 199 uint8_t battery; 200 201 struct 202 { 203 uint64_t timestamp_ticks; 204 struct xrt_vec3 acc; 205 struct xrt_vec3 gyro; 206 int32_t temperature; 207 } imu; 208}; 209#undef WMR_PACKED 210 211/* OG WMR Controller device struct */ 212struct wmr_controller_og 213{ 214 struct wmr_controller_base base; 215 216 //! The last decoded package of IMU and button data 217 struct wmr_controller_og_input last_inputs; 218}; 219 220/* 221 * 222 * WMR Motion Controller protocol helpers 223 * 224 */ 225 226static inline void 227vec3_from_wmr_controller_accel(const int32_t sample[3], struct xrt_vec3 *out_vec) 228{ 229 // Reverb G1 observation: 1g is approximately 490,000. 230 231 out_vec->x = (float)sample[0] / (98000 / 2); 232 out_vec->y = (float)sample[1] / (98000 / 2); 233 out_vec->z = (float)sample[2] / (98000 / 2); 234} 235 236 237static inline void 238vec3_from_wmr_controller_gyro(const int32_t sample[3], struct xrt_vec3 *out_vec) 239{ 240 out_vec->x = (float)sample[0] * 0.00001f; 241 out_vec->y = (float)sample[1] * 0.00001f; 242 out_vec->z = (float)sample[2] * 0.00001f; 243} 244 245static bool 246wmr_controller_og_packet_parse(struct wmr_controller_og *ctrl, const unsigned char *buffer, size_t len) 247{ 248 struct wmr_controller_og_input *last_input = &ctrl->last_inputs; 249 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(ctrl); 250 251 if (len != 44) { 252 U_LOG_IFL_E(wcb->log_level, "WMR Controller: unexpected message length: %zd", len); 253 return false; 254 } 255 256 const unsigned char *p = buffer; 257 258 // Read buttons 259 uint8_t buttons = read8(&p); 260 last_input->thumbstick.click = buttons & 0x01; 261 last_input->home = buttons & 0x02; 262 last_input->menu = buttons & 0x04; 263 last_input->squeeze = buttons & 0x08; // squeeze-click 264 last_input->trackpad.click = buttons & 0x10; 265 last_input->bt_pairing = buttons & 0x20; 266 last_input->trackpad.touch = buttons & 0x40; 267 268 269 // Read thumbstick coordinates (12 bit resolution) 270 int16_t stick_x = read8(&p); 271 uint8_t nibbles = read8(&p); 272 stick_x += ((nibbles & 0x0F) << 8); 273 int16_t stick_y = (nibbles >> 4); 274 stick_y += (read8(&p) << 4); 275 276 last_input->thumbstick.values.x = (float)(stick_x - 0x07FF) / 0x07FF; 277 if (last_input->thumbstick.values.x > 1.0f) { 278 last_input->thumbstick.values.x = 1.0f; 279 } 280 281 last_input->thumbstick.values.y = (float)(stick_y - 0x07FF) / 0x07FF; 282 if (last_input->thumbstick.values.y > 1.0f) { 283 last_input->thumbstick.values.y = 1.0f; 284 } 285 286 // Read trigger value (0x00 - 0xFF) 287 last_input->trigger = (float)read8(&p) / 0xFF; 288 289 // Read trackpad coordinates (0x00 - 0x64. Both are 0xFF when untouched) 290 uint8_t trackpad_x = read8(&p); 291 uint8_t trackpad_y = read8(&p); 292 last_input->trackpad.values.x = (trackpad_x == 0xFF) ? 0.0f : (float)(trackpad_x - 0x32) / 0x32; 293 last_input->trackpad.values.y = (trackpad_y == 0xFF) ? 0.0f : (float)(trackpad_y - 0x32) / 0x32; 294 295 last_input->battery = read8(&p); 296 297 int32_t acc[3]; 298 acc[0] = read24(&p); // x 299 acc[1] = read24(&p); // y 300 acc[2] = read24(&p); // z 301 vec3_from_wmr_controller_accel(acc, &last_input->imu.acc); 302 math_matrix_3x3_transform_vec3(&wcb->config.sensors.accel.mix_matrix, &last_input->imu.acc, 303 &last_input->imu.acc); 304 math_vec3_accum(&wcb->config.sensors.accel.bias_offsets, &last_input->imu.acc); 305 math_quat_rotate_vec3(&wcb->config.sensors.transforms.P_oxr_acc.orientation, &last_input->imu.acc, 306 &last_input->imu.acc); 307 308 U_LOG_IFL_T(wcb->log_level, "Accel [m/s^2] : %f", 309 sqrtf(last_input->imu.acc.x * last_input->imu.acc.x + 310 last_input->imu.acc.y * last_input->imu.acc.y + 311 last_input->imu.acc.z * last_input->imu.acc.z)); 312 313 314 last_input->imu.temperature = read16(&p); 315 316 int32_t gyro[3]; 317 gyro[0] = read24(&p); 318 gyro[1] = read24(&p); 319 gyro[2] = read24(&p); 320 vec3_from_wmr_controller_gyro(gyro, &last_input->imu.gyro); 321 math_matrix_3x3_transform_vec3(&wcb->config.sensors.gyro.mix_matrix, &last_input->imu.gyro, 322 &last_input->imu.gyro); 323 math_vec3_accum(&wcb->config.sensors.gyro.bias_offsets, &last_input->imu.gyro); 324 math_quat_rotate_vec3(&wcb->config.sensors.transforms.P_oxr_gyr.orientation, &last_input->imu.gyro, 325 &last_input->imu.gyro); 326 327 328 uint32_t prev_ticks = last_input->imu.timestamp_ticks & UINT32_C(0xFFFFFFFF); 329 330 // Write the new ticks value into the lower half of timestamp_ticks 331 last_input->imu.timestamp_ticks &= (UINT64_C(0xFFFFFFFF) << 32u); 332 last_input->imu.timestamp_ticks += (uint32_t)read32(&p); 333 334 if ((last_input->imu.timestamp_ticks & UINT64_C(0xFFFFFFFF)) < prev_ticks) { 335 // Timer overflow, so increment the upper half of timestamp_ticks 336 last_input->imu.timestamp_ticks += (UINT64_C(0x1) << 32u); 337 } 338 339 /* Todo: More decoding here 340 read16(&p); // Unknown. Seems to depend on controller orientation (probably mag) 341 read32(&p); // Unknown. 342 read16(&p); // Unknown. Device state, etc. 343 read16(&p); 344 read16(&p); 345 */ 346 347 return true; 348} 349 350static bool 351handle_input_packet(struct wmr_controller_base *wcb, uint64_t time_ns, uint8_t *buffer, uint32_t buf_size) 352{ 353 struct wmr_controller_og *ctrl = (struct wmr_controller_og *)(wcb); 354 bool b = wmr_controller_og_packet_parse(ctrl, buffer, buf_size); 355 if (b) { 356 m_imu_3dof_update(&wcb->fusion, 357 ctrl->last_inputs.imu.timestamp_ticks * WMR_MOTION_CONTROLLER_NS_PER_TICK, 358 &ctrl->last_inputs.imu.acc, &ctrl->last_inputs.imu.gyro); 359 360 wcb->last_imu_timestamp_ns = time_ns; 361 wcb->last_angular_velocity = ctrl->last_inputs.imu.gyro; 362 } 363 364 return b; 365} 366 367static xrt_result_t 368wmr_controller_og_update_inputs(struct xrt_device *xdev) 369{ 370 DRV_TRACE_MARKER(); 371 372 struct wmr_controller_og *ctrl = (struct wmr_controller_og *)(xdev); 373 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(xdev); 374 375 os_mutex_lock(&wcb->data_lock); 376 377 struct xrt_input *xrt_inputs = xdev->inputs; 378 struct wmr_controller_og_input *cur_inputs = &ctrl->last_inputs; 379 380 xrt_inputs[WMR_CONTROLLER_INDEX_MENU_CLICK].value.boolean = cur_inputs->menu; 381 xrt_inputs[WMR_CONTROLLER_INDEX_HOME_CLICK].value.boolean = cur_inputs->home; 382 xrt_inputs[WMR_CONTROLLER_INDEX_SQUEEZE_CLICK].value.boolean = cur_inputs->squeeze; 383 xrt_inputs[WMR_CONTROLLER_INDEX_TRIGGER_VALUE].value.vec1.x = cur_inputs->trigger; 384 xrt_inputs[WMR_CONTROLLER_INDEX_THUMBSTICK_CLICK].value.boolean = cur_inputs->thumbstick.click; 385 xrt_inputs[WMR_CONTROLLER_INDEX_THUMBSTICK].value.vec2 = cur_inputs->thumbstick.values; 386 xrt_inputs[WMR_CONTROLLER_INDEX_TRACKPAD_CLICK].value.boolean = cur_inputs->trackpad.click; 387 xrt_inputs[WMR_CONTROLLER_INDEX_TRACKPAD_TOUCH].value.boolean = cur_inputs->trackpad.touch; 388 xrt_inputs[WMR_CONTROLLER_INDEX_TRACKPAD].value.vec2 = cur_inputs->trackpad.values; 389 390 os_mutex_unlock(&wcb->data_lock); 391 392 return XRT_SUCCESS; 393} 394 395static void 396wmr_controller_og_destroy(struct xrt_device *xdev) 397{ 398 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(xdev); 399 400 wmr_controller_base_deinit(wcb); 401 free(wcb); 402} 403 404struct wmr_controller_base * 405wmr_controller_og_create(struct wmr_controller_connection *conn, 406 enum xrt_device_type controller_type, 407 uint16_t pid, 408 enum u_logging_level log_level) 409{ 410 DRV_TRACE_MARKER(); 411 412 enum u_device_alloc_flags flags = U_DEVICE_ALLOC_TRACKING_NONE; 413 struct wmr_controller_og *ctrl = U_DEVICE_ALLOCATE(struct wmr_controller_og, flags, 11, 1); 414 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(ctrl); 415 416 if (!wmr_controller_base_init(wcb, conn, controller_type, log_level, wmr_controller_og_destroy)) { 417 wmr_controller_og_destroy(&wcb->base); 418 return NULL; 419 } 420 421 wcb->handle_input_packet = handle_input_packet; 422 423 // Only set those we want to overwrite. 424 wcb->base.update_inputs = wmr_controller_og_update_inputs; 425 426 if (pid == ODYSSEY_CONTROLLER_PID) { 427 wcb->base.name = XRT_DEVICE_SAMSUNG_ODYSSEY_CONTROLLER; 428 } else { 429 wcb->base.name = XRT_DEVICE_WMR_CONTROLLER; 430 } 431 432 if (pid == ODYSSEY_CONTROLLER_PID) { 433 SET_ODYSSEY_INPUT(wcb, MENU_CLICK); 434 SET_ODYSSEY_INPUT(wcb, HOME_CLICK); 435 SET_ODYSSEY_INPUT(wcb, SQUEEZE_CLICK); 436 SET_ODYSSEY_INPUT(wcb, TRIGGER_VALUE); 437 SET_ODYSSEY_INPUT(wcb, THUMBSTICK_CLICK); 438 SET_ODYSSEY_INPUT(wcb, THUMBSTICK); 439 SET_ODYSSEY_INPUT(wcb, TRACKPAD_CLICK); 440 SET_ODYSSEY_INPUT(wcb, TRACKPAD_TOUCH); 441 SET_ODYSSEY_INPUT(wcb, TRACKPAD); 442 SET_ODYSSEY_INPUT(wcb, GRIP_POSE); 443 SET_ODYSSEY_INPUT(wcb, AIM_POSE); 444 445 wcb->base.outputs[0].name = XRT_OUTPUT_NAME_ODYSSEY_CONTROLLER_HAPTIC; 446 447 wcb->base.binding_profiles = binding_profiles_odyssey; 448 wcb->base.binding_profile_count = ARRAY_SIZE(binding_profiles_odyssey); 449 } else { 450 SET_WMR_INPUT(wcb, MENU_CLICK); 451 SET_WMR_INPUT(wcb, HOME_CLICK); 452 SET_WMR_INPUT(wcb, SQUEEZE_CLICK); 453 SET_WMR_INPUT(wcb, TRIGGER_VALUE); 454 SET_WMR_INPUT(wcb, THUMBSTICK_CLICK); 455 SET_WMR_INPUT(wcb, THUMBSTICK); 456 SET_WMR_INPUT(wcb, TRACKPAD_CLICK); 457 SET_WMR_INPUT(wcb, TRACKPAD_TOUCH); 458 SET_WMR_INPUT(wcb, TRACKPAD); 459 SET_WMR_INPUT(wcb, GRIP_POSE); 460 SET_WMR_INPUT(wcb, AIM_POSE); 461 462 wcb->base.outputs[0].name = XRT_OUTPUT_NAME_WMR_HAPTIC; 463 464 wcb->base.binding_profiles = binding_profiles_og; 465 wcb->base.binding_profile_count = ARRAY_SIZE(binding_profiles_og); 466 } 467 468 for (uint32_t i = 0; i < wcb->base.input_count; i++) { 469 wcb->base.inputs[0].active = true; 470 } 471 472 ctrl->last_inputs.imu.timestamp_ticks = 0; 473 474 u_var_add_bool(wcb, &ctrl->last_inputs.menu, "input.menu"); 475 u_var_add_bool(wcb, &ctrl->last_inputs.home, "input.home"); 476 u_var_add_bool(wcb, &ctrl->last_inputs.bt_pairing, "input.bt_pairing"); 477 u_var_add_bool(wcb, &ctrl->last_inputs.squeeze, "input.squeeze"); 478 u_var_add_f32(wcb, &ctrl->last_inputs.trigger, "input.trigger"); 479 u_var_add_u8(wcb, &ctrl->last_inputs.battery, "input.battery"); 480 u_var_add_bool(wcb, &ctrl->last_inputs.thumbstick.click, "input.thumbstick.click"); 481 u_var_add_f32(wcb, &ctrl->last_inputs.thumbstick.values.x, "input.thumbstick.values.y"); 482 u_var_add_f32(wcb, &ctrl->last_inputs.thumbstick.values.y, "input.thumbstick.values.x"); 483 u_var_add_bool(wcb, &ctrl->last_inputs.trackpad.click, "input.trackpad.click"); 484 u_var_add_bool(wcb, &ctrl->last_inputs.trackpad.touch, "input.trackpad.touch"); 485 u_var_add_f32(wcb, &ctrl->last_inputs.trackpad.values.x, "input.trackpad.values.x"); 486 u_var_add_f32(wcb, &ctrl->last_inputs.trackpad.values.y, "input.trackpad.values.y"); 487 u_var_add_ro_vec3_f32(wcb, &ctrl->last_inputs.imu.acc, "imu.acc"); 488 u_var_add_ro_vec3_f32(wcb, &ctrl->last_inputs.imu.gyro, "imu.gyro"); 489 u_var_add_i32(wcb, &ctrl->last_inputs.imu.temperature, "imu.temperature"); 490 491 return wcb; 492}