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
use std::convert::From;

use openvr_sys as sys;

use Chaperone;

/// Chaperone warning states
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChaperoneCalibrationWarningState {
    Warning,
    BaseStationMayHaveMoved,
    BaseStationRemoved,
    SeatedBoundsInvalid,
    Unknown(u32),
}

impl From<sys::ChaperoneCalibrationState> for ChaperoneCalibrationWarningState {
    fn from(state: sys::ChaperoneCalibrationState) -> Self {
        use self::ChaperoneCalibrationWarningState::*;
        match state {
            sys::ChaperoneCalibrationState_Warning => Warning,
            sys::ChaperoneCalibrationState_Warning_BaseStationMayHaveMoved => {
                BaseStationMayHaveMoved
            }
            sys::ChaperoneCalibrationState_Warning_BaseStationRemoved => BaseStationRemoved,
            sys::ChaperoneCalibrationState_Warning_SeatedBoundsInvalid => SeatedBoundsInvalid,
            _ => Unknown(state as u32),
        }
    }
}

/// Chaperone error states
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChaperoneCalibrationErrorState {
    Error,
    BaseStationUninitialized,
    BaseStationConflict,
    PlayAreaInvalid,
    CollisionBoundsInvalid,
    Unknown(u32),
}

impl From<sys::ChaperoneCalibrationState> for ChaperoneCalibrationErrorState {
    fn from(state: sys::ChaperoneCalibrationState) -> Self {
        use self::ChaperoneCalibrationErrorState::*;
        match state {
            sys::ChaperoneCalibrationState_Error => Error,
            sys::ChaperoneCalibrationState_Error_BaseStationUninitialized => {
                BaseStationUninitialized
            }
            sys::ChaperoneCalibrationState_Error_BaseStationConflict => BaseStationConflict,
            sys::ChaperoneCalibrationState_Error_PlayAreaInvalid => CollisionBoundsInvalid,
            _ => Unknown(state as u32),
        }
    }
}

/// State of Chaperone calibration.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChaperoneCalibrationState {
    Ok,
    Warning(ChaperoneCalibrationWarningState),
    Error(ChaperoneCalibrationErrorState),
    Unknown(u32),
}

impl From<sys::ChaperoneCalibrationState> for ChaperoneCalibrationState {
    fn from(state: sys::ChaperoneCalibrationState) -> Self {
        use self::ChaperoneCalibrationState::*;
        match state {
            1 => Ok,
            100...199 => Warning(ChaperoneCalibrationWarningState::from(state)),
            200...299 => Error(ChaperoneCalibrationErrorState::from(state)),
            _ => Unknown(state as u32),
        }
    }
}

impl Chaperone {
    /// Get the current state of Chaperone calibration.
    /// This state can change at any time during a session due to physical base station changes.
    /// (NOTE: Some of these error codes are never returned as implementation for the error states
    /// is still a work in progress.)
    pub fn get_calibration_state(&self) -> ChaperoneCalibrationState {
        unsafe { self.0.GetCalibrationState.unwrap()() }.into()
    }

    /// Returns the width and depth of the Play Area.
    pub fn get_play_area_size(&self) -> Option<(f32, f32)> {
        let mut x: f32 = 0.0;
        let mut z: f32 = 0.0;
        let is_ok = unsafe { self.0.GetPlayAreaSize.unwrap()(&mut x, &mut z) };
        if is_ok {
            Some((x, z))
        } else {
            None
        }
    }

    /// Returns the 4 corner positions of the PlayArea.
    pub fn get_play_area_rect(&self) -> Option<[[f32; 3]; 4]> {
        let mut r = sys::HmdQuad_t {
            vCorners: [sys::HmdVector3_t { v: [0.0; 3] }; 4],
        };
        let is_ok = unsafe { self.0.GetPlayAreaRect.unwrap()(&mut r) };
        if is_ok {
            Some([
                r.vCorners[0].v,
                r.vCorners[1].v,
                r.vCorners[2].v,
                r.vCorners[3].v,
            ])
        } else {
            None
        }
    }

    /// Are chaperone bounds visible?
    pub fn are_bounds_visible(&self) -> bool {
        unsafe { self.0.AreBoundsVisible.unwrap()() }
    }

    /// Set chaperone bounds to always be visible. If set to false, chaperone
    /// bounds will only show when near the edge.
    ///
    /// Caution: this change is persistent, even after your program exits.
    pub fn force_bounds_visible(&self, force: bool) {
        unsafe { self.0.ForceBoundsVisible.unwrap()(force) };
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn convert_chaperone_state() {
        assert_eq!(
            ChaperoneCalibrationState::from(sys::ChaperoneCalibrationState_OK),
            ChaperoneCalibrationState::Ok
        );
        assert_eq!(
            ChaperoneCalibrationState::from(sys::ChaperoneCalibrationState_Warning),
            ChaperoneCalibrationState::Warning(ChaperoneCalibrationWarningState::Warning)
        );
        assert_eq!(
            ChaperoneCalibrationState::from(199),
            ChaperoneCalibrationState::Warning(ChaperoneCalibrationWarningState::Unknown(199))
        );
        assert_eq!(
            ChaperoneCalibrationState::from(
                sys::ChaperoneCalibrationState_Warning_BaseStationRemoved
            ),
            ChaperoneCalibrationState::Warning(
                ChaperoneCalibrationWarningState::BaseStationRemoved
            )
        );
        assert_eq!(
            ChaperoneCalibrationState::from(sys::ChaperoneCalibrationState_Error),
            ChaperoneCalibrationState::Error(ChaperoneCalibrationErrorState::Error)
        );
        assert_eq!(
            ChaperoneCalibrationState::from(
                sys::ChaperoneCalibrationState_Error_BaseStationUninitialized
            ),
            ChaperoneCalibrationState::Error(
                ChaperoneCalibrationErrorState::BaseStationUninitialized
            )
        );
        assert_eq!(
            ChaperoneCalibrationState::from(299),
            ChaperoneCalibrationState::Error(ChaperoneCalibrationErrorState::Unknown(299))
        );
        assert_eq!(
            ChaperoneCalibrationState::from(2),
            ChaperoneCalibrationState::Unknown(2)
        );
    }
}