#[cfg(feature = "submit_d3d11")]
extern crate windows;
extern crate openvr_sys;
#[macro_use]
extern crate lazy_static;
use std::fmt::Debug;
use std::ffi::CString;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{fmt, ptr};
use openvr_sys as sys;
mod tracking;
pub mod application;
pub mod chaperone;
pub mod compositor;
mod init_error;
pub mod property;
pub mod render_models;
pub mod system;
pub use init_error::InitError;
pub use tracking::*;
pub use sys::VkDevice_T;
pub use sys::VkInstance_T;
pub use sys::VkPhysicalDevice_T;
pub use sys::VkQueue_T;
pub mod input;
pub mod errors;
pub mod settings;
pub mod overlay;
pub mod pose;
static INITIALIZED: AtomicBool = AtomicBool::new(false);
pub unsafe fn init(ty: ApplicationType) -> Result<Context, InitError> {
let mut error = sys::EVRInitError_VRInitError_None;
sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType);
if error != sys::EVRInitError_VRInitError_None {
return Err(error.into());
}
if !sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) {
sys::VR_ShutdownInternal();
return Err(InitError::Init_InterfaceNotFound);
}
if INITIALIZED.swap(true, Ordering::Acquire) {
panic!("OpenVR has already been initialized!");
}
Ok(Context {
live: AtomicBool::new(true),
})
}
pub fn is_hmd_present() -> bool {
unsafe { sys::VR_IsHmdPresent() }
}
pub fn is_runtime_installed() -> bool {
unsafe { sys::VR_IsRuntimeInstalled() }
}
pub struct System(&'static sys::VR_IVRSystem_FnTable);
pub struct Application(&'static sys::VR_IVRApplications_FnTable);
pub struct Compositor(&'static sys::VR_IVRCompositor_FnTable);
pub struct RenderModels(&'static sys::VR_IVRRenderModels_FnTable);
pub struct Chaperone(&'static sys::VR_IVRChaperone_FnTable);
pub struct Input(&'static sys::VR_IVRInput_FnTable);
pub struct Settings(&'static sys::VR_IVRSettings_FnTable);
pub struct Overlay(&'static sys::VR_IVROverlay_FnTable);
pub struct Context {
live: AtomicBool,
}
fn load<T>(suffix: &[u8]) -> Result<*const T, InitError> {
let mut magic = Vec::from(b"FnTable:".as_ref());
magic.extend(suffix);
let mut error = sys::EVRInitError_VRInitError_None;
let result = unsafe { sys::VR_GetGenericInterface(magic.as_ptr() as *const i8, &mut error) };
if error != sys::EVRInitError_VRInitError_None {
return Err(InitError::Init_InterfaceNotFound);
}
Ok(result as *const T)
}
impl Context {
pub fn system(&self) -> Result<System, InitError> {
load(sys::IVRSystem_Version).map(|x| unsafe { System(&*x) })
}
pub fn application(&self) -> Result<Application, InitError> {
load(sys::IVRApplications_Version).map(|x| unsafe { Application(&*x) })
}
pub fn compositor(&self) -> Result<Compositor, InitError> {
load(sys::IVRCompositor_Version).map(|x| unsafe { Compositor(&*x) })
}
pub fn render_models(&self) -> Result<RenderModels, InitError> {
load(sys::IVRRenderModels_Version).map(|x| unsafe { RenderModels(&*x) })
}
pub fn chaperone(&self) -> Result<Chaperone, InitError> {
load(sys::IVRChaperone_Version).map(|x| unsafe { Chaperone(&*x) })
}
pub fn input(&self) -> Result<Input, InitError> {
load(sys::IVRInput_Version).map(|x| unsafe { Input(&*x) })
}
pub fn settings(&self) -> Result<Settings, InitError> {
load(sys::IVRSettings_Version).map(|x| unsafe { Settings(&*x) })
}
pub fn overlay(&self) -> Result<Overlay, InitError> {
load(sys::IVROverlay_Version).map(|x| unsafe { Overlay(&*x) })
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { self.shutdown() }
}
}
impl Context {
pub unsafe fn shutdown(&self) {
if self.live.swap(false,Ordering::Acquire) {
sys::VR_ShutdownInternal();
INITIALIZED.store(false, Ordering::Release);
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ApplicationType {
Other = sys::EVRApplicationType_VRApplication_Other as isize,
Scene = sys::EVRApplicationType_VRApplication_Scene as isize,
Overlay = sys::EVRApplicationType_VRApplication_Overlay as isize,
Background = sys::EVRApplicationType_VRApplication_Background as isize,
Utility = sys::EVRApplicationType_VRApplication_Utility as isize,
VRMonitor = sys::EVRApplicationType_VRApplication_VRMonitor as isize,
SteamWatchdog = sys::EVRApplicationType_VRApplication_SteamWatchdog as isize,
Bootstrapper = sys::EVRApplicationType_VRApplication_Bootstrapper as isize,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Eye {
Left = sys::EVREye_Eye_Left as isize,
Right = sys::EVREye_Eye_Right as isize,
}
unsafe fn get_string<F: FnMut(*mut std::os::raw::c_char, u32) -> u32>(mut f: F) -> Option<CString> {
let n = f(ptr::null_mut(), 0);
if n == 0 {
return None;
}
let mut storage = Vec::with_capacity(n as usize);
storage.set_len(n as usize);
let n_ = f(storage.as_mut_ptr() as *mut _, n);
assert!(n == n_);
storage.truncate((n - 1) as usize); Some(CString::from_vec_unchecked(storage))
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ControllerAxis {
pub x: f32,
pub y: f32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ControllerState {
pub packet_num: u32,
pub button_pressed: u64,
pub button_touched: u64,
pub axis: [ControllerAxis; 5],
}
pub mod button_id {
use super::sys;
pub const SYSTEM: sys::EVRButtonId = sys::EVRButtonId_k_EButton_System;
pub const APPLICATION_MENU: sys::EVRButtonId = sys::EVRButtonId_k_EButton_ApplicationMenu;
pub const GRIP: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Grip;
pub const DPAD_LEFT: sys::EVRButtonId = sys::EVRButtonId_k_EButton_DPad_Left;
pub const DPAD_UP: sys::EVRButtonId = sys::EVRButtonId_k_EButton_DPad_Up;
pub const DPAD_RIGHT: sys::EVRButtonId = sys::EVRButtonId_k_EButton_DPad_Right;
pub const DPAD_DOWN: sys::EVRButtonId = sys::EVRButtonId_k_EButton_DPad_Down;
pub const A: sys::EVRButtonId = sys::EVRButtonId_k_EButton_A;
pub const PROXIMITY_SENSOR: sys::EVRButtonId = sys::EVRButtonId_k_EButton_ProximitySensor;
pub const AXIS0: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Axis0;
pub const AXIS1: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Axis1;
pub const AXIS2: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Axis2;
pub const AXIS3: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Axis3;
pub const AXIS4: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Axis4;
pub const STEAM_VR_TOUCHPAD: sys::EVRButtonId = sys::EVRButtonId_k_EButton_SteamVR_Touchpad;
pub const STEAM_VR_TRIGGER: sys::EVRButtonId = sys::EVRButtonId_k_EButton_SteamVR_Trigger;
pub const DASHBOARD_BACK: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Dashboard_Back;
pub const MAX: sys::EVRButtonId = sys::EVRButtonId_k_EButton_Max;
}
pub struct TextureBounds(pub sys::VRTextureBounds_t);
impl Clone for TextureBounds {
fn clone(&self) -> Self {
Self(sys::VRTextureBounds_t {
uMin: self.0.uMin,
vMin: self.0.vMin,
uMax: self.0.uMax,
vMax: self.0.vMax,
})
}
}
impl Debug for TextureBounds {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TextureBounds")
.field("uMin", &self.0.uMin)
.field("vMin", &self.0.vMin)
.field("uMax", &self.0.uMax)
.field("vMax", &self.0.vMax)
.finish()
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct ColorTint {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Default for ColorTint {
fn default() -> Self {
Self {
r: 1.,
g: 1.,
b: 1.,
a: 1.,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
use std::ptr;
fn mock_success(output: *mut std::os::raw::c_char, size: u32) -> u32 {
let text = "OpenVR Test";
let c_str = CString::new(text).unwrap();
let bytes = c_str.as_bytes_with_nul();
if size == 0 {
return bytes.len() as u32; }
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), output as *mut u8, bytes.len());
}
bytes.len() as u32
}
fn mock_empty(_: *mut std::os::raw::c_char, _: u32) -> u32 {
0
}
#[test]
fn test_get_string_success() {
let result = unsafe { get_string(mock_success) };
assert!(result.is_some());
assert_eq!(result.unwrap().to_str().unwrap(), "OpenVR Test");
}
#[test]
fn test_get_string_empty() {
let result = unsafe { get_string(mock_empty) };
assert!(result.is_none());
}
#[test]
fn test_get_string_function_called_twice() {
let mut call_count = 0;
let mock_resizing = |output: *mut std::os::raw::c_char, size: u32| -> u32 {
call_count += 1;
let text = "Resize Test";
let c_str = CString::new(text).unwrap();
let bytes = c_str.as_bytes_with_nul();
if size == 0 {
return bytes.len() as u32;
}
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), output as *mut u8, bytes.len());
}
bytes.len() as u32
};
let result = unsafe { get_string(mock_resizing) };
assert!(result.is_some());
assert_eq!(result.unwrap().to_str().unwrap(), "Resize Test");
assert_eq!(call_count, 2);
}
#[test]
fn test_defaultable_pose() {
let pose = TrackedDevicePose::default();
for x in 0..3 {
for y in 0..4 {
assert_eq!(pose.device_to_absolute_tracking()[x][y], 0f32);
}
}
}
}