1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
extern crate openvr_sys;
#[macro_use]
extern crate lazy_static;
use std::cell::Cell;
use std::ffi::{CStr, CString};
use std::sync::atomic::{AtomicBool, Ordering};
use std::{error, fmt, mem, ptr};
use openvr_sys as sys;
mod tracking;
pub mod chaperone;
pub mod compositor;
pub mod property;
pub mod render_models;
pub mod system;
pub use tracking::*;
pub use sys::VkDevice_T;
pub use sys::VkInstance_T;
pub use sys::VkPhysicalDevice_T;
pub use sys::VkQueue_T;
static INITIALIZED: AtomicBool = AtomicBool::new(false);
pub unsafe fn init(ty: ApplicationType) -> Result<Context, InitError> {
if INITIALIZED.swap(true, Ordering::Acquire) {
panic!("OpenVR has already been initialized!");
}
let mut error = sys::EVRInitError_VRInitError_None;
sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType);
if error != sys::EVRInitError_VRInitError_None {
return Err(InitError(error));
}
if !sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) {
sys::VR_ShutdownInternal();
return Err(InitError(
sys::EVRInitError_VRInitError_Init_InterfaceNotFound,
));
}
Ok(Context { live: AtomicBool::new(true) })
}
pub struct System(&'static sys::VR_IVRSystem_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 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(
sys::EVRInitError_VRInitError_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 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) })
}
}
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(Copy, Clone)]
pub struct InitError(sys::EVRInitError);
impl fmt::Debug for InitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = unsafe { CStr::from_ptr(sys::VR_GetVRInitErrorAsSymbol(self.0)) };
f.pad(
msg.to_str()
.expect("OpenVR init error symbol was not valid UTF-8"),
)
}
}
impl error::Error for InitError {
fn description(&self) -> &str {
let msg = unsafe { CStr::from_ptr(sys::VR_GetVRInitErrorAsEnglishDescription(self.0)) };
msg.to_str()
.expect("OpenVR init error description was not valid UTF-8")
}
}
impl fmt::Display for InitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(error::Error::description(self))
}
}
#[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::new();
storage.reserve_exact(n as usize);
storage.resize(n as usize, mem::uninitialized());
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;
}