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
//! To get started, construct an `Entry` object.

#![allow(clippy::transmute_ptr_to_ptr)]
use std::os::raw::c_char;

pub use sys::{
    self, Duration, Path, SystemId, Time, Version, CURRENT_API_VERSION, FREQUENCY_UNSPECIFIED,
};

mod generated;
pub use generated::*;
mod entry;
pub use entry::*;
mod instance;
pub use instance::*;
mod session;
pub use session::*;
mod frame_stream;
pub use frame_stream::*;
mod graphics;
pub use graphics::*;
mod swapchain;
pub use swapchain::*;
mod space;
pub use space::*;
mod action_set;
pub use action_set::*;
mod action;
pub use action::*;
mod hand_tracker;
pub use hand_tracker::*;
mod secondary_view;
pub use secondary_view::*;

pub use builder::{
    CompositionLayerBase, CompositionLayerCubeKHR, CompositionLayerCylinderKHR,
    CompositionLayerEquirectKHR, CompositionLayerProjection, CompositionLayerProjectionView,
    CompositionLayerQuad, HapticBase, HapticVibration, SwapchainSubImage,
};

pub type Result<T> = std::result::Result<T, sys::Result>;

// Reserved semantic paths
pub const USER_HAND_LEFT: &str = "/user/hand/left";
pub const USER_HAND_RIGHT: &str = "/user/hand/right";
pub const USER_HEAD: &str = "/user/head";
pub const USER_GAMEPAD: &str = "/user/gamepad";
pub const USER_TREADMILL: &str = "/user/treadmill";

// FFI helpers
fn cvt(x: sys::Result) -> Result<sys::Result> {
    if x.into_raw() >= 0 {
        Ok(x)
    } else {
        Err(x)
    }
}

fn place_cstr(out: &mut [c_char], s: &str) {
    if s.len() + 1 > out.len() {
        panic!(
            "string requires {} > {} bytes (including trailing null)",
            s.len(),
            out.len()
        );
    }
    for (i, o) in s.bytes().zip(out.iter_mut()) {
        *o = i as c_char;
    }
    out[s.len()] = 0;
}

unsafe fn fixed_str(x: &[c_char]) -> &str {
    std::str::from_utf8_unchecked(std::ffi::CStr::from_ptr(x.as_ptr()).to_bytes())
}

/// Includes null for convenience of comparison with C string constants
fn fixed_str_bytes(x: &[c_char]) -> &[u8] {
    let end = x.iter().position(|&x| x == 0).unwrap();
    unsafe { std::mem::transmute(&x[..=end]) }
}

fn get_str(mut getter: impl FnMut(u32, &mut u32, *mut c_char) -> sys::Result) -> Result<String> {
    let mut bytes = get_arr(|x, y, z| getter(x, y, z as _))?;
    // Strip null byte
    bytes.truncate(bytes.len() - 1);
    unsafe { Ok(String::from_utf8_unchecked(bytes)) }
}

fn get_arr<T: Copy>(
    mut getter: impl FnMut(u32, &mut u32, *mut T) -> sys::Result,
) -> Result<Vec<T>> {
    let mut output = 0;
    cvt(getter(0, &mut output, std::ptr::null_mut()))?;
    let mut buffer = Vec::with_capacity(output as usize);
    loop {
        match cvt(getter(
            buffer.capacity() as u32,
            &mut output,
            buffer.as_mut_ptr() as _,
        )) {
            Ok(_) => {
                unsafe {
                    buffer.set_len(output as usize);
                }
                return Ok(buffer);
            }
            Err(sys::Result::ERROR_SIZE_INSUFFICIENT) => {
                buffer.reserve(output as usize - buffer.capacity());
            }
            Err(e) => {
                return Err(e);
            }
        }
    }
}

fn get_arr_init<T: Copy>(
    init: T,
    mut getter: impl FnMut(u32, &mut u32, *mut T) -> sys::Result,
) -> Result<Vec<T>> {
    let mut output = 0;
    cvt(getter(0, &mut output, std::ptr::null_mut()))?;
    let mut buffer = vec![init; output as usize];
    loop {
        match cvt(getter(output, &mut output, buffer.as_mut_ptr() as _)) {
            Ok(_) => {
                buffer.truncate(output as usize);
                return Ok(buffer);
            }
            Err(sys::Result::ERROR_SIZE_INSUFFICIENT) => {
                buffer.resize(output as usize, init);
            }
            Err(e) => {
                return Err(e);
            }
        }
    }
}