The open source OpenXR runtime

steamvr: Implement basic SteamVR driver

v3:
targets: Add Monado-SteamVR driver target
st/ovrd: Add OpenVR driver header
build: Factor out sdl hack into lib_sdl2_hack and update steamvr build
build: Revert lib_sdl2_refactor
steamvr: Emulate Index Controller by default
steamvr: Use oxr_handle_destroy instead of exposing oxr_instance_destroy
steamvr: don't use oxr internals
steamvr: communicate 3dof tracking to steamvr
steamvr: use util functions for device assignment and tracking origin setup
steamvr: Install plugin to <prefix>/share/steamvr-monado
steamvr: Use thread for updating poses every 1ms

Makes a big difference for the Index @144Hz on the vive driver.
Still somewhat choppy on survive driver - prediction should solve it.

Main-author: Christoph Haag <christoph.haag@collabora.com>
Co-author: Jakob Bornecrantz <jakob@collabora.com>

authored by

Christoph Haag and committed by
Jakob Bornecrantz
cb62514f b6c34753

+2002 -1
+3
CMakeLists.txt
··· 123 cmake_dependent_option(XRT_HAVE_SYSTEMD "Enable systemd support (for socket activation of service)" ON "Systemd_FOUND AND XRT_FEATURE_SERVICE" OFF) 124 cmake_dependent_option(XRT_INSTALL_SYSTEMD_UNIT_FILES "Install user unit files for systemd socket activation on installation" ON "XRT_HAVE_SYSTEMD" OFF) 125 cmake_dependent_option(XRT_INSTALL_ABSOLUTE_SYSTEMD_UNIT_FILES "Use an absolute path to monado-system in installed user unit files for systemd socket activation" ON "XRT_INSTALL_SYSTEMD_UNIT_FILES" OFF) 126 if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_DEPTH) 127 set(XRT_FEATURE_OPENXR_LAYER_DEPTH ON) 128 endif() ··· 283 message(STATUS "# FEATURE_OPENXR_LAYER_CYLINDER: ${XRT_FEATURE_OPENXR_LAYER_CYLINDER}") 284 message(STATUS "# FEATURE_OPENXR_LAYER_EQUIRECT: ${XRT_FEATURE_OPENXR_LAYER_EQUIRECT}") 285 message(STATUS "# FEATURE_OPENXR_LAYER_EQUIRECT_LEGACY: ${XRT_FEATURE_OPENXR_LAYER_EQUIRECT_LEGACY}") 286 message(STATUS "#") 287 message(STATUS "# DRIVER_ANDROID: ${XRT_BUILD_DRIVER_ANDROID}") 288 message(STATUS "# DRIVER_ARDUINO: ${XRT_BUILD_DRIVER_ARDUINO}")
··· 123 cmake_dependent_option(XRT_HAVE_SYSTEMD "Enable systemd support (for socket activation of service)" ON "Systemd_FOUND AND XRT_FEATURE_SERVICE" OFF) 124 cmake_dependent_option(XRT_INSTALL_SYSTEMD_UNIT_FILES "Install user unit files for systemd socket activation on installation" ON "XRT_HAVE_SYSTEMD" OFF) 125 cmake_dependent_option(XRT_INSTALL_ABSOLUTE_SYSTEMD_UNIT_FILES "Use an absolute path to monado-system in installed user unit files for systemd socket activation" ON "XRT_INSTALL_SYSTEMD_UNIT_FILES" OFF) 126 + option(XRT_FEATURE_STEAMVR_PLUGIN "Build SteamVR plugin" ON) 127 + 128 if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_DEPTH) 129 set(XRT_FEATURE_OPENXR_LAYER_DEPTH ON) 130 endif() ··· 285 message(STATUS "# FEATURE_OPENXR_LAYER_CYLINDER: ${XRT_FEATURE_OPENXR_LAYER_CYLINDER}") 286 message(STATUS "# FEATURE_OPENXR_LAYER_EQUIRECT: ${XRT_FEATURE_OPENXR_LAYER_EQUIRECT}") 287 message(STATUS "# FEATURE_OPENXR_LAYER_EQUIRECT_LEGACY: ${XRT_FEATURE_OPENXR_LAYER_EQUIRECT_LEGACY}") 288 + message(STATUS "# FEATURE_STEAMVR_PLUGIN: ${XRT_FEATURE_STEAMVR_PLUGIN}") 289 message(STATUS "#") 290 message(STATUS "# DRIVER_ANDROID: ${XRT_BUILD_DRIVER_ANDROID}") 291 message(STATUS "# DRIVER_ARDUINO: ${XRT_BUILD_DRIVER_ARDUINO}")
+6
meson.build
··· 245 else 246 message(' tracking: no') 247 endif
··· 245 else 246 message(' tracking: no') 247 endif 248 + 249 + if get_option('steamvr_plugin') 250 + message('steamvr plugin: yes') 251 + else 252 + message('steamvr plugin: no') 253 + endif
+7
meson_options.txt
··· 107 value: true, 108 description: 'Enable support for Equirect2 Layers' 109 )
··· 107 value: true, 108 description: 'Enable support for Equirect2 Layers' 109 ) 110 + 111 + option('steamvr_plugin', 112 + type: 'boolean', 113 + value: true, 114 + description: 'Enable SteamVR Plugin' 115 + ) 116 +
+1 -1
src/external/meson.build
··· 1 # Copyright 2019-2020, Collabora, Ltd. 2 # SPDX-License-Identifier: BSL-1.0 3 4 - openvr_include = include_directories('openvr_includes') 5 cjson_include = include_directories('cjson') 6 flexkalman_include = include_directories('flexkalman') 7 glad_include = include_directories('glad/include')
··· 1 # Copyright 2019-2020, Collabora, Ltd. 2 # SPDX-License-Identifier: BSL-1.0 3 4 + openvr_include = include_directories('openvr_includes', is_system: true) 5 cjson_include = include_directories('cjson') 6 flexkalman_include = include_directories('flexkalman') 7 glad_include = include_directories('glad/include')
+2
src/xrt/include/meson.build
··· 1 # Copyright 2020, Collabora, Ltd. 2 # SPDX-License-Identifier: BSL-1.0 3 4 subdir('xrt')
··· 1 # Copyright 2020, Collabora, Ltd. 2 # SPDX-License-Identifier: BSL-1.0 3 4 + xrt_include = include_directories('.') 5 + 6 subdir('xrt')
+4
src/xrt/state_trackers/CMakeLists.txt
··· 6 endif() 7 add_subdirectory(oxr) 8 add_subdirectory(prober)
··· 6 endif() 7 add_subdirectory(oxr) 8 add_subdirectory(prober) 9 + 10 + if(XRT_FEATURE_STEAMVR_PLUGIN) 11 + add_subdirectory(steamvr_drv) 12 + endif()
+4
src/xrt/state_trackers/meson.build
··· 6 subdir('gui') 7 subdir('oxr') 8 subdir('prober')
··· 6 subdir('gui') 7 subdir('oxr') 8 subdir('prober') 9 + 10 + if get_option('steamvr_plugin') 11 + subdir('steamvr_drv') 12 + endif
+19
src/xrt/state_trackers/steamvr_drv/CMakeLists.txt
···
··· 1 + # Copyright 2020, Collabora, Ltd. 2 + # SPDX-License-Identifier: BSL-1.0 3 + 4 + set(OVRD_SOURCE_FILES 5 + ovrd_driver.cpp 6 + ovrd_interface.h 7 + ) 8 + 9 + add_library(st_ovrd STATIC 10 + ${OVRD_SOURCE_FILES} 11 + ) 12 + target_include_directories(st_ovrd INTERFACE 13 + ${CMAKE_CURRENT_SOURCE_DIR} 14 + ) 15 + target_link_libraries(st_ovrd PRIVATE 16 + xrt-interfaces 17 + xrt-external-openvr 18 + aux_math 19 + )
+20
src/xrt/state_trackers/steamvr_drv/meson.build
···
··· 1 + # Copyright 2020, Collabora, Ltd. 2 + # SPDX-License-Identifier: BSL-1.0 3 + 4 + st_ovrd_include = include_directories('.') 5 + 6 + lib_st_ovrd = static_library( 7 + 'st_ovrd', 8 + files( 9 + 'ovrd_driver.cpp', 10 + 'ovrd_interface.h' 11 + ), 12 + include_directories: [ 13 + openvr_include, 14 + st_include, # Sigh debian meson requires this. 15 + xrt_include, 16 + ], 17 + dependencies: aux_util, 18 + c_args: compile_args, 19 + cpp_args: compile_args, 20 + )
+1558
src/xrt/state_trackers/steamvr_drv/ovrd_driver.cpp
···
··· 1 + // Copyright 2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Main driver code for @ref st_ovrd. 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @author Christoph Haag <christoph.haag@collabora.com> 8 + * @ingroup st_ovrd 9 + */ 10 + 11 + #include <cstring> 12 + #include <thread> 13 + 14 + #include "ovrd_log.hpp" 15 + #include "openvr_driver.h" 16 + 17 + extern "C" { 18 + #include "ovrd_interface.h" 19 + 20 + #include <math.h> 21 + 22 + #include <math/m_space.h> 23 + #include "os/os_time.h" 24 + #include "util/u_debug.h" 25 + #include "util/u_device.h" 26 + 27 + #include "xrt/xrt_defines.h" 28 + #include "xrt/xrt_device.h" 29 + #include "xrt/xrt_instance.h" 30 + } 31 + 32 + //! When set, all controllers pretend to be Index controllers. Provides best 33 + //! compatibility with legacy games due to steamvr's legacy binding for Index 34 + //! controllers, but input mapping may be incomplete or not ideal. 35 + DEBUG_GET_ONCE_BOOL_OPTION(emulate_index_controller, 36 + "STEAMVR_EMULATE_INDEX_CONTROLLER", 37 + true) 38 + 39 + DEBUG_GET_ONCE_NUM_OPTION(scale_percentage, 40 + "XRT_COMPOSITOR_SCALE_PERCENTAGE", 41 + 140) 42 + 43 + //#define DUMP_POSE 44 + //#define DUMP_POSE_CONTROLLERS 45 + 46 + 47 + /* 48 + * Controller 49 + */ 50 + 51 + struct MonadoInputComponent 52 + { 53 + bool has_component; 54 + bool x; 55 + bool y; 56 + }; 57 + 58 + struct SteamVRDriverControl 59 + { 60 + const char *steamvr_control_path; 61 + vr::VRInputComponentHandle_t control_handle; 62 + enum xrt_input_type monado_input_type; 63 + enum xrt_input_name monado_input_name; 64 + struct MonadoInputComponent component; 65 + }; 66 + 67 + static void 68 + copy_vec3(struct xrt_vec3 *from, double *to) 69 + { 70 + to[0] = from->x; 71 + to[1] = from->y; 72 + to[2] = from->z; 73 + } 74 + 75 + static void 76 + copy_quat(struct xrt_quat *from, vr::HmdQuaternion_t *to) 77 + { 78 + to->x = from->x; 79 + to->y = from->y; 80 + to->z = from->z; 81 + to->w = from->w; 82 + } 83 + 84 + static void 85 + apply_pose(struct xrt_space_relation *rel, vr::DriverPose_t *m_pose) 86 + { 87 + if ((rel->relation_flags & 88 + XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT) != 0) { 89 + copy_quat(&rel->pose.orientation, &m_pose->qRotation); 90 + } else { 91 + m_pose->result = vr::TrackingResult_Running_OutOfRange; 92 + m_pose->poseIsValid = false; 93 + } 94 + 95 + if ((rel->relation_flags & XRT_SPACE_RELATION_POSITION_TRACKED_BIT) != 96 + 0) { 97 + copy_vec3(&rel->pose.position, m_pose->vecPosition); 98 + } else { 99 + } 100 + 101 + if ((rel->relation_flags & 102 + XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT) != 0) { 103 + // linear velocity in world space 104 + copy_vec3(&rel->linear_velocity, m_pose->vecVelocity); 105 + } 106 + 107 + if ((rel->relation_flags & 108 + XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT) != 0) { 109 + // angular velocity reported by monado in world space, 110 + // expected by steamvr to be in "controller space" 111 + struct xrt_quat orientation_inv; 112 + math_quat_invert(&rel->pose.orientation, &orientation_inv); 113 + 114 + struct xrt_vec3 vel; 115 + math_quat_rotate_derivative(&orientation_inv, 116 + &rel->angular_velocity, &vel); 117 + 118 + copy_vec3(&vel, m_pose->vecAngularVelocity); 119 + } 120 + } 121 + 122 + class CDeviceDriver_Monado_Controller : public vr::ITrackedDeviceServerDriver 123 + { 124 + public: 125 + CDeviceDriver_Monado_Controller(struct xrt_instance *xinst, 126 + struct xrt_device *xdev, 127 + enum xrt_hand hand) 128 + : m_xdev(xdev), m_hand(hand) 129 + { 130 + ovrd_log("Creating Controller %s\n", xdev->str); 131 + 132 + m_handed_controller = true; 133 + 134 + m_emulate_index_controller = 135 + debug_get_bool_option_emulate_index_controller(); 136 + 137 + if (m_emulate_index_controller) { 138 + ovrd_log("Emulating Index Controller\n"); 139 + } else { 140 + ovrd_log("Using Monado Controller profile\n"); 141 + } 142 + 143 + m_unObjectId = vr::k_unTrackedDeviceIndexInvalid; 144 + m_pose = {}; 145 + 146 + strncpy(m_sSerialNumber, xdev->str, XRT_DEVICE_NAME_LEN); 147 + strncpy(m_sModelNumber, xdev->str, XRT_DEVICE_NAME_LEN); 148 + 149 + 150 + switch (this->m_xdev->name) { 151 + case XRT_DEVICE_INDEX_CONTROLLER: 152 + if (hand == XRT_HAND_LEFT) { 153 + m_render_model = 154 + "{indexcontroller}valve_controller_knu_1_0_" 155 + "left"; 156 + } 157 + if (hand == XRT_HAND_RIGHT) { 158 + m_render_model = 159 + "{indexcontroller}valve_controller_knu_1_0_" 160 + "right"; 161 + } 162 + break; 163 + case XRT_DEVICE_VIVE_WAND: 164 + m_render_model = "vr_controller_vive_1_5"; 165 + break; 166 + case XRT_DEVICE_VIVE_TRACKER_GEN1: 167 + case XRT_DEVICE_VIVE_TRACKER_GEN2: 168 + m_render_model = "{htc}vr_tracker_vive_1_0"; 169 + break; 170 + case XRT_DEVICE_PSMV: 171 + case XRT_DEVICE_HYDRA: 172 + case XRT_DEVICE_DAYDREAM: 173 + case XRT_DEVICE_GENERIC_HMD: 174 + default: m_render_model = "locator_one_sided"; break; 175 + } 176 + 177 + ovrd_log("Render model based on Monado: %s\n", m_render_model); 178 + } 179 + virtual ~CDeviceDriver_Monado_Controller() {} 180 + 181 + void 182 + AddControl(enum xrt_input_type monado_input_type, 183 + const char *steamvr_control_path, 184 + enum xrt_input_name monado_input_name, 185 + struct MonadoInputComponent *component) 186 + { 187 + m_controls[m_control_count].monado_input_type = 188 + monado_input_type; 189 + m_controls[m_control_count].steamvr_control_path = 190 + steamvr_control_path; 191 + m_controls[m_control_count].monado_input_name = 192 + monado_input_name; 193 + if (component != NULL) { 194 + m_controls[m_control_count].component = *component; 195 + } else { 196 + m_controls[m_control_count].component.has_component = 197 + false; 198 + } 199 + 200 + if (monado_input_type == XRT_INPUT_TYPE_BOOLEAN) { 201 + vr::VRDriverInput()->CreateBooleanComponent( 202 + m_ulPropertyContainer, steamvr_control_path, 203 + &m_controls[m_control_count].control_handle); 204 + 205 + } else if (monado_input_type == 206 + XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE) { 207 + vr::VRDriverInput()->CreateScalarComponent( 208 + m_ulPropertyContainer, steamvr_control_path, 209 + &m_controls[m_control_count].control_handle, 210 + vr::VRScalarType_Absolute, 211 + vr::VRScalarUnits_NormalizedTwoSided); 212 + 213 + } else if (monado_input_type == 214 + XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE) { 215 + vr::VRDriverInput()->CreateScalarComponent( 216 + m_ulPropertyContainer, steamvr_control_path, 217 + &m_controls[m_control_count].control_handle, 218 + vr::VRScalarType_Absolute, 219 + vr::VRScalarUnits_NormalizedOneSided); 220 + 221 + } else if (monado_input_type == 222 + XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE) { 223 + vr::VRDriverInput()->CreateScalarComponent( 224 + m_ulPropertyContainer, steamvr_control_path, 225 + &m_controls[m_control_count].control_handle, 226 + vr::VRScalarType_Absolute, 227 + vr::VRScalarUnits_NormalizedTwoSided); 228 + } 229 + 230 + ovrd_log("Added input %s\n", steamvr_control_path); 231 + m_control_count++; 232 + } 233 + 234 + void 235 + AddEmulatedIndexControls() 236 + { 237 + switch (this->m_xdev->name) { 238 + case XRT_DEVICE_INDEX_CONTROLLER: { 239 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 240 + "/input/trigger/value", 241 + XRT_INPUT_INDEX_TRIGGER_VALUE, NULL); 242 + 243 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 244 + "/input/trigger/click", 245 + XRT_INPUT_INDEX_TRIGGER_CLICK, NULL); 246 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 247 + "/input/trigger/touch", 248 + XRT_INPUT_INDEX_TRIGGER_TOUCH, NULL); 249 + 250 + 251 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 252 + "/input/system/click", 253 + XRT_INPUT_INDEX_SYSTEM_CLICK, NULL); 254 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 255 + "/input/system/touch", 256 + XRT_INPUT_INDEX_SYSTEM_TOUCH, NULL); 257 + 258 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", 259 + XRT_INPUT_INDEX_A_CLICK, NULL); 260 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/touch", 261 + XRT_INPUT_INDEX_A_TOUCH, NULL); 262 + 263 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", 264 + XRT_INPUT_INDEX_B_CLICK, NULL); 265 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/touch", 266 + XRT_INPUT_INDEX_B_TOUCH, NULL); 267 + 268 + 269 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 270 + "/input/grip/force", 271 + XRT_INPUT_INDEX_SQUEEZE_FORCE, NULL); 272 + 273 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 274 + "/input/grip/value", 275 + XRT_INPUT_INDEX_SQUEEZE_VALUE, NULL); 276 + 277 + struct MonadoInputComponent x = {true, true, false}; 278 + struct MonadoInputComponent y = {true, false, true}; 279 + 280 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 281 + "/input/thumbstick/click", 282 + XRT_INPUT_INDEX_THUMBSTICK_CLICK, NULL); 283 + 284 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 285 + "/input/thumbstick/touch", 286 + XRT_INPUT_INDEX_THUMBSTICK_TOUCH, NULL); 287 + 288 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 289 + "/input/thumbstick/x", 290 + XRT_INPUT_INDEX_THUMBSTICK, &x); 291 + 292 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 293 + "/input/thumbstick/y", 294 + XRT_INPUT_INDEX_THUMBSTICK, &y); 295 + 296 + 297 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 298 + "/input/trackpad/force", 299 + XRT_INPUT_INDEX_TRACKPAD_FORCE, NULL); 300 + 301 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 302 + "/input/trackpad/touch", 303 + XRT_INPUT_INDEX_TRACKPAD_TOUCH, NULL); 304 + 305 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 306 + "/input/trackpad/x", 307 + XRT_INPUT_INDEX_TRACKPAD, &x); 308 + 309 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 310 + "/input/trackpad/y", 311 + XRT_INPUT_INDEX_TRACKPAD, &y); 312 + 313 + vr::VRDriverInput()->CreateHapticComponent( 314 + m_ulPropertyContainer, "/output/haptic", 315 + &m_hapticHandle); 316 + } 317 + 318 + break; 319 + case XRT_DEVICE_VIVE_WAND: { 320 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 321 + "/input/trigger/value", 322 + XRT_INPUT_VIVE_TRIGGER_VALUE, NULL); 323 + 324 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 325 + "/input/trigger/click", 326 + XRT_INPUT_VIVE_TRIGGER_CLICK, NULL); 327 + 328 + 329 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 330 + "/input/system/click", 331 + XRT_INPUT_VIVE_SYSTEM_CLICK, NULL); 332 + 333 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", 334 + XRT_INPUT_VIVE_TRACKPAD_CLICK, NULL); 335 + 336 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", 337 + XRT_INPUT_VIVE_MENU_CLICK, NULL); 338 + 339 + struct MonadoInputComponent x = {true, true, false}; 340 + struct MonadoInputComponent y = {true, false, true}; 341 + 342 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 343 + "/input/trackpad/touch", 344 + XRT_INPUT_VIVE_TRACKPAD_TOUCH, NULL); 345 + 346 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 347 + "/input/trackpad/x", XRT_INPUT_VIVE_TRACKPAD, 348 + &x); 349 + 350 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 351 + "/input/trackpad/y", XRT_INPUT_VIVE_TRACKPAD, 352 + &y); 353 + 354 + vr::VRDriverInput()->CreateHapticComponent( 355 + m_ulPropertyContainer, "/output/haptic", 356 + &m_hapticHandle); 357 + } break; 358 + case XRT_DEVICE_VIVE_TRACKER_GEN1: 359 + case XRT_DEVICE_VIVE_TRACKER_GEN2: break; 360 + case XRT_DEVICE_PSMV: { 361 + 362 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 363 + "/input/trigger/value", 364 + XRT_INPUT_PSMV_TRIGGER_VALUE, NULL); 365 + 366 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 367 + "/input/trigger/click", 368 + XRT_INPUT_PSMV_MOVE_CLICK, NULL); 369 + 370 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 371 + "/input/system/click", 372 + XRT_INPUT_PSMV_PS_CLICK, NULL); 373 + 374 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", 375 + XRT_INPUT_PSMV_CROSS_CLICK, NULL); 376 + 377 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", 378 + XRT_INPUT_PSMV_SQUARE_CLICK, NULL); 379 + 380 + vr::VRDriverInput()->CreateHapticComponent( 381 + m_ulPropertyContainer, "/output/haptic", 382 + &m_hapticHandle); 383 + } 384 + 385 + break; 386 + case XRT_DEVICE_HYDRA: 387 + case XRT_DEVICE_DAYDREAM: 388 + case XRT_DEVICE_GENERIC_HMD: 389 + default: break; 390 + } 391 + } 392 + 393 + void 394 + AddMonadoControls() 395 + { 396 + switch (this->m_xdev->name) { 397 + case XRT_DEVICE_INDEX_CONTROLLER: { 398 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 399 + "/input/trigger/value", 400 + XRT_INPUT_INDEX_TRIGGER_VALUE, NULL); 401 + 402 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 403 + "/input/trigger/click", 404 + XRT_INPUT_INDEX_TRIGGER_CLICK, NULL); 405 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 406 + "/input/trigger/touch", 407 + XRT_INPUT_INDEX_TRIGGER_TOUCH, NULL); 408 + 409 + 410 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 411 + "/input/system/click", 412 + XRT_INPUT_INDEX_SYSTEM_CLICK, NULL); 413 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 414 + "/input/system/touch", 415 + XRT_INPUT_INDEX_SYSTEM_TOUCH, NULL); 416 + 417 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", 418 + XRT_INPUT_INDEX_A_CLICK, NULL); 419 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/touch", 420 + XRT_INPUT_INDEX_A_TOUCH, NULL); 421 + 422 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", 423 + XRT_INPUT_INDEX_B_CLICK, NULL); 424 + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/touch", 425 + XRT_INPUT_INDEX_B_TOUCH, NULL); 426 + 427 + 428 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 429 + "/input/grip/force", 430 + XRT_INPUT_INDEX_SQUEEZE_FORCE, NULL); 431 + 432 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 433 + "/input/grip/value", 434 + XRT_INPUT_INDEX_SQUEEZE_VALUE, NULL); 435 + 436 + struct MonadoInputComponent x = {true, true, false}; 437 + struct MonadoInputComponent y = {true, false, true}; 438 + 439 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 440 + "/input/thumbstick/click", 441 + XRT_INPUT_INDEX_THUMBSTICK_CLICK, NULL); 442 + 443 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 444 + "/input/thumbstick/touch", 445 + XRT_INPUT_INDEX_THUMBSTICK_TOUCH, NULL); 446 + 447 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 448 + "/input/thumbstick/x", 449 + XRT_INPUT_INDEX_THUMBSTICK, &x); 450 + 451 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 452 + "/input/thumbstick/y", 453 + XRT_INPUT_INDEX_THUMBSTICK, &y); 454 + 455 + 456 + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, 457 + "/input/trackpad/force", 458 + XRT_INPUT_INDEX_TRACKPAD_FORCE, NULL); 459 + 460 + AddControl(XRT_INPUT_TYPE_BOOLEAN, 461 + "/input/trackpad/touch", 462 + XRT_INPUT_INDEX_TRACKPAD_TOUCH, NULL); 463 + 464 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 465 + "/input/trackpad/x", 466 + XRT_INPUT_INDEX_TRACKPAD, &x); 467 + 468 + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, 469 + "/input/trackpad/y", 470 + XRT_INPUT_INDEX_TRACKPAD, &y); 471 + 472 + vr::VRDriverInput()->CreateHapticComponent( 473 + m_ulPropertyContainer, "/output/haptic", 474 + &m_hapticHandle); 475 + } 476 + 477 + break; 478 + case XRT_DEVICE_VIVE_WAND: break; 479 + case XRT_DEVICE_VIVE_TRACKER_GEN1: 480 + case XRT_DEVICE_VIVE_TRACKER_GEN2: break; 481 + case XRT_DEVICE_PSMV: 482 + case XRT_DEVICE_HYDRA: 483 + case XRT_DEVICE_DAYDREAM: 484 + case XRT_DEVICE_GENERIC_HMD: 485 + default: break; 486 + } 487 + } 488 + 489 + void 490 + PoseUpdateThreadFunction() 491 + { 492 + ovrd_log("Starting controller pose update thread for %s\n", 493 + m_xdev->str); 494 + 495 + while (m_poseUpdating) { 496 + //! @todo figure out the best pose update rate 497 + std::this_thread::sleep_for( 498 + std::chrono::milliseconds(1)); 499 + 500 + if (m_unObjectId != vr::k_unTrackedDeviceIndexInvalid) { 501 + vr::VRServerDriverHost() 502 + ->TrackedDevicePoseUpdated( 503 + m_unObjectId, GetPose(), 504 + sizeof(vr::DriverPose_t)); 505 + } 506 + } 507 + 508 + ovrd_log("Stopping controller pose update thread for %s\n", 509 + m_xdev->str); 510 + } 511 + 512 + vr::EVRInitError 513 + Activate(vr::TrackedDeviceIndex_t unObjectId) 514 + { 515 + ovrd_log("Activating Controller SteamVR[%d]\n", unObjectId); 516 + 517 + if (!m_handed_controller) { 518 + //! @todo handle trackers etc 519 + ovrd_log("Unhandled: %s\n", m_xdev->str); 520 + return vr::VRInitError_Unknown; 521 + } 522 + 523 + m_unObjectId = unObjectId; 524 + 525 + if (this->m_xdev == NULL) { 526 + ovrd_log("Error: xdev NULL\n"); 527 + return vr::VRInitError_Init_InterfaceNotFound; 528 + } 529 + 530 + // clang-format off 531 + 532 + 533 + m_ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_unObjectId); 534 + 535 + // return a constant that's not 0 (invalid) or 1 (reserved for Oculus) 536 + vr::VRProperties()->SetUint64Property(m_ulPropertyContainer, vr::Prop_CurrentUniverseId_Uint64, 2); 537 + vr::VRProperties()->SetInt32Property(m_ulPropertyContainer, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); 538 + 539 + if (m_hand == XRT_HAND_LEFT) { 540 + ovrd_log("Left Controller\n"); 541 + vr::VRProperties()->SetInt32Property(m_ulPropertyContainer, vr::Prop_ControllerRoleHint_Int32, vr::TrackedControllerRole_LeftHand); 542 + } else if (m_hand == XRT_HAND_RIGHT) { 543 + ovrd_log("Right Controller\n"); 544 + vr::VRProperties()->SetInt32Property(m_ulPropertyContainer, vr::Prop_ControllerRoleHint_Int32, vr::TrackedControllerRole_RightHand); 545 + } 546 + m_pose.poseIsValid = false; 547 + m_pose.deviceIsConnected = true; 548 + m_pose.result = vr::TrackingResult_Uninitialized; 549 + m_pose.willDriftInYaw = !m_xdev->position_tracking_supported; 550 + 551 + if (m_emulate_index_controller) { 552 + m_input_profile = "{indexcontroller}/input/index_controller_profile.json"; 553 + m_controller_type = "knuckles"; 554 + if (m_hand == XRT_HAND_LEFT) { 555 + m_render_model = "{indexcontroller}valve_controller_knu_1_0_left"; 556 + } else if (m_hand == XRT_HAND_RIGHT) { 557 + m_render_model = "{indexcontroller}valve_controller_knu_1_0_right"; 558 + } 559 + } else { 560 + m_input_profile = "{monado}/input/monado_controller_profile.json"; 561 + m_controller_type = "monado_controller"; 562 + } 563 + 564 + ovrd_log("Using input profile %s\n", m_input_profile); 565 + ovrd_log("Using render model%s\n", m_render_model); 566 + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_InputProfilePath_String, m_input_profile); 567 + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_RenderModelName_String, m_render_model); 568 + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_ModelNumber_String, m_xdev->str); 569 + 570 + // clang-format on 571 + 572 + m_control_count = 0; 573 + if (m_emulate_index_controller) { 574 + AddEmulatedIndexControls(); 575 + } else { 576 + AddMonadoControls(); 577 + } 578 + 579 + ovrd_log("Controller %d activated\n", m_unObjectId); 580 + 581 + m_poseUpdateThread = new std::thread( 582 + &CDeviceDriver_Monado_Controller::PoseUpdateThreadFunction, 583 + this); 584 + if (!m_poseUpdateThread) { 585 + ovrd_log( 586 + "Unable to create pose updated thread for %s\n", 587 + m_xdev->str); 588 + return vr::VRInitError_Driver_Failed; 589 + } 590 + 591 + return vr::VRInitError_None; 592 + } 593 + 594 + void 595 + Deactivate() 596 + { 597 + ovrd_log("deactivate controller\n"); 598 + m_poseUpdating = false; 599 + m_poseUpdateThread->join(); 600 + m_unObjectId = vr::k_unTrackedDeviceIndexInvalid; 601 + } 602 + 603 + void 604 + EnterStandby() 605 + { 606 + ovrd_log("standby controller\n"); 607 + } 608 + 609 + void * 610 + GetComponent(const char *pchComponentNameAndVersion) 611 + { 612 + // deprecated API 613 + // ovrd_log("get controller component %s\n", 614 + // pchComponentNameAndVersion); 615 + return NULL; 616 + } 617 + 618 + /** debug request from a client */ 619 + void 620 + DebugRequest(const char *pchRequest, 621 + char *pchResponseBuffer, 622 + uint32_t unResponseBufferSize) 623 + { 624 + if (unResponseBufferSize >= 1) 625 + pchResponseBuffer[0] = 0; 626 + } 627 + 628 + vr::DriverPose_t 629 + GetPose() 630 + { 631 + m_pose.poseIsValid = true; 632 + m_pose.result = vr::TrackingResult_Running_OK; 633 + m_pose.deviceIsConnected = true; 634 + 635 + enum xrt_input_name grip_name; 636 + 637 + //! @todo better method to find grip name 638 + if (m_xdev->name == XRT_DEVICE_VIVE_WAND) { 639 + grip_name = XRT_INPUT_VIVE_GRIP_POSE; 640 + } else if (m_xdev->name == XRT_DEVICE_INDEX_CONTROLLER) { 641 + grip_name = XRT_INPUT_INDEX_GRIP_POSE; 642 + } else if (m_xdev->name == XRT_DEVICE_PSMV) { 643 + grip_name = XRT_INPUT_PSMV_GRIP_POSE; 644 + } else if (m_xdev->name == XRT_DEVICE_DAYDREAM) { 645 + grip_name = XRT_INPUT_DAYDREAM_POSE; 646 + } else if (m_xdev->name == XRT_DEVICE_HYDRA) { 647 + grip_name = XRT_INPUT_HYDRA_POSE; 648 + } else { 649 + ovrd_log("Unhandled device name %u\n", m_xdev->name); 650 + grip_name = XRT_INPUT_GENERIC_HEAD_POSE; // ??? 651 + } 652 + 653 + timepoint_ns now_ns = os_monotonic_get_ns(); 654 + 655 + struct xrt_space_relation rel; 656 + xrt_device_get_tracked_pose(m_xdev, grip_name, now_ns, &rel); 657 + 658 + struct xrt_pose *offset = &m_xdev->tracking_origin->offset; 659 + 660 + struct xrt_space_graph graph = {}; 661 + m_space_graph_add_relation(&graph, &rel); 662 + m_space_graph_add_pose_if_not_identity(&graph, offset); 663 + m_space_graph_resolve(&graph, &rel); 664 + 665 + apply_pose(&rel, &m_pose); 666 + 667 + #ifdef DUMP_POSE_CONTROLLERS 668 + ovrd_log("get controller %d pose %f %f %f %f, %f %f %f\n", 669 + m_unObjectId, m_pose.qRotation.x, m_pose.qRotation.y, 670 + m_pose.qRotation.z, m_pose.qRotation.w, 671 + m_pose.vecPosition[0], m_pose.vecPosition[1], 672 + m_pose.vecPosition[2]); 673 + #endif 674 + 675 + vr::HmdQuaternion_t identityquat{1, 0, 0, 0}; 676 + m_pose.qWorldFromDriverRotation = identityquat; 677 + m_pose.qDriverFromHeadRotation = identityquat; 678 + m_pose.vecDriverFromHeadTranslation[0] = 0.f; 679 + m_pose.vecDriverFromHeadTranslation[1] = 0.f; 680 + m_pose.vecDriverFromHeadTranslation[2] = 0.f; 681 + 682 + return m_pose; 683 + } 684 + 685 + void 686 + RunFrame() 687 + { 688 + m_xdev->update_inputs(m_xdev); 689 + 690 + 691 + for (int i = 0; i < m_control_count; i++) { 692 + 693 + // ovrd_log("Update %d: %s\n", i, 694 + // m_controls[i].steamvr_control_path); 695 + 696 + enum xrt_input_name input_name = 697 + m_controls[i].monado_input_name; 698 + 699 + struct xrt_input *input = NULL; 700 + for (uint32_t ii = 0; ii < m_xdev->num_inputs; ii++) { 701 + if (m_xdev->inputs[ii].name == input_name) { 702 + input = &m_xdev->inputs[ii]; 703 + break; 704 + } 705 + } 706 + 707 + if (input == NULL) { 708 + ovrd_log("Input for %s not found!\n", 709 + m_controls[i].steamvr_control_path); 710 + continue; 711 + } 712 + 713 + vr::VRInputComponentHandle_t handle = 714 + m_controls[i].control_handle; 715 + 716 + if (m_controls[i].monado_input_type == 717 + XRT_INPUT_TYPE_BOOLEAN) { 718 + bool state = input->value.boolean; 719 + vr::VRDriverInput()->UpdateBooleanComponent( 720 + handle, state, 0); 721 + // ovrd_log("Update %s: %d\n", 722 + // m_controls[i].steamvr_control_path, state); 723 + // printf("Update %s: %d\n", 724 + // m_controls[i].steamvr_control_path, 725 + // state); 726 + } 727 + if (m_controls[i].monado_input_type == 728 + XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE || 729 + m_controls[i].monado_input_type == 730 + XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE) { 731 + 732 + float value; 733 + if (m_controls[i].component.has_component && 734 + m_controls[i].component.x) { 735 + value = input->value.vec2.x; 736 + } else if (m_controls[i] 737 + .component.has_component && 738 + m_controls[i].component.y) { 739 + value = input->value.vec2.y; 740 + } else { 741 + value = input->value.vec1.x; 742 + } 743 + 744 + vr::VRDriverInput()->UpdateScalarComponent( 745 + handle, value, 0); 746 + // ovrd_log("Update %s: %f\n", 747 + // m_controls[i].steamvr_control_path, 748 + // state->x); 749 + // printf("Update %s: %f\n", 750 + // m_controls[i].steamvr_control_path, 751 + // value); 752 + } 753 + } 754 + } 755 + 756 + 757 + vr::VRControllerState_t 758 + GetControllerState() 759 + { 760 + // deprecated API 761 + vr::VRControllerState_t controllerstate; 762 + return controllerstate; 763 + } 764 + 765 + bool 766 + TriggerHapticPulse(uint32_t unAxisId, 767 + uint16_t usPulseDurationMicroseconds) 768 + { 769 + // deprecated API 770 + return false; 771 + } 772 + 773 + std::string 774 + GetSerialNumber() const 775 + { 776 + ovrd_log("get controller serial number: %s\n", m_sSerialNumber); 777 + return m_sSerialNumber; 778 + } 779 + 780 + 781 + struct xrt_device *m_xdev; 782 + vr::DriverPose_t m_pose; 783 + vr::TrackedDeviceIndex_t m_unObjectId; 784 + vr::PropertyContainerHandle_t m_ulPropertyContainer; 785 + 786 + bool m_emulate_index_controller = false; 787 + 788 + private: 789 + char m_sSerialNumber[XRT_DEVICE_NAME_LEN]; 790 + char m_sModelNumber[XRT_DEVICE_NAME_LEN]; 791 + 792 + const char *m_controller_type = NULL; 793 + 794 + const char *m_render_model = NULL; 795 + enum xrt_hand m_hand; 796 + bool m_handed_controller; 797 + 798 + const char *m_input_profile = NULL; 799 + 800 + struct SteamVRDriverControl m_controls[50]; 801 + int m_control_count; 802 + 803 + vr::VRInputComponentHandle_t m_hapticHandle = 0; 804 + 805 + bool m_poseUpdating = true; 806 + std::thread *m_poseUpdateThread = NULL; 807 + }; 808 + 809 + /* 810 + * 811 + * Device driver 812 + * 813 + */ 814 + 815 + class CDeviceDriver_Monado : public vr::ITrackedDeviceServerDriver, 816 + public vr::IVRDisplayComponent 817 + { 818 + public: 819 + CDeviceDriver_Monado(struct xrt_instance *xinst, 820 + struct xrt_device *xdev) 821 + : m_xdev(xdev) 822 + { 823 + //! @todo latency 824 + m_flSecondsFromVsyncToPhotons = 0.011; 825 + 826 + float ns = 827 + (float)m_xdev->hmd->screens->nominal_frame_interval_ns; 828 + m_flDisplayFrequency = 1. / ns * 1000. * 1000. * 1000.; 829 + ovrd_log("display frequency from device: %f\n", 830 + m_flDisplayFrequency); 831 + 832 + // steamvr can really misbehave when freq is inf or so 833 + if (m_flDisplayFrequency < 0 || m_flDisplayFrequency > 1000) { 834 + ovrd_log("Setting display frequency to 60 Hz!\n"); 835 + m_flDisplayFrequency = 60.; 836 + } 837 + 838 + //! @todo get ipd user setting from monado session 839 + float ipd_meters = 0.063; 840 + struct xrt_vec3 ipd_vec = {ipd_meters, 0, 0}; 841 + 842 + for (int view = 0; view < 2; view++) { 843 + xdev->get_view_pose(xdev, &ipd_vec, view, 844 + &m_view_pose[view]); 845 + } 846 + 847 + //! @todo more versatile IPD calculation 848 + float actual_ipd = 849 + -m_view_pose[0].position.x + m_view_pose[1].position.x; 850 + 851 + m_flIPD = actual_ipd; 852 + 853 + ovrd_log("Seconds from Vsync to Photons: %f\n", 854 + m_flSecondsFromVsyncToPhotons); 855 + ovrd_log("Display Frequency: %f\n", m_flDisplayFrequency); 856 + ovrd_log("IPD: %f\n", m_flIPD); 857 + }; 858 + virtual ~CDeviceDriver_Monado(){}; 859 + 860 + // clang-format off 861 + 862 + // ITrackedDeviceServerDriver 863 + virtual vr::EVRInitError Activate(vr::TrackedDeviceIndex_t unObjectId); 864 + virtual void Deactivate(); 865 + virtual void EnterStandby(); 866 + virtual void *GetComponent(const char *pchComponentNameAndVersion); 867 + virtual void DebugRequest(const char *pchRequest, char *pchResponseBuffer, uint32_t unResponseBufferSize); 868 + virtual vr::DriverPose_t GetPose(); 869 + 870 + // IVRDisplayComponent 871 + virtual void GetWindowBounds(int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight); 872 + virtual bool IsDisplayOnDesktop(); 873 + virtual bool IsDisplayRealDisplay(); 874 + virtual void GetRecommendedRenderTargetSize(uint32_t *pnWidth, uint32_t *pnHeight); 875 + virtual void GetEyeOutputViewport(vr::EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight); 876 + virtual void GetProjectionRaw(vr::EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom); 877 + virtual vr::DistortionCoordinates_t ComputeDistortion(vr::EVREye eEye, float fU, float fV); 878 + 879 + // clang-format on 880 + 881 + private: 882 + struct xrt_device *m_xdev = NULL; 883 + 884 + // clang-format off 885 + 886 + vr::TrackedDeviceIndex_t m_trackedDeviceIndex = 0; 887 + vr::PropertyContainerHandle_t m_ulPropertyContainer = vr::k_ulInvalidPropertyContainer; 888 + 889 + float m_flSecondsFromVsyncToPhotons = -1; 890 + float m_flDisplayFrequency = -1; 891 + float m_flIPD = -1; 892 + 893 + struct xrt_pose m_view_pose[2]; 894 + 895 + bool m_poseUpdating = true; 896 + std::thread *m_poseUpdateThread = NULL; 897 + virtual void PoseUpdateThreadFunction(); 898 + 899 + // clang-format on 900 + }; 901 + 902 + static void 903 + create_translation_rotation_matrix(struct xrt_pose *pose, 904 + struct vr::HmdMatrix34_t *res) 905 + { 906 + struct xrt_vec3 t = pose->position; 907 + struct xrt_quat r = pose->orientation; 908 + res->m[0][0] = (1.0f - 2.0f * (r.y * r.y + r.z * r.z)); 909 + res->m[1][0] = (r.x * r.y + r.z * r.w) * 2.0f; 910 + res->m[2][0] = (r.x * r.z - r.y * r.w) * 2.0f; 911 + res->m[0][1] = (r.x * r.y - r.z * r.w) * 2.0f; 912 + res->m[1][1] = (1.0f - 2.0f * (r.x * r.x + r.z * r.z)); 913 + res->m[2][1] = (r.y * r.z + r.x * r.w) * 2.0f; 914 + res->m[0][2] = (r.x * r.z + r.y * r.w) * 2.0f; 915 + res->m[1][2] = (r.y * r.z - r.x * r.w) * 2.0f; 916 + res->m[2][2] = (1.0f - 2.0f * (r.x * r.x + r.y * r.y)); 917 + res->m[0][3] = t.x; 918 + res->m[1][3] = t.y; 919 + res->m[2][3] = t.z; 920 + } 921 + 922 + void 923 + CDeviceDriver_Monado::PoseUpdateThreadFunction() 924 + { 925 + ovrd_log("Starting HMD pose update thread\n"); 926 + 927 + while (m_poseUpdating) { 928 + //! @todo figure out the best pose update rate 929 + std::this_thread::sleep_for(std::chrono::milliseconds(1)); 930 + vr::VRServerDriverHost()->TrackedDevicePoseUpdated( 931 + m_trackedDeviceIndex, GetPose(), sizeof(vr::DriverPose_t)); 932 + } 933 + ovrd_log("Stopping HMD pose update thread\n"); 934 + } 935 + 936 + vr::EVRInitError 937 + CDeviceDriver_Monado::Activate(vr::TrackedDeviceIndex_t unObjectId) 938 + { 939 + ovrd_log("Activate tracked device %u: %s\n", unObjectId, m_xdev->str); 940 + 941 + m_trackedDeviceIndex = unObjectId; 942 + 943 + // clang-format off 944 + 945 + m_ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(unObjectId); 946 + //! @todo: proper serial and model number 947 + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_ModelNumber_String, m_xdev->str); 948 + vr::VRProperties()->SetFloatProperty(m_ulPropertyContainer, vr::Prop_UserIpdMeters_Float, m_flIPD); 949 + vr::VRProperties()->SetFloatProperty(m_ulPropertyContainer, vr::Prop_UserHeadToEyeDepthMeters_Float, 0.f); 950 + vr::VRProperties()->SetFloatProperty(m_ulPropertyContainer, vr::Prop_DisplayFrequency_Float, m_flDisplayFrequency); 951 + vr::VRProperties()->SetFloatProperty( m_ulPropertyContainer, vr::Prop_SecondsFromVsyncToPhotons_Float, m_flSecondsFromVsyncToPhotons); 952 + 953 + // return a constant that's not 0 (invalid) or 1 (reserved for Oculus) 954 + vr::VRProperties()->SetUint64Property( m_ulPropertyContainer, vr::Prop_CurrentUniverseId_Uint64, 2); 955 + 956 + // avoid "not fullscreen" warnings from vrmonitor 957 + //vr::VRProperties()->SetBoolProperty(m_ulPropertyContainer, vr::Prop_IsOnDesktop_Bool, false); 958 + 959 + // clang-format on 960 + 961 + 962 + //! @todo update when ipd changes 963 + vr::HmdMatrix34_t left; 964 + create_translation_rotation_matrix(&m_view_pose[0], &left); 965 + vr::HmdMatrix34_t right; 966 + create_translation_rotation_matrix(&m_view_pose[1], &right); 967 + vr::VRServerDriverHost()->TrackedDeviceDisplayTransformUpdated( 968 + m_trackedDeviceIndex, left, right); 969 + 970 + 971 + m_poseUpdateThread = new std::thread( 972 + &CDeviceDriver_Monado::PoseUpdateThreadFunction, this); 973 + if (!m_poseUpdateThread) { 974 + ovrd_log("Unable to create pose updated thread for %s\n", 975 + m_xdev->str); 976 + return vr::VRInitError_Driver_Failed; 977 + } 978 + 979 + return vr::VRInitError_None; 980 + } 981 + 982 + void 983 + CDeviceDriver_Monado::Deactivate() 984 + { 985 + m_poseUpdating = false; 986 + m_poseUpdateThread->join(); 987 + ovrd_log("Deactivate\n"); 988 + } 989 + 990 + void 991 + CDeviceDriver_Monado::EnterStandby() 992 + { 993 + ovrd_log("Enter Standby\n"); 994 + } 995 + 996 + void * 997 + CDeviceDriver_Monado::GetComponent(const char *pchComponentNameAndVersion) 998 + { 999 + // clang-format off 1000 + if (strcmp(pchComponentNameAndVersion, vr::IVRDisplayComponent_Version) == 0) { 1001 + return (vr::IVRDisplayComponent *)this; 1002 + } 1003 + // clang-format on 1004 + 1005 + return NULL; 1006 + } 1007 + 1008 + void 1009 + CDeviceDriver_Monado::DebugRequest(const char *pchRequest, 1010 + char *pchResponseBuffer, 1011 + uint32_t unResponseBufferSize) 1012 + { 1013 + //! @todo 1014 + } 1015 + 1016 + static inline vr::HmdQuaternion_t 1017 + HmdQuaternion_Init(double w, double x, double y, double z) 1018 + { 1019 + vr::HmdQuaternion_t quat; 1020 + quat.w = w; 1021 + quat.x = x; 1022 + quat.y = y; 1023 + quat.z = z; 1024 + return quat; 1025 + } 1026 + 1027 + vr::DriverPose_t 1028 + CDeviceDriver_Monado::GetPose() 1029 + { 1030 + 1031 + struct xrt_space_relation rel; 1032 + xrt_device_get_tracked_pose(m_xdev, XRT_INPUT_GENERIC_HEAD_POSE, 1, 1033 + &rel); 1034 + 1035 + struct xrt_pose *offset = &m_xdev->tracking_origin->offset; 1036 + 1037 + struct xrt_space_graph graph = {}; 1038 + m_space_graph_add_relation(&graph, &rel); 1039 + m_space_graph_add_pose_if_not_identity(&graph, offset); 1040 + m_space_graph_resolve(&graph, &rel); 1041 + 1042 + vr::DriverPose_t t = {}; 1043 + 1044 + //! @todo: Monado head model? 1045 + t.shouldApplyHeadModel = !m_xdev->position_tracking_supported; 1046 + t.willDriftInYaw = !m_xdev->position_tracking_supported; 1047 + 1048 + t.qWorldFromDriverRotation = HmdQuaternion_Init(1, 0, 0, 0); 1049 + t.qDriverFromHeadRotation = HmdQuaternion_Init(1, 0, 0, 0); 1050 + 1051 + t.poseIsValid = 1052 + rel.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT; 1053 + t.result = vr::TrackingResult_Running_OK; 1054 + t.deviceIsConnected = true; 1055 + 1056 + apply_pose(&rel, &t); 1057 + 1058 + #ifdef DUMP_POSE 1059 + ovrd_log("get hmd pose %f %f %f %f, %f %f %f\n", t.qRotation.x, 1060 + t.qRotation.y, t.qRotation.z, t.qRotation.w, t.vecPosition[0], 1061 + t.vecPosition[1], t.vecPosition[2]); 1062 + #endif 1063 + 1064 + //! @todo 1065 + // copy_vec3(&rel.angular_velocity, t.vecAngularVelocity); 1066 + // copy_vec3(&rel.angular_acceleration, t.vecAngularAcceleration); 1067 + 1068 + // ovrd_log("Vel: %f %f %f\n", t.vecAngularVelocity[0], 1069 + // t.vecAngularVelocity[1], t.vecAngularVelocity[2]); ovrd_log("Accel: 1070 + // %f %f %f\n", t.vecAngularAcceleration[0], 1071 + // t.vecAngularAcceleration[1], t.vecAngularAcceleration[2]); 1072 + 1073 + return t; 1074 + } 1075 + 1076 + void 1077 + CDeviceDriver_Monado::GetWindowBounds(int32_t *pnX, 1078 + int32_t *pnY, 1079 + uint32_t *pnWidth, 1080 + uint32_t *pnHeight) 1081 + { 1082 + // offset in extended mode, e.g. to the right of a 1920x1080 monitor 1083 + *pnX = 1920; 1084 + *pnY = 0; 1085 + 1086 + *pnWidth = m_xdev->hmd->views[0].viewport.w_pixels + 1087 + m_xdev->hmd->views[1].viewport.w_pixels; 1088 + 1089 + uint32_t max_height = m_xdev->hmd->views[0].viewport.h_pixels > 1090 + m_xdev->hmd->views[1].viewport.h_pixels 1091 + ? m_xdev->hmd->views[0].viewport.h_pixels 1092 + : m_xdev->hmd->views[1].viewport.h_pixels; 1093 + *pnHeight = max_height; 1094 + 1095 + ovrd_log("Window Bounds: %dx%d\n", *pnWidth, *pnHeight); 1096 + } 1097 + 1098 + bool 1099 + CDeviceDriver_Monado::IsDisplayOnDesktop() 1100 + { 1101 + return false; 1102 + } 1103 + 1104 + bool 1105 + CDeviceDriver_Monado::IsDisplayRealDisplay() 1106 + { 1107 + return true; 1108 + } 1109 + 1110 + void 1111 + CDeviceDriver_Monado::GetRecommendedRenderTargetSize(uint32_t *pnWidth, 1112 + uint32_t *pnHeight) 1113 + { 1114 + int scale = debug_get_num_option_scale_percentage(); 1115 + float fscale = (float)scale / 100.f; 1116 + 1117 + 1118 + 1119 + *pnWidth = m_xdev->hmd->screens[0].w_pixels * fscale; 1120 + *pnHeight = m_xdev->hmd->screens[0].h_pixels * fscale; 1121 + 1122 + ovrd_log("Render Target Size: %dx%d (%fx)\n", *pnWidth, *pnHeight, 1123 + fscale); 1124 + } 1125 + 1126 + void 1127 + CDeviceDriver_Monado::GetEyeOutputViewport(vr::EVREye eEye, 1128 + uint32_t *pnX, 1129 + uint32_t *pnY, 1130 + uint32_t *pnWidth, 1131 + uint32_t *pnHeight) 1132 + { 1133 + *pnWidth = m_xdev->hmd->views[eEye].viewport.w_pixels; 1134 + *pnHeight = m_xdev->hmd->views[eEye].viewport.h_pixels; 1135 + 1136 + *pnY = 0; 1137 + if (eEye == vr::Eye_Left) { 1138 + *pnX = 0; 1139 + } else { 1140 + *pnX = m_xdev->hmd->views[0].viewport.w_pixels; 1141 + } 1142 + 1143 + ovrd_log("Output Viewport for eye %d: %dx%d offset %dx%d\n", eEye, 1144 + *pnWidth, *pnHeight, *pnX, *pnY); 1145 + } 1146 + 1147 + void 1148 + CDeviceDriver_Monado::GetProjectionRaw(vr::EVREye eEye, 1149 + float *pfLeft, 1150 + float *pfRight, 1151 + float *pfTop, 1152 + float *pfBottom) 1153 + { 1154 + *pfLeft = tanf(m_xdev->hmd->views[eEye].fov.angle_left); 1155 + *pfRight = tanf(m_xdev->hmd->views[eEye].fov.angle_right); 1156 + *pfTop = tanf(-m_xdev->hmd->views[eEye].fov.angle_up); 1157 + *pfBottom = tanf(-m_xdev->hmd->views[eEye].fov.angle_down); 1158 + ovrd_log("Projection Raw: L%f R%f T%f B%f\n", *pfLeft, *pfRight, *pfTop, 1159 + *pfBottom); 1160 + } 1161 + 1162 + vr::DistortionCoordinates_t 1163 + CDeviceDriver_Monado::ComputeDistortion(vr::EVREye eEye, float fU, float fV) 1164 + { 1165 + /** Used to return the post-distortion UVs for each color channel. 1166 + * UVs range from 0 to 1 with 0,0 in the upper left corner of the 1167 + * source render target. The 0,0 to 1,1 range covers a single eye. */ 1168 + 1169 + struct xrt_uv_triplet d; 1170 + 1171 + if (!m_xdev->compute_distortion(m_xdev, eEye, fU, fV, &d)) { 1172 + ovrd_log("Failed to compute distortion for view %d at %f,%f!\n", 1173 + eEye, fU, fV); 1174 + 1175 + vr::DistortionCoordinates_t coordinates; 1176 + coordinates.rfBlue[0] = fU; 1177 + coordinates.rfBlue[1] = fV; 1178 + coordinates.rfGreen[0] = fU; 1179 + coordinates.rfGreen[1] = fV; 1180 + coordinates.rfRed[0] = fU; 1181 + coordinates.rfRed[1] = fV; 1182 + return coordinates; 1183 + } 1184 + 1185 + vr::DistortionCoordinates_t coordinates; 1186 + coordinates.rfRed[0] = d.r.x; 1187 + coordinates.rfRed[1] = d.r.y; 1188 + coordinates.rfGreen[0] = d.g.x; 1189 + coordinates.rfGreen[1] = d.g.y; 1190 + coordinates.rfBlue[0] = d.b.x; 1191 + coordinates.rfBlue[1] = d.b.y; 1192 + 1193 + // ovrd_log("Computed distortion for view %d at %f,%f -> %f,%f | %f,%f | 1194 + // %f,%f!\n", eEye, fU, fV, d.r.x, d.r.y, d.g.x, d.g.y, d.b.x, d.b.y); 1195 + 1196 + return coordinates; 1197 + } 1198 + 1199 + 1200 + /* 1201 + * 1202 + * Device driver server 1203 + * 1204 + */ 1205 + 1206 + class CServerDriver_Monado : public vr::IServerTrackedDeviceProvider 1207 + { 1208 + public: 1209 + CServerDriver_Monado() : m_MonadoDeviceDriver(NULL) {} 1210 + 1211 + // clang-format off 1212 + virtual ~CServerDriver_Monado() {}; 1213 + virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext); 1214 + virtual void Cleanup(); 1215 + virtual const char *const * GetInterfaceVersions() { return vr::k_InterfaceVersions; } 1216 + virtual void RunFrame(); 1217 + virtual bool ShouldBlockStandbyMode() { return false; } 1218 + virtual void EnterStandby() {} 1219 + virtual void LeaveStandby() {} 1220 + virtual void HandleHapticEvent(vr::VREvent_t *event); 1221 + // clang-format on 1222 + 1223 + private: 1224 + struct xrt_instance *m_xinst = NULL; 1225 + struct xrt_device *m_xhmd = NULL; 1226 + 1227 + CDeviceDriver_Monado *m_MonadoDeviceDriver = NULL; 1228 + CDeviceDriver_Monado_Controller *m_left = NULL; 1229 + CDeviceDriver_Monado_Controller *m_right = NULL; 1230 + }; 1231 + 1232 + CServerDriver_Monado g_serverDriverMonado; 1233 + 1234 + #define NUM_XDEVS 16 1235 + 1236 + vr::EVRInitError 1237 + CServerDriver_Monado::Init(vr::IVRDriverContext *pDriverContext) 1238 + { 1239 + 1240 + VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext); 1241 + ovrd_log_init(vr::VRDriverLog()); 1242 + 1243 + ovrd_log("Initializing Monado driver\n"); 1244 + 1245 + //! @todo instance initialization is difficult to replicate 1246 + 1247 + int ret; 1248 + ret = xrt_instance_create(NULL, &m_xinst); 1249 + if (ret < 0) { 1250 + ovrd_log("Failed to create instance\n"); 1251 + return vr::VRInitError_Init_HmdNotFound; 1252 + } 1253 + 1254 + struct xrt_device *xdevs[NUM_XDEVS] = {0}; 1255 + 1256 + ret = xrt_instance_select(m_xinst, xdevs, NUM_XDEVS); 1257 + 1258 + int head, left, right; 1259 + 1260 + u_device_assign_xdev_roles(xdevs, NUM_XDEVS, &head, &left, &right); 1261 + 1262 + if (ret < 0 || head == XRT_DEVICE_ROLE_UNASSIGNED) { 1263 + ovrd_log("Failed to select HMD\n"); 1264 + xrt_instance_destroy(&m_xinst); 1265 + return vr::VRInitError_Init_HmdNotFound; 1266 + } 1267 + 1268 + m_xhmd = xdevs[head]; 1269 + 1270 + ovrd_log("Selected HMD %s\n", m_xhmd->str); 1271 + m_MonadoDeviceDriver = new CDeviceDriver_Monado(m_xinst, m_xhmd); 1272 + //! @todo provide a serial number 1273 + vr::VRServerDriverHost()->TrackedDeviceAdded( 1274 + m_xhmd->str, vr::TrackedDeviceClass_HMD, m_MonadoDeviceDriver); 1275 + 1276 + struct xrt_device *left_xdev = NULL; 1277 + if (left != XRT_DEVICE_ROLE_UNASSIGNED) { 1278 + left_xdev = xdevs[left]; 1279 + } 1280 + struct xrt_device *right_xdev = NULL; 1281 + if (right != XRT_DEVICE_ROLE_UNASSIGNED) { 1282 + right_xdev = xdevs[right]; 1283 + } 1284 + 1285 + ovrd_log("Left Controller: %s\n", left_xdev ? left_xdev->str : ""); 1286 + ovrd_log("Right Controller: %s\n", right_xdev ? right_xdev->str : ""); 1287 + 1288 + u_device_setup_tracking_origins(m_xhmd, left_xdev, right_xdev); 1289 + 1290 + if (left) { 1291 + m_left = new CDeviceDriver_Monado_Controller(m_xinst, left_xdev, 1292 + XRT_HAND_LEFT); 1293 + vr::VRServerDriverHost()->TrackedDeviceAdded( 1294 + m_left->GetSerialNumber().c_str(), 1295 + vr::TrackedDeviceClass_Controller, m_left); 1296 + } 1297 + if (right) { 1298 + m_right = new CDeviceDriver_Monado_Controller( 1299 + m_xinst, right_xdev, XRT_HAND_RIGHT); 1300 + vr::VRServerDriverHost()->TrackedDeviceAdded( 1301 + m_right->GetSerialNumber().c_str(), 1302 + vr::TrackedDeviceClass_Controller, m_right); 1303 + } 1304 + 1305 + return vr::VRInitError_None; 1306 + } 1307 + 1308 + void 1309 + CServerDriver_Monado::Cleanup() 1310 + { 1311 + if (m_MonadoDeviceDriver != NULL) { 1312 + delete m_MonadoDeviceDriver; 1313 + m_MonadoDeviceDriver = NULL; 1314 + } 1315 + 1316 + if (m_xhmd) { 1317 + xrt_device_destroy(&m_xhmd); 1318 + } 1319 + 1320 + if (m_left) { 1321 + xrt_device_destroy(&m_left->m_xdev); 1322 + } 1323 + if (m_right) { 1324 + xrt_device_destroy(&m_right->m_xdev); 1325 + } 1326 + 1327 + if (m_xinst) { 1328 + xrt_instance_destroy(&m_xinst); 1329 + } 1330 + 1331 + return; 1332 + } 1333 + 1334 + void 1335 + CServerDriver_Monado::HandleHapticEvent(vr::VREvent_t *event) 1336 + { 1337 + float freq = event->data.hapticVibration.fFrequency; 1338 + float amp = event->data.hapticVibration.fAmplitude; 1339 + float duration = event->data.hapticVibration.fDurationSeconds; 1340 + 1341 + 1342 + ovrd_log("Haptic vibration %fs %fHz %famp\n", duration, freq, amp); 1343 + 1344 + struct xrt_device *xdev = NULL; 1345 + if (m_left && m_left->m_ulPropertyContainer == 1346 + event->data.hapticVibration.containerHandle) { 1347 + xdev = m_left->m_xdev; 1348 + ovrd_log("Haptic vibration left\n"); 1349 + } else if (m_right && m_right->m_ulPropertyContainer == 1350 + event->data.hapticVibration.containerHandle) { 1351 + xdev = m_right->m_xdev; 1352 + ovrd_log("Haptic vibration right\n"); 1353 + } else { 1354 + ovrd_log("Haptic vibration ignored\n"); 1355 + } 1356 + 1357 + union xrt_output_value out; 1358 + out.vibration.amplitude = amp; 1359 + if (duration > 0.00001) { 1360 + out.vibration.duration = duration * 1000. * 1000. * 1000.; 1361 + } else { 1362 + out.vibration.duration = XRT_MIN_HAPTIC_DURATION; 1363 + } 1364 + out.vibration.frequency = freq; 1365 + if (xdev != NULL) { 1366 + enum xrt_output_name name; 1367 + switch (xdev->name) { 1368 + case XRT_DEVICE_INDEX_CONTROLLER: 1369 + name = XRT_OUTPUT_NAME_INDEX_HAPTIC; 1370 + break; 1371 + case XRT_DEVICE_VIVE_WAND: 1372 + name = XRT_OUTPUT_NAME_VIVE_HAPTIC; 1373 + break; 1374 + case XRT_DEVICE_PSMV: 1375 + name = XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION; 1376 + break; 1377 + default: 1378 + //! @todo 1379 + name = XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION; 1380 + break; 1381 + } 1382 + 1383 + xdev->set_output(xdev, name, &out); 1384 + } 1385 + } 1386 + 1387 + void 1388 + CServerDriver_Monado::RunFrame() 1389 + { 1390 + if (m_left) { 1391 + m_left->RunFrame(); 1392 + } 1393 + if (m_right) { 1394 + m_right->RunFrame(); 1395 + } 1396 + 1397 + // https://github.com/ValveSoftware/openvr/issues/719#issuecomment-358038640 1398 + struct vr::VREvent_t event; 1399 + while (vr::VRServerDriverHost()->PollNextEvent( 1400 + &event, sizeof(struct vr::VREvent_t))) { 1401 + switch (event.eventType) { 1402 + case vr::VREvent_Input_HapticVibration: 1403 + HandleHapticEvent(&event); 1404 + break; 1405 + case vr::VREvent_PropertyChanged: 1406 + // ovrd_log("Property changed\n"); 1407 + break; 1408 + case vr::VREvent_TrackedDeviceActivated: 1409 + ovrd_log("Device activated %d\n", 1410 + event.trackedDeviceIndex); 1411 + break; 1412 + case vr::VREvent_TrackedDeviceUserInteractionStarted: 1413 + ovrd_log("Device interaction started %d\n", 1414 + event.trackedDeviceIndex); 1415 + break; 1416 + case vr::VREvent_IpdChanged: 1417 + ovrd_log("ipd changed to %fm\n", 1418 + event.data.ipd.ipdMeters); 1419 + break; 1420 + case vr::VREvent_ActionBindingReloaded: 1421 + ovrd_log("action binding reloaded\n"); 1422 + break; 1423 + case vr::VREvent_StatusUpdate: 1424 + ovrd_log("EVRState: %d\n", 1425 + event.data.status.statusState); 1426 + break; 1427 + 1428 + case vr::VREvent_TrackedDeviceRoleChanged: 1429 + // device roles are for legacy input 1430 + case vr::VREvent_ChaperoneUniverseHasChanged: 1431 + case vr::VREvent_ProcessQuit: 1432 + case vr::VREvent_QuitAcknowledged: 1433 + case vr::VREvent_ProcessDisconnected: 1434 + case vr::VREvent_ProcessConnected: 1435 + case vr::VREvent_DashboardActivated: 1436 + case vr::VREvent_DashboardDeactivated: 1437 + case vr::VREvent_Compositor_ChaperoneBoundsShown: 1438 + case vr::VREvent_Compositor_ChaperoneBoundsHidden: break; 1439 + 1440 + default: ovrd_log("Unhandled Event: %d\n", event.eventType); 1441 + } 1442 + } 1443 + } 1444 + 1445 + 1446 + /* 1447 + * 1448 + * Whatchdog code 1449 + * 1450 + */ 1451 + 1452 + class CWatchdogDriver_Monado : public vr::IVRWatchdogProvider 1453 + { 1454 + public: 1455 + CWatchdogDriver_Monado() 1456 + { 1457 + ovrd_log("Created watchdog object\n"); 1458 + m_pWatchdogThread = nullptr; 1459 + } 1460 + 1461 + // clang-format off 1462 + virtual ~CWatchdogDriver_Monado() {}; 1463 + virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext); 1464 + virtual void Cleanup(); 1465 + // clang-format on 1466 + 1467 + private: 1468 + std::thread *m_pWatchdogThread; 1469 + }; 1470 + 1471 + CWatchdogDriver_Monado g_watchdogDriverMonado; 1472 + bool g_bExiting = false; 1473 + 1474 + void 1475 + WatchdogThreadFunction() 1476 + { 1477 + while (!g_bExiting) { 1478 + #if defined(_WINDOWS) 1479 + // on windows send the event when the Y key is pressed. 1480 + if ((0x01 & GetAsyncKeyState('Y')) != 0) { 1481 + // Y key was pressed. 1482 + vr::VRWatchdogHost()->WatchdogWakeUp(); 1483 + } 1484 + std::this_thread::sleep_for(std::chrono::microseconds(500)); 1485 + #else 1486 + ovrd_log("Watchdog wakeup\n"); 1487 + // for the other platforms, just send one every five seconds 1488 + std::this_thread::sleep_for(std::chrono::seconds(1)); 1489 + vr::VRWatchdogHost()->WatchdogWakeUp(); 1490 + #endif 1491 + } 1492 + 1493 + ovrd_log("Watchdog exit\n"); 1494 + } 1495 + 1496 + vr::EVRInitError 1497 + CWatchdogDriver_Monado::Init(vr::IVRDriverContext *pDriverContext) 1498 + { 1499 + VR_INIT_WATCHDOG_DRIVER_CONTEXT(pDriverContext); 1500 + ovrd_log_init(vr::VRDriverLog()); 1501 + 1502 + // Watchdog mode on Windows starts a thread that listens for the 'Y' key 1503 + // on the keyboard to be pressed. A real driver should wait for a system 1504 + // button event or something else from the the hardware that signals 1505 + // that the VR system should start up. 1506 + g_bExiting = false; 1507 + 1508 + ovrd_log("starting watchdog thread\n"); 1509 + 1510 + m_pWatchdogThread = new std::thread(WatchdogThreadFunction); 1511 + if (!m_pWatchdogThread) { 1512 + ovrd_log("Unable to create watchdog thread\n"); 1513 + return vr::VRInitError_Driver_Failed; 1514 + } 1515 + 1516 + return vr::VRInitError_None; 1517 + } 1518 + 1519 + void 1520 + CWatchdogDriver_Monado::Cleanup() 1521 + { 1522 + g_bExiting = true; 1523 + if (m_pWatchdogThread) { 1524 + m_pWatchdogThread->join(); 1525 + delete m_pWatchdogThread; 1526 + m_pWatchdogThread = nullptr; 1527 + } 1528 + 1529 + ovrd_log_cleanup(); 1530 + } 1531 + 1532 + 1533 + /* 1534 + * 1535 + * 'Exported' functions. 1536 + * 1537 + */ 1538 + 1539 + void * 1540 + ovrd_hmd_driver_impl(const char *pInterfaceName, int *pReturnCode) 1541 + { 1542 + // clang-format off 1543 + if (0 == strcmp(vr::IServerTrackedDeviceProvider_Version, pInterfaceName)) { 1544 + return &g_serverDriverMonado; 1545 + } 1546 + if (0 == strcmp(vr::IVRWatchdogProvider_Version, pInterfaceName)) { 1547 + return &g_watchdogDriverMonado; 1548 + } 1549 + // clang-format on 1550 + 1551 + ovrd_log("Unimplemented interface: %s\n", pInterfaceName); 1552 + 1553 + if (pReturnCode) { 1554 + *pReturnCode = vr::VRInitError_Init_InterfaceNotFound; 1555 + } 1556 + 1557 + return NULL; 1558 + }
+29
src/xrt/state_trackers/steamvr_drv/ovrd_interface.h
···
··· 1 + // Copyright 2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Interface to the Monado SteamVR Driver exporter. 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @ingroup st_ovrd 8 + */ 9 + 10 + #pragma once 11 + 12 + #include "xrt/xrt_defines.h" 13 + 14 + /*! 15 + * @defgroup st_ovrd SteamVR driver provider 16 + * 17 + * Wraps a @ref xrt_instance and one or more @ref xrt_device and exposes those 18 + * to SteamVR via the OpenVR driver interface. 19 + * 20 + * @ingroup xrt 21 + */ 22 + 23 + /*! 24 + * Implementation of the HmdDriverFactory function. 25 + * 26 + * @ingroup st_ovrd 27 + */ 28 + void * 29 + ovrd_hmd_driver_impl(const char *pInterfaceName, int *pReturnCode);
+62
src/xrt/state_trackers/steamvr_drv/ovrd_log.hpp
···
··· 1 + // Copyright 2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Logger code. 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @ingroup st_ovrd 8 + */ 9 + 10 + #pragma once 11 + 12 + #ifdef __cplusplus 13 + extern "C" { 14 + #endif 15 + 16 + #include <stdarg.h> 17 + 18 + #include "xrt/xrt_compiler.h" 19 + 20 + #ifdef __cplusplus 21 + } 22 + #endif 23 + 24 + #include "openvr_driver.h" 25 + 26 + static vr::IVRDriverLog *s_pLogFile = NULL; 27 + 28 + static inline void 29 + ovrd_log_init(vr::IVRDriverLog *pDriverLog) 30 + { 31 + // Noop 32 + s_pLogFile = vr::VRDriverLog(); 33 + } 34 + 35 + // Can not use the XRT_PRINTF_FORMAT macro on a function definition. 36 + static inline void 37 + ovrd_log(const char *fmt, ...) XRT_PRINTF_FORMAT(1, 2); 38 + 39 + static inline void 40 + ovrd_log(const char *fmt, ...) 41 + { 42 + va_list args; 43 + va_start(args, fmt); 44 + 45 + char buf[1024]; 46 + #if defined(WIN32) 47 + vsprintf_s(buf, pMsgFormat, args); 48 + #else 49 + vsnprintf(buf, sizeof(buf), fmt, args); 50 + #endif 51 + 52 + if (s_pLogFile) 53 + s_pLogFile->Log(buf); 54 + 55 + va_end(args); 56 + } 57 + 58 + static inline void 59 + ovrd_log_cleanup() 60 + { 61 + s_pLogFile = nullptr; 62 + }
+4
src/xrt/targets/CMakeLists.txt
··· 29 add_subdirectory(service) 30 endif() 31 endif()
··· 29 add_subdirectory(service) 30 endif() 31 endif() 32 + 33 + if(XRT_FEATURE_STEAMVR_PLUGIN) 34 + add_subdirectory(steamvr_drv) 35 + endif()
+3
src/xrt/targets/meson.build
··· 78 subdir('ctl') 79 endif 80
··· 78 subdir('ctl') 79 endif 80 81 + if get_option('steamvr_plugin') 82 + subdir('steamvr_drv') 83 + endif
+51
src/xrt/targets/steamvr_drv/CMakeLists.txt
···
··· 1 + # Copyright 2020, Collabora, Ltd. 2 + # SPDX-License-Identifier: BSL-1.0 3 + 4 + 5 + add_library(driver_monado MODULE 6 + main.c 7 + ) 8 + 9 + target_link_libraries(driver_monado PRIVATE 10 + xrt-external-openvr 11 + aux_util 12 + st_ovrd 13 + st_prober 14 + target_lists 15 + target_instance_no_comp 16 + ) 17 + 18 + 19 + # meta data that the steamvr plugin needs in the base directory of the steamvr plugin 20 + file(COPY driver.vrdrivermanifest DESTINATION ${CMAKE_BINARY_DIR}/steamvr-monado) 21 + file(COPY resources DESTINATION ${CMAKE_BINARY_DIR}/steamvr-monado) 22 + 23 + #determine the output directory for the steamvr plugin 24 + if (WIN32) 25 + # FIXME need to account for different architectures 26 + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") 27 + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/win64 CACHE TYPE INTERNAL) 28 + else() 29 + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/win32 CACHE TYPE INTERNAL) 30 + endif() 31 + elseif(APPLE) 32 + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") 33 + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/osx64 CACHE STRING INTERNAL) 34 + else() 35 + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/osx32 CACHE STRING INTERNAL) 36 + endif() 37 + elseif(NOT ANDROID) 38 + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") 39 + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/linux64 CACHE STRING INTERNAL) 40 + else() 41 + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/linux32 CACHE STRING INTERNAL) 42 + endif() 43 + endif() 44 + 45 + MESSAGE("SteamVR plugin path: ${PLUGINDIR}") 46 + set_target_properties(driver_monado PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PLUGINDIR}") 47 + 48 + # don't add lib prefix to driver_monado.so 49 + SET_TARGET_PROPERTIES(driver_monado PROPERTIES PREFIX "") 50 + 51 + install(DIRECTORY "${PROJECT_BINARY_DIR}/steamvr-monado" DESTINATION "${CMAKE_INSTALL_PREFIX}/share")
+31
src/xrt/targets/steamvr_drv/copy_assets.py
···
··· 1 + #!/usr/bin/env python3 2 + 3 + # Copyright 2020, Collabora, Ltd. 4 + # SPDX-License-Identifier: BSL-1.0 5 + 6 + import os, sys, shutil 7 + 8 + print(sys.argv[1], sys.argv[2], sys.argv[3]) 9 + 10 + is_file = sys.argv[1] == "FILE" 11 + is_dir = sys.argv[1] == "DIRECTORY" 12 + 13 + # get absolute input and output paths 14 + input_path = os.path.join( 15 + os.getenv('MESON_SOURCE_ROOT'), 16 + os.getenv('MESON_SUBDIR'), 17 + sys.argv[2]) 18 + 19 + output_path = os.path.join( 20 + os.getenv('MESON_BUILD_ROOT'), 21 + sys.argv[3]) 22 + 23 + # make sure destination directory exists 24 + os.makedirs(os.path.dirname(output_path), exist_ok=True) 25 + 26 + if is_file: 27 + shutil.copyfile(input_path, output_path) 28 + elif is_dir: 29 + shutil.copytree(input_path, output_path) 30 + 31 + print("Copying asset " + str(input_path) + " to " + str(output_path))
+20
src/xrt/targets/steamvr_drv/copy_plugin.py
···
··· 1 + #!/usr/bin/env python3 2 + 3 + # Copyright 2020, Collabora, Ltd. 4 + # SPDX-License-Identifier: BSL-1.0 5 + 6 + import os, sys, shutil 7 + 8 + # get absolute input and output paths 9 + input_path = os.path.join( 10 + sys.argv[1]) 11 + 12 + output_path = os.path.join( 13 + sys.argv[2]) 14 + 15 + # make sure destination directory exists 16 + os.makedirs(os.path.dirname(output_path), exist_ok=True) 17 + 18 + shutil.copyfile(input_path, output_path) 19 + 20 + print("Copying plugin " + str(input_path) + " to " + str(output_path))
+11
src/xrt/targets/steamvr_drv/driver.vrdrivermanifest
···
··· 1 + { 2 + "alwaysActivate": false, 3 + "name" : "monado", 4 + "directory" : "", 5 + "resourceOnly" : false, 6 + 7 + "hmd_presence" : 8 + [ 9 + "*.*" 10 + ] 11 + }
+28
src/xrt/targets/steamvr_drv/main.c
···
··· 1 + // Copyright 2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Very simple target main file to export the right symbol. 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @author Christoph Haag <christoph.haag@collabora.com> 8 + * @ingroup st_ovrd 9 + */ 10 + 11 + #include "ovrd_interface.h" 12 + 13 + #if defined(_WIN32) 14 + #define HMD_DLL_EXPORT __declspec(dllexport) 15 + #define HMD_DLL_IMPORT __declspec(dllimport) 16 + #elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__) 17 + #define HMD_DLL_EXPORT __attribute__((visibility("default"))) 18 + #define HMD_DLL_IMPORT 19 + #else 20 + #error "Unsupported Platform." 21 + #endif 22 + 23 + 24 + HMD_DLL_EXPORT void * 25 + HmdDriverFactory(const char *pInterfaceName, int *pReturnCode) 26 + { 27 + return ovrd_hmd_driver_impl(pInterfaceName, pReturnCode); 28 + }
+65
src/xrt/targets/steamvr_drv/meson.build
···
··· 1 + # Copyright 2020, Collabora, Ltd. 2 + # SPDX-License-Identifier: BSL-1.0 3 + 4 + plugin_dir = 'steamvr-monado' 5 + 6 + driver_monado = shared_library( 7 + 'driver_monado', 8 + files( 9 + 'main.c' 10 + ), 11 + link_whole: [ 12 + lib_target_instance_no_comp, 13 + lib_st_ovrd, 14 + ], 15 + include_directories: [ 16 + openvr_include, 17 + st_ovrd_include, 18 + openvr_include, 19 + aux_include, 20 + xrt_include, 21 + ], 22 + link_with : driver_libs, 23 + dependencies : [pthreads, libjpeg], 24 + # build 'driver_monado.so' instead of 'libdriver_monado.so' 25 + name_prefix: '', 26 + ) 27 + 28 + copy_asset = find_program('copy_assets.py') 29 + copy_plugin = find_program('copy_plugin.py') 30 + 31 + 32 + run_command(copy_asset, 'FILE', 'driver.vrdrivermanifest', join_paths(plugin_dir, 'driver.vrdrivermanifest')) 33 + run_command(copy_asset, 'DIRECTORY', 'resources', join_paths(plugin_dir, 'resources')) 34 + 35 + plugin_archdir = '' 36 + 37 + if host_machine.system() == 'windows' 38 + if host_machine.cpu_family() == 'x86' 39 + plugin_archdir = 'win32' 40 + elif host_machine.cpu_family() == 'x86_64' 41 + plugin_archdir = 'win64' 42 + endif 43 + elif host_machine.system() == 'linux' 44 + if host_machine.cpu_family() == 'x86' 45 + plugin_archdir = 'linux32' 46 + elif host_machine.cpu_family() == 'x86_64' 47 + plugin_archdir = 'linux64' 48 + endif 49 + endif 50 + 51 + custom_target( 52 + 'plugin_copy', 53 + depends : driver_monado, 54 + input : driver_monado, 55 + output : 'fake_plugin', 56 + command : [copy_plugin, '@INPUT@', join_paths(plugin_dir, 'bin', plugin_archdir, '@PLAINNAME@')], 57 + build_by_default : true 58 + ) 59 + 60 + if meson.version().version_compare('<0.56') 61 + build_root = meson.build_root() 62 + else 63 + build_root = meson.project_build_root() 64 + endif 65 + install_subdir(join_paths(build_root, plugin_dir), install_dir: join_paths(get_option('prefix'), 'share'))
+47
src/xrt/targets/steamvr_drv/resources/driver.vrresources
···
··· 1 + { 2 + "jsonid" : "vrresources", 3 + "statusicons" : { 4 + "HMD" : { 5 + "Prop_NamedIconPathDeviceOff_String" : "{monado}/icons/headset_monado_status_off.png", 6 + "Prop_NamedIconPathDeviceSearching_String" : "{monado}/icons/headset_monado_status_searching.gif", 7 + "Prop_NamedIconPathDeviceSearchingAlert_String" : "{monado}/icons/headset_monado_status_searching_alert.gif", 8 + "Prop_NamedIconPathDeviceReady_String" : "{monado}/icons/headset_monado_status_ready.png", 9 + "Prop_NamedIconPathDeviceReadyAlert_String" : "{monado}/icons/headset_monado_status_ready_alert.png", 10 + "Prop_NamedIconPathDeviceNotReady_String" : "{monado}/icons/headset_monado_status_error.png", 11 + "Prop_NamedIconPathDeviceStandby_String" : "{monado}/icons/headset_monado_status_standby.png", 12 + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/headset_monado_status_ready_low.png" 13 + }, 14 + 15 + "Model-v Defaults" : { 16 + "Prop_NamedIconPathDeviceOff_String" : "{monado}/icons/headset_monado_status_off.png", 17 + "Prop_NamedIconPathDeviceSearching_String" : "Prop_NamedIconPathDeviceOff_String", 18 + "Prop_NamedIconPathDeviceSearchingAlert_String" : "Prop_NamedIconPathDeviceOff_String", 19 + "Prop_NamedIconPathDeviceReady_String" : "Prop_NamedIconPathDeviceOff_String", 20 + "Prop_NamedIconPathDeviceReadyAlert_String" : "Prop_NamedIconPathDeviceOff_String", 21 + "Prop_NamedIconPathDeviceNotReady_String" : "Prop_NamedIconPathDeviceOff_String", 22 + "Prop_NamedIconPathDeviceStandby_String" : "Prop_NamedIconPathDeviceOff_String", 23 + "Prop_NamedIconPathDeviceAlertLow_String" : "Prop_NamedIconPathDeviceOff_String" 24 + }, 25 + 26 + "Model-v1.0" : { 27 + "Alias" : "Model-v Defaults", 28 + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/headset_model1_alertlow.png" 29 + }, 30 + 31 + "Model-v2.0" : { 32 + "Alias" : "Model-v1.0", 33 + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/headset_model2_alertlow.png" 34 + }, 35 + 36 + "Controller" : { 37 + "Prop_NamedIconPathDeviceOff_String" : "{monado}/icons/controller_status_off.png", 38 + "Prop_NamedIconPathDeviceSearching_String" : "{monado}/icons/controller_status_searching.gif", 39 + "Prop_NamedIconPathDeviceSearchingAlert_String" : "{monado}/icons/controller_status_searching_alert.gif", 40 + "Prop_NamedIconPathDeviceReady_String" : "{monado}/icons/controller_status_ready.png", 41 + "Prop_NamedIconPathDeviceReadyAlert_String" : "{monado}/icons/controller_status_ready_alert.png", 42 + "Prop_NamedIconPathDeviceNotReady_String" : "{monado}/icons/controller_status_error.png", 43 + "Prop_NamedIconPathDeviceStandby_String" : "{monado}/icons/controller_status_standby.png", 44 + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/controller_status_ready_low.png" 45 + } 46 + } 47 + }
src/xrt/targets/steamvr_drv/resources/icons/controller_status_error.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_off.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_alert.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_low.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching.gif

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching_alert.gif

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/controller_status_standby.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_error.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_off.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_alert.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_low.png

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching.gif

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching_alert.gif

This is a binary file and will not be displayed.

src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_standby.png

This is a binary file and will not be displayed.

+6
src/xrt/targets/steamvr_drv/resources/localization/localization.json
···
··· 1 + [ 2 + { 3 + "language_tag": "en_US", 4 + "monado_controller" : "Monado Driver Controller" 5 + } 6 + ]
+5
src/xrt/targets/steamvr_drv/resources/settings/default.vrsettings
···
··· 1 + { 2 + "driver_monado" : { 3 + "enable" : true 4 + } 5 + }
+16
src/xrt/targets/steamvr_drv/steamvr.vrsettings
···
··· 1 + { 2 + "jsonid" : "vrsettings", 3 + "driver_lighthouse": { 4 + "enable": false 5 + }, 6 + "driver_oculus": { 7 + "enable": false 8 + }, 9 + "driver_oculus_legacy": { 10 + "enable": false 11 + }, 12 + "driver_monado" : { 13 + "enable": true 14 + }, 15 + "version" : "1" 16 + }