use std::ffi::CString;
use std::marker::PhantomData;
use std::{mem, ptr, slice};
use openvr_sys as sys;
pub mod event;
use super::*;
pub use self::event::{Event, EventInfo};
impl System {
pub fn recommended_render_target_size(&self) -> (u32, u32) {
let mut result: (mem::MaybeUninit<u32>, mem::MaybeUninit<u32>) =
(mem::MaybeUninit::uninit(), mem::MaybeUninit::uninit());
unsafe {
self.0.GetRecommendedRenderTargetSize.unwrap()(
result.0.as_mut_ptr(),
result.1.as_mut_ptr(),
);
(result.0.assume_init(), result.1.assume_init())
}
}
pub fn projection_matrix(&self, eye: Eye, near_z: f32, far_z: f32) -> [[f32; 4]; 4] {
unsafe { self.0.GetProjectionMatrix.unwrap()(eye as sys::EVREye, near_z, far_z) }.m
}
pub fn projection_raw(&self, eye: Eye) -> RawProjection {
let mut result = mem::MaybeUninit::<RawProjection>::uninit();
unsafe {
self.0.GetProjectionRaw.unwrap()(
eye as sys::EVREye,
&mut (*result.as_mut_ptr()).left,
&mut (*result.as_mut_ptr()).right,
&mut (*result.as_mut_ptr()).top,
&mut (*result.as_mut_ptr()).bottom,
);
result.assume_init()
}
}
pub fn eye_to_head_transform(&self, eye: Eye) -> [[f32; 4]; 3] {
unsafe { (self.0.GetEyeToHeadTransform.unwrap())(eye as sys::EVREye) }.m
}
pub fn time_since_last_vsync(&self) -> Option<(f32, u64)> {
unsafe {
let mut result: (f32, u64) = (0.0, 0);
if self.0.GetTimeSinceLastVsync.unwrap()(&mut result.0, &mut result.1) {
Some(result)
} else {
None
}
}
}
pub fn device_to_absolute_tracking_pose(
&self,
origin: TrackingUniverseOrigin,
predicted_seconds_to_photons_from_now: f32,
) -> TrackedDevicePoses {
unsafe {
let mut result: mem::MaybeUninit<TrackedDevicePoses> = mem::MaybeUninit::uninit();
self.0.GetDeviceToAbsoluteTrackingPose.unwrap()(
origin as sys::ETrackingUniverseOrigin,
predicted_seconds_to_photons_from_now,
result.as_mut_ptr() as *mut _,
MAX_TRACKED_DEVICE_COUNT as u32,
);
result.assume_init()
}
}
pub fn tracked_device_class(&self, index: TrackedDeviceIndex) -> TrackedDeviceClass {
use self::TrackedDeviceClass::*;
match unsafe { self.0.GetTrackedDeviceClass.unwrap()(index.0) } {
sys::ETrackedDeviceClass_TrackedDeviceClass_Invalid => Invalid,
sys::ETrackedDeviceClass_TrackedDeviceClass_HMD => HMD,
sys::ETrackedDeviceClass_TrackedDeviceClass_Controller => Controller,
sys::ETrackedDeviceClass_TrackedDeviceClass_GenericTracker => GenericTracker,
sys::ETrackedDeviceClass_TrackedDeviceClass_TrackingReference => TrackingReference,
sys::ETrackedDeviceClass_TrackedDeviceClass_DisplayRedirect => DisplayRedirect,
_ => Invalid,
}
}
pub fn is_tracked_device_connected(&self, index: TrackedDeviceIndex) -> bool {
unsafe { self.0.IsTrackedDeviceConnected.unwrap()(index.0) }
}
pub fn poll_next_event_with_pose(
&self,
origin: TrackingUniverseOrigin,
) -> Option<(EventInfo, TrackedDevicePose)> {
let mut event = mem::MaybeUninit::uninit();
let mut pose = mem::MaybeUninit::uninit();
if unsafe {
self.0.PollNextEventWithPose.unwrap()(
origin as sys::ETrackingUniverseOrigin,
event.as_mut_ptr(),
mem::size_of_val(&event) as u32,
pose.as_mut_ptr() as *mut _,
)
} {
unsafe { Some((event.assume_init().into(), pose.assume_init())) }
} else {
None
}
}
pub fn poll_next_event(
&self,
) -> Option<EventInfo> {
let mut event = mem::MaybeUninit::uninit();
if unsafe {
self.0.PollNextEvent.unwrap()(
event.as_mut_ptr(),
mem::size_of_val(&event) as u32,
)
} {
unsafe { Some(event.assume_init().into()) }
} else {
None
}
}
pub fn compute_distortion(&self, eye: Eye, u: f32, v: f32) -> Option<DistortionCoordinates> {
let mut coord = mem::MaybeUninit::uninit();
let success = unsafe {
self.0.ComputeDistortion.unwrap()(eye as sys::EVREye, u, v, coord.as_mut_ptr())
};
if !success {
return None;
}
let coord = unsafe { coord.assume_init() };
Some(DistortionCoordinates {
red: coord.rfRed,
blue: coord.rfBlue,
green: coord.rfGreen,
})
}
pub fn tracked_device_index_for_controller_role(
&self,
role: TrackedControllerRole,
) -> Option<TrackedDeviceIndex> {
let x = unsafe {
self.0.GetTrackedDeviceIndexForControllerRole.unwrap()(
role as sys::ETrackedControllerRole,
)
};
if TrackedDeviceIndex(x) == tracked_device_index::INVALID {
None
} else {
Some(tracking::TrackedDeviceIndex(x))
}
}
pub fn get_controller_role_for_tracked_device_index(
&self,
i: TrackedDeviceIndex,
) -> Option<TrackedControllerRole> {
let x = unsafe { self.0.GetControllerRoleForTrackedDeviceIndex.unwrap()(i.0) };
match x {
sys::ETrackedControllerRole_TrackedControllerRole_LeftHand => {
Some(TrackedControllerRole::LeftHand)
},
sys::ETrackedControllerRole_TrackedControllerRole_RightHand => {
Some(TrackedControllerRole::RightHand)
},
sys::ETrackedControllerRole_TrackedControllerRole_Invalid=>{Some(TrackedControllerRole::Invalid)},
sys::ETrackedControllerRole_TrackedControllerRole_OptOut=>{Some(TrackedControllerRole::OptOut)},
sys::ETrackedControllerRole_TrackedControllerRole_Treadmill=>{Some(TrackedControllerRole::Treadmill)},
sys::ETrackedControllerRole_TrackedControllerRole_Stylus=>{Some(TrackedControllerRole::Stylus)},
_=>unreachable!()
}
}
pub fn vulkan_output_device(
&self,
instance: *mut VkInstance_T,
) -> Option<*mut VkPhysicalDevice_T> {
unsafe {
let mut device = mem::MaybeUninit::uninit();
self.0.GetOutputDevice.unwrap()(
device.as_mut_ptr(),
sys::ETextureType_TextureType_Vulkan,
instance,
);
let device = device.assume_init();
if device == 0 {
None
} else {
Some(device as usize as *mut _)
}
}
}
pub fn bool_tracked_device_property(
&self,
device: TrackedDeviceIndex,
property: TrackedDeviceProperty,
) -> Result<bool, TrackedPropertyError> {
let mut error: mem::MaybeUninit<TrackedPropertyError> = mem::MaybeUninit::uninit();
let r = unsafe {
self.0.GetBoolTrackedDeviceProperty.unwrap()(
device.0,
property.0,
error.as_mut_ptr() as *mut sys::TrackedPropertyError,
)
};
let error = unsafe { error.assume_init() };
if error == tracked_property_error::SUCCESS {
Ok(r)
} else {
Err(error)
}
}
pub fn float_tracked_device_property(
&self,
device: TrackedDeviceIndex,
property: TrackedDeviceProperty,
) -> Result<f32, TrackedPropertyError> {
let mut error: mem::MaybeUninit<TrackedPropertyError> = mem::MaybeUninit::uninit();
let r = unsafe {
self.0.GetFloatTrackedDeviceProperty.unwrap()(
device.0,
property.0,
error.as_mut_ptr() as *mut sys::TrackedPropertyError,
)
};
let error = unsafe { error.assume_init() };
if error == tracked_property_error::SUCCESS {
Ok(r)
} else {
Err(error)
}
}
pub fn int32_tracked_device_property(
&self,
device: TrackedDeviceIndex,
property: TrackedDeviceProperty,
) -> Result<i32, TrackedPropertyError> {
let mut error: mem::MaybeUninit<TrackedPropertyError> = mem::MaybeUninit::uninit();
let r = unsafe {
self.0.GetInt32TrackedDeviceProperty.unwrap()(
device.0,
property.0,
error.as_mut_ptr() as *mut sys::TrackedPropertyError,
)
};
let error = unsafe { error.assume_init() };
if error == tracked_property_error::SUCCESS {
Ok(r)
} else {
Err(error)
}
}
pub fn uint64_tracked_device_property(
&self,
device: TrackedDeviceIndex,
property: TrackedDeviceProperty,
) -> Result<u64, TrackedPropertyError> {
let mut error: mem::MaybeUninit<TrackedPropertyError> = mem::MaybeUninit::uninit();
let r = unsafe {
self.0.GetUint64TrackedDeviceProperty.unwrap()(
device.0,
property.0,
error.as_mut_ptr() as *mut sys::TrackedPropertyError,
)
};
let error = unsafe { error.assume_init() };
if error == tracked_property_error::SUCCESS {
Ok(r)
} else {
Err(error)
}
}
pub fn matrix34_tracked_device_property(
&self,
device: TrackedDeviceIndex,
property: TrackedDeviceProperty,
) -> Result<[[f32; 4]; 3], TrackedPropertyError> {
let mut error: mem::MaybeUninit<TrackedPropertyError> = mem::MaybeUninit::uninit();
let r = unsafe {
self.0.GetMatrix34TrackedDeviceProperty.unwrap()(
device.0,
property.0,
error.as_mut_ptr() as *mut sys::TrackedPropertyError,
)
};
let error = unsafe { error.assume_init() };
if error == tracked_property_error::SUCCESS {
Ok(r.m)
} else {
Err(error)
}
}
pub fn string_tracked_device_property(
&self,
device: TrackedDeviceIndex,
property: TrackedDeviceProperty,
) -> Result<CString, TrackedPropertyError> {
unsafe {
let mut error = mem::MaybeUninit::uninit();
let res = get_string(|ptr, n| {
self.0.GetStringTrackedDeviceProperty.unwrap()(
device.0,
property.0,
ptr,
n,
error.as_mut_ptr(),
)
});
res.map_or(Err(TrackedPropertyError(error.assume_init())), Ok)
}
}
pub fn hidden_area_mesh(&self, eye: Eye, ty: HiddenAreaMeshType) -> Option<HiddenAreaMesh> {
let mesh = unsafe {
self.0.GetHiddenAreaMesh.unwrap()(eye as sys::EVREye, ty as sys::EHiddenAreaMeshType)
};
if mesh.pVertexData == ptr::null_mut() {
None
} else {
Some(HiddenAreaMesh {
mesh,
_phantom: PhantomData,
})
}
}
pub fn controller_state(&self, device: TrackedDeviceIndex) -> Option<ControllerState> {
unsafe {
let mut state = mem::MaybeUninit::uninit();
if self.0.GetControllerState.unwrap()(
device.0,
&mut state as *mut _ as *mut _,
mem::size_of_val(&state) as u32,
) {
Some(state.assume_init())
} else {
None
}
}
}
pub fn get_tracked_device_activity_level(
&self,
device: TrackedDeviceIndex,
) -> DeviceActivityLevel {
unsafe { self.0.GetTrackedDeviceActivityLevel.unwrap()(device.0).into() }
}
pub fn controller_state_with_pose(
&self,
origin: TrackingUniverseOrigin,
device: TrackedDeviceIndex,
) -> Option<(ControllerState, TrackedDevicePose)> {
let mut state: mem::MaybeUninit<ControllerState> = mem::MaybeUninit::uninit();
let mut pose = mem::MaybeUninit::uninit();
unsafe {
if self.0.GetControllerStateWithPose.unwrap()(
origin as sys::ETrackingUniverseOrigin,
device.0,
state.as_mut_ptr() as *mut _,
mem::size_of::<ControllerState>() as u32,
pose.as_mut_ptr(),
) {
Some((state.assume_init(), pose.assume_init().into()))
} else {
None
}
}
}
pub fn trigger_haptic_pulse(&self, device: TrackedDeviceIndex, axis: u32, microseconds: u16) {
unsafe { self.0.TriggerHapticPulse.unwrap()(device.0, axis, microseconds) }
}
pub fn acknowledge_quit_exiting(&self) {
unsafe {
self.0.AcknowledgeQuit_Exiting.unwrap()();
}
}
pub fn raw_zero_pose_to_standing_absolute_tracking_pose(&self) -> [[f32; 4]; 3] {
unsafe {
let matrix = self.0.GetRawZeroPoseToStandingAbsoluteTrackingPose.unwrap()();
matrix.m
}
}
pub fn get_tracked_device_property_string(
&self,
index: TrackedDeviceIndex,
prop: sys::ETrackedDeviceProperty,
) -> Result<String, TrackedPropertyError> {
let mut vec: Vec<u8> = Vec::with_capacity(512);
let mut err = TrackedPropertyError(0);
unsafe {
let read = self.0.GetStringTrackedDeviceProperty.unwrap()(
index.0,
prop,
vec.as_mut_ptr().cast(),
vec.capacity() as u32,
(&raw mut err).cast(),
);
if err != system::TrackedPropertyError(0) {
return Err(err);
}
vec.set_len(read as usize);
Ok(String::from_utf8(vec).unwrap())
}
}
pub fn get_tracked_device_property_bool(
&self,
index: TrackedDeviceIndex,
prop: sys::ETrackedDeviceProperty,
) -> Result<bool, TrackedPropertyError> {
let mut err = TrackedPropertyError(0);
unsafe {
let ret =
self.0.GetBoolTrackedDeviceProperty.unwrap()(index.0, prop, (&raw mut err).cast());
if err != system::TrackedPropertyError(0) {
return Err(err);
}
Ok(ret)
}
}
pub fn get_tracked_device_property_f32(
&self,
index: TrackedDeviceIndex,
prop: sys::ETrackedDeviceProperty,
) -> Result<f32, TrackedPropertyError> {
let mut err = TrackedPropertyError(0);
unsafe {
let ret =
self.0.GetFloatTrackedDeviceProperty.unwrap()(index.0, prop, (&raw mut err).cast());
if err != system::TrackedPropertyError(0) {
return Err(err);
}
Ok(ret)
}
}
pub fn get_tracked_device_property_i32(
&self,
index: TrackedDeviceIndex,
prop: sys::ETrackedDeviceProperty,
) -> Result<i32, TrackedPropertyError> {
let mut err = TrackedPropertyError(0);
unsafe {
let ret =
self.0.GetInt32TrackedDeviceProperty.unwrap()(index.0, prop, (&raw mut err).cast());
if err != system::TrackedPropertyError(0) {
return Err(err);
}
Ok(ret)
}
}
pub fn get_tracked_device_property_u64(
&self,
index: TrackedDeviceIndex,
prop: sys::ETrackedDeviceProperty,
) -> Result<u64, TrackedPropertyError> {
let mut err = TrackedPropertyError(0);
unsafe {
let ret =
self.0.GetUint64TrackedDeviceProperty.unwrap()(index.0, prop, (&raw mut err).cast());
if err != system::TrackedPropertyError(0) {
return Err(err);
}
Ok(ret)
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct RawProjection {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
#[derive(Debug, Copy, Clone)]
pub struct DistortionCoordinates {
pub red: [f32; 2],
pub green: [f32; 2],
pub blue: [f32; 2],
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct TrackedPropertyError(sys::TrackedPropertyError);
pub mod tracked_property_error {
use super::{sys, TrackedPropertyError};
pub const SUCCESS: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_Success);
pub const WRONG_DATA_TYPE: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_WrongDataType);
pub const WRONG_DEVICE_CLASS: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_WrongDeviceClass);
pub const BUFFER_TOO_SMALL: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_BufferTooSmall);
pub const UNKNOWN_PROPERTY: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_UnknownProperty);
pub const INVALID_DEVICE: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_InvalidDevice);
pub const COULD_NOT_CONTACT_SERVER: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_CouldNotContactServer);
pub const VALUE_NOT_PROVIDED_BY_DEVICE: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_ValueNotProvidedByDevice);
pub const STRING_EXCEEDS_MAXIMUM_LENGTH: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_StringExceedsMaximumLength);
pub const NOT_YET_AVAILABLE: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_NotYetAvailable);
pub const PERMISSION_DENIED: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_PermissionDenied);
pub const INVALID_OPERATION: TrackedPropertyError =
TrackedPropertyError(sys::ETrackedPropertyError_TrackedProp_InvalidOperation);
}
impl fmt::Debug for TrackedPropertyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl ::std::error::Error for TrackedPropertyError {}
impl fmt::Display for TrackedPropertyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::tracked_property_error::*;
let description = match *self {
SUCCESS => "SUCCESS",
WRONG_DATA_TYPE => "WRONG_DATA_TYPE",
WRONG_DEVICE_CLASS => "WRONG_DEVICE_CLASS",
BUFFER_TOO_SMALL => "BUFFER_TOO_SMALL",
UNKNOWN_PROPERTY => "UNKNOWN_PROPERTY",
INVALID_DEVICE => "INVALID_DEVICE",
COULD_NOT_CONTACT_SERVER => "COULD_NOT_CONTACT_SERVER",
VALUE_NOT_PROVIDED_BY_DEVICE => "VALUE_NOT_PROVIDED_BY_DEVICE",
STRING_EXCEEDS_MAXIMUM_LENGTH => "STRING_EXCEEDS_MAXIMUM_LENGTH",
NOT_YET_AVAILABLE => "NOT_YET_AVAILABLE",
PERMISSION_DENIED => "PERMISSION_DENIED",
INVALID_OPERATION => "INVALID_OPERATION",
_ => "UNKNOWN",
};
f.pad(&description)
}
}
pub enum HiddenAreaMeshType {
Standard = sys::EHiddenAreaMeshType_k_eHiddenAreaMesh_Standard as isize,
Inverse = sys::EHiddenAreaMeshType_k_eHiddenAreaMesh_Inverse as isize,
}
impl Default for HiddenAreaMeshType {
fn default() -> Self {
HiddenAreaMeshType::Standard
}
}
pub struct HiddenAreaMesh<'a> {
mesh: sys::HiddenAreaMesh_t,
_phantom: PhantomData<&'a [[f32; 2]]>,
}
impl<'a> ::std::ops::Deref for HiddenAreaMesh<'a> {
type Target = [[f32; 2]];
fn deref(&self) -> &Self::Target {
unsafe {
slice::from_raw_parts(
&(*self.mesh.pVertexData).v,
self.mesh.unTriangleCount as usize * 3,
)
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum DeviceActivityLevel {
Unknown = openvr_sys::EDeviceActivityLevel_k_EDeviceActivityLevel_Unknown as isize,
Idle = openvr_sys::EDeviceActivityLevel_k_EDeviceActivityLevel_Idle as isize,
UserInteraction =
openvr_sys::EDeviceActivityLevel_k_EDeviceActivityLevel_UserInteraction as isize,
UserInteractionTimeout =
openvr_sys::EDeviceActivityLevel_k_EDeviceActivityLevel_UserInteraction_Timeout as isize,
Standby = openvr_sys::EDeviceActivityLevel_k_EDeviceActivityLevel_Standby as isize,
IdleTimeout = openvr_sys::EDeviceActivityLevel_k_EDeviceActivityLevel_Idle_Timeout as isize,
}
impl From<core::ffi::c_int> for DeviceActivityLevel {
fn from(value: core::ffi::c_int) -> Self {
match value {
-1 => Self::Unknown,
0 => Self::Idle,
1 => Self::UserInteraction,
2 => Self::UserInteractionTimeout,
3 => Self::Standby,
4 => Self::IdleTimeout,
_ => unreachable!("out of range"),
}
}
}