use crate::{
StereoKitError,
maths::{Bool32T, Vec3, lerp},
sk::DisplayBlend,
system::TextContext,
ui::IdHashT,
};
use std::{
ffi::{CStr, CString, c_char, c_void},
fmt::Display,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
path::Path,
ptr::NonNull,
};
#[repr(C)]
#[derive(Debug, Copy, Clone, Default)]
pub struct Color128 {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl PartialEq for Color128 {
fn eq(&self, other: &Self) -> bool {
(self.r - other.r).abs() < 0.000001
&& (self.g - other.g).abs() < 0.000001
&& (self.b - other.b).abs() < 0.000001
&& (self.a - other.a).abs() < 0.000001
}
}
impl From<[f32; 4]> for Color128 {
fn from(s: [f32; 4]) -> Self {
Self::new(s[0], s[1], s[2], s[3])
}
}
impl From<Color32> for Color128 {
fn from(a: Color32) -> Self {
Self::new(a.r as f32 / 255.0, a.g as f32 / 255.0, a.b as f32 / 255.0, a.a as f32 / 255.0)
}
}
unsafe extern "C" {
pub fn color_hsv(hue: f32, saturation: f32, value: f32, transparency: f32) -> Color128;
pub fn color_to_hsv(color: *const Color128) -> Vec3;
pub fn color_lab(l: f32, a: f32, b: f32, transparency: f32) -> Color128;
pub fn color_to_lab(color: *const Color128) -> Vec3;
pub fn color_to_linear(srgb_gamma_correct: Color128) -> Color128;
pub fn color_to_gamma(srgb_linear: Color128) -> Color128;
}
impl Color128 {
pub const BLACK: Color128 = Color128 { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
pub const BLACK_TRANSPARENT: Color128 = Color128 { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
pub const WHITE: Color128 = Color128 { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
#[deprecated = "Use Color128::rgb instead"]
pub const fn new_rgb(r: f32, g: f32, b: f32) -> Self {
Self::new(r, g, b, 1.0)
}
pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
Self::new(r, g, b, 1.0)
}
pub fn hsv(hue: f32, saturation: f32, value: f32, transparency: f32) -> Color128 {
unsafe { color_hsv(hue, saturation, value, transparency) }
}
pub fn hsv_vec3(vec: impl Into<Vec3>, transparency: f32) -> Self {
let vec = vec.into();
unsafe { color_hsv(vec.x, vec.y, vec.z, transparency) }
}
pub fn lab(l: f32, a: f32, b: f32, transparency: f32) -> Self {
unsafe { color_lab(l, a, b, transparency) }
}
pub fn lab_vec3(vec: Vec3, transparency: f32) -> Self {
unsafe { color_lab(vec.x, vec.y, vec.z, transparency) }
}
pub fn hex(hex_value: u32) -> Self {
Self {
r: (hex_value >> 24) as f32 / 255.0,
g: ((hex_value >> 16) & 0x000000FF) as f32 / 255.0,
b: ((hex_value >> 8) & 0x000000FF) as f32 / 255.0,
a: (hex_value & 0x000000FF) as f32 / 255.0,
}
}
pub fn lerp(a: Color128, b: Color128, t: f32) -> Self {
Self { r: lerp(a.r, b.r, t), g: lerp(a.g, b.g, t), b: lerp(a.b, b.b, t), a: lerp(a.a, b.a, t) }
}
pub fn to_linear(&self) -> Self {
unsafe { color_to_linear(*self) }
}
pub fn to_gamma(&self) -> Self {
unsafe { color_to_gamma(*self) }
}
pub fn to_hsv(&self) -> Vec3 {
unsafe { color_to_hsv(self) }
}
pub fn to_lab(&self) -> Vec3 {
unsafe { color_to_lab(self) }
}
}
impl Display for Color128 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "r:{}, g:{}, b:{}, a:{}", self.r, self.g, self.b, self.a)
}
}
impl Add for Color128 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Color128 { r: self.r + rhs.r, g: self.g + rhs.g, b: self.b + rhs.b, a: self.a + rhs.a }
}
}
impl AddAssign for Color128 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
self.a += rhs.a;
}
}
impl Sub for Color128 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Color128 { r: self.r - rhs.r, g: self.g - rhs.g, b: self.b - rhs.b, a: self.a - rhs.a }
}
}
impl SubAssign for Color128 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.r -= rhs.r;
self.g -= rhs.g;
self.b -= rhs.b;
self.a -= rhs.a;
}
}
impl Div<Color128> for Color128 {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Self { r: self.r.div(rhs.r), g: self.g.div(rhs.g), b: self.b.div(rhs.b), a: self.a.div(rhs.a) }
}
}
impl DivAssign<Color128> for Color128 {
#[inline]
fn div_assign(&mut self, rhs: Self) {
self.r.div_assign(rhs.r);
self.g.div_assign(rhs.g);
self.b.div_assign(rhs.b);
self.a.div_assign(rhs.a);
}
}
impl Div<f32> for Color128 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self { r: self.r.div(rhs), g: self.g.div(rhs), b: self.b.div(rhs), a: self.a.div(rhs) }
}
}
impl DivAssign<f32> for Color128 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.r.div_assign(rhs);
self.g.div_assign(rhs);
self.b.div_assign(rhs);
self.a.div_assign(rhs);
}
}
impl Div<Color128> for f32 {
type Output = Color128;
#[inline]
fn div(self, rhs: Color128) -> Self::Output {
Self::Output { r: self.div(rhs.r), g: self.div(rhs.g), b: self.div(rhs.b), a: self.div(rhs.a) }
}
}
impl Mul<Color128> for Color128 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self { r: self.r.mul(rhs.r), g: self.g.mul(rhs.g), b: self.b.mul(rhs.b), a: self.a.mul(rhs.a) }
}
}
impl MulAssign<Color128> for Color128 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.r.mul_assign(rhs.r);
self.g.mul_assign(rhs.g);
self.b.mul_assign(rhs.b);
self.a.mul_assign(rhs.a);
}
}
impl Mul<f32> for Color128 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self { r: self.r.mul(rhs), g: self.g.mul(rhs), b: self.b.mul(rhs), a: self.a.mul(rhs) }
}
}
impl MulAssign<f32> for Color128 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.r.mul_assign(rhs);
self.g.mul_assign(rhs);
self.b.mul_assign(rhs);
self.a.mul_assign(rhs);
}
}
impl Mul<Color128> for f32 {
type Output = Color128;
#[inline]
fn mul(self, rhs: Color128) -> Self::Output {
Self::Output { r: self.mul(rhs.r), g: self.mul(rhs.g), b: self.mul(rhs.b), a: self.mul(rhs.a) }
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub struct Color32 {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl From<Color128> for Color32 {
fn from(a: Color128) -> Self {
Self::new((a.r * 255.0) as u8, (a.g * 255.0) as u8, (a.b * 255.0) as u8, (a.a * 255.0) as u8)
}
}
impl From<[u8; 4]> for Color32 {
fn from(s: [u8; 4]) -> Self {
Self::new(s[0], s[1], s[2], s[3])
}
}
impl Color32 {
pub const BLACK: Color32 = Color32 { r: 0, g: 0, b: 0, a: 255 };
pub const BLACK_TRANSPARENT: Color32 = Color32 { r: 0, g: 0, b: 0, a: 0 };
pub const WHITE: Color32 = Color32 { r: 255, g: 255, b: 255, a: 255 };
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 255 }
}
#[deprecated = "Use Color32::rgb instead"]
pub const fn new_rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 255 }
}
pub fn hex(hex_value: u32) -> Self {
Self {
r: (hex_value >> 24) as u8,
g: ((hex_value >> 16) & 0x000000FF) as u8,
b: ((hex_value >> 8) & 0x000000FF) as u8,
a: (hex_value & 0x000000FF) as u8,
}
}
}
impl Display for Color32 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "r:{}, g:{}, b:{}, a:{}", self.r, self.g, self.b, self.a)
}
}
pub mod named_colors {
use super::Color32;
pub const BLACK: Color32 = Color32::rgb(0, 0, 0);
pub const SILVER: Color32 = Color32::rgb(192, 192, 192);
pub const GRAY: Color32 = Color32::rgb(128, 128, 128);
pub const WHITE: Color32 = Color32::rgb(255, 255, 255);
pub const MAROON: Color32 = Color32::rgb(128, 0, 0);
pub const RED: Color32 = Color32::rgb(255, 0, 0);
pub const PURPLE: Color32 = Color32::rgb(128, 0, 128);
pub const FUCHSIA: Color32 = Color32::rgb(255, 0, 255);
pub const GREEN: Color32 = Color32::rgb(0, 128, 0);
pub const LIME: Color32 = Color32::rgb(0, 255, 0);
pub const OLIVE: Color32 = Color32::rgb(128, 128, 0);
pub const YELLOW: Color32 = Color32::rgb(255, 255, 0);
pub const NAVY: Color32 = Color32::rgb(0, 0, 128);
pub const BLUE: Color32 = Color32::rgb(0, 0, 255);
pub const TEAL: Color32 = Color32::rgb(0, 128, 128);
pub const AQUA: Color32 = Color32::rgb(0, 255, 255);
pub const ALICE_BLUE: Color32 = Color32::rgb(240, 248, 255);
pub const ANTIQUE_WHITE: Color32 = Color32::rgb(250, 235, 215);
pub const AQUAMARINE: Color32 = Color32::rgb(127, 255, 212);
pub const AZURE: Color32 = Color32::rgb(240, 255, 255);
pub const BEIGE: Color32 = Color32::rgb(255, 228, 196);
pub const BISQUE: Color32 = Color32::rgb(255, 228, 196);
pub const BLANCHED_ALMOND: Color32 = Color32::rgb(255, 235, 205);
pub const BLUE_VIOLET: Color32 = Color32::rgb(138, 43, 226);
pub const BROWN: Color32 = Color32::rgb(165, 42, 42);
pub const BURLY_WOOD: Color32 = Color32::rgb(222, 184, 135);
pub const CADET_BLUE: Color32 = Color32::rgb(95, 158, 160);
pub const CHARTREUSE: Color32 = Color32::rgb(127, 255, 0);
pub const CHOCOLATE: Color32 = Color32::rgb(210, 105, 30);
pub const CORAL: Color32 = Color32::rgb(255, 127, 80);
pub const CORNFLOWER_BLUE: Color32 = Color32::rgb(100, 149, 237);
pub const CORN_SILK: Color32 = Color32::rgb(255, 248, 220);
pub const CRIMSON: Color32 = Color32::rgb(220, 20, 60);
pub const CYAN: Color32 = Color32::rgb(0, 255, 255);
pub const DARK_BLUE: Color32 = Color32::rgb(0, 0, 139);
pub const DARK_CYAN: Color32 = Color32::rgb(0, 139, 139);
pub const DARK_GOLDEN_ROD: Color32 = Color32::rgb(184, 134, 11);
pub const DARK_GRAY: Color32 = Color32::rgb(169, 169, 169);
pub const DARK_GREEN: Color32 = Color32::rgb(0, 100, 0);
pub const DARK_GREY: Color32 = Color32::rgb(169, 169, 169);
pub const DARK_KHAKI: Color32 = Color32::rgb(189, 183, 107);
pub const DARK_MAGENTA: Color32 = Color32::rgb(139, 0, 139);
pub const DARK_OLIVE_GREEN: Color32 = Color32::rgb(85, 107, 47);
pub const DARK_ORANGE: Color32 = Color32::rgb(255, 140, 0);
pub const DARK_ORCHID: Color32 = Color32::rgb(153, 50, 204);
pub const DARK_RED: Color32 = Color32::rgb(139, 0, 0);
pub const DARK_SALMON: Color32 = Color32::rgb(233, 150, 122);
pub const DARK_SEA_GREEN: Color32 = Color32::rgb(143, 188, 143);
pub const DARK_SLATE_BLUE: Color32 = Color32::rgb(72, 61, 139);
pub const DARK_SLATE_GRAY: Color32 = Color32::rgb(47, 79, 79);
pub const DARK_SLATE_GREY: Color32 = Color32::rgb(47, 79, 79);
pub const DARK_TURQUOISE: Color32 = Color32::rgb(0, 206, 209);
pub const DARK_VIOLET: Color32 = Color32::rgb(148, 0, 211);
pub const DEEP_PINK: Color32 = Color32::rgb(255, 20, 147);
pub const DEEP_SKY_BLUE: Color32 = Color32::rgb(0, 191, 255);
pub const DIM_GRAY: Color32 = Color32::rgb(105, 105, 105);
pub const DIM_GREY: Color32 = Color32::rgb(105, 105, 105);
pub const DOGER_BLUE: Color32 = Color32::rgb(30, 144, 255);
pub const FIRE_BRICK: Color32 = Color32::rgb(178, 34, 34);
pub const FLORAL_WHITE: Color32 = Color32::rgb(255, 250, 240);
pub const FOREST_GREEN: Color32 = Color32::rgb(34, 139, 34);
pub const GAINSBORO: Color32 = Color32::rgb(220, 220, 220);
pub const GHOST_WHITE: Color32 = Color32::rgb(248, 248, 255);
pub const GOLD: Color32 = Color32::rgb(255, 215, 0);
pub const GOLDENROD: Color32 = Color32::rgb(218, 165, 32);
pub const GREEN_YELLOW: Color32 = Color32::rgb(173, 255, 47);
pub const GREY: Color32 = Color32::rgb(128, 128, 128);
pub const HONEYDEW: Color32 = Color32::rgb(240, 255, 240);
pub const HOT_PINK: Color32 = Color32::rgb(255, 105, 180);
pub const INDIAN_RED: Color32 = Color32::rgb(205, 92, 92);
pub const INDIGO: Color32 = Color32::rgb(75, 0, 130);
pub const IVORY: Color32 = Color32::rgb(255, 255, 240);
pub const KHAKI: Color32 = Color32::rgb(240, 230, 140);
pub const LAVENDER: Color32 = Color32::rgb(230, 230, 250);
pub const LAVENDER_BLUSH: Color32 = Color32::rgb(255, 240, 245);
pub const LAWN_GREEN: Color32 = Color32::rgb(124, 242, 0);
pub const LEMON_CHIFFON: Color32 = Color32::rgb(255, 250, 205);
pub const LIGHT_BLUE: Color32 = Color32::rgb(173, 216, 230);
pub const LIGHT_CORAL: Color32 = Color32::rgb(240, 128, 128);
pub const LIGHT_CYAN: Color32 = Color32::rgb(224, 255, 255);
pub const LIGHT_GOLDENROD_YELLOW: Color32 = Color32::rgb(250, 250, 210);
pub const LIGHT_GRAY: Color32 = Color32::rgb(211, 211, 211);
pub const LIGHT_GREEN: Color32 = Color32::rgb(144, 238, 144);
pub const LIGHT_GREY: Color32 = Color32::rgb(211, 211, 211);
pub const LIGHT_PINK: Color32 = Color32::rgb(255, 182, 193);
pub const LIGHT_SALMON: Color32 = Color32::rgb(255, 160, 122);
pub const LIGHT_SEA_GREEN: Color32 = Color32::rgb(32, 178, 170);
pub const LIGHT_SKY_BLUE: Color32 = Color32::rgb(135, 206, 250);
pub const LIGHT_SLATE_GRAY: Color32 = Color32::rgb(119, 136, 153);
pub const LIGHT_STEEL_BLUE: Color32 = Color32::rgb(176, 196, 222);
pub const LIGHT_YELLOW: Color32 = Color32::rgb(255, 255, 224);
pub const LIME_GREEN: Color32 = Color32::rgb(50, 205, 50);
pub const LINEN: Color32 = Color32::rgb(250, 240, 230);
pub const MAGENTA: Color32 = Color32::rgb(255, 0, 255);
pub const MEDIUM_AQUAMARINE: Color32 = Color32::rgb(102, 205, 170);
pub const MEDIUM_BLUE: Color32 = Color32::rgb(0, 0, 205);
pub const MEDIUM_ORCHID: Color32 = Color32::rgb(186, 85, 211);
pub const MEDIUM_PURPLE: Color32 = Color32::rgb(147, 112, 219);
pub const MEDIUM_SEA_GREEN: Color32 = Color32::rgb(60, 179, 113);
pub const MEDIUM_SLATE_BLUE: Color32 = Color32::rgb(123, 104, 238);
pub const MEDIUM_SPRING_GREEN: Color32 = Color32::rgb(0, 250, 154);
pub const MEDIUM_TURQUOISE: Color32 = Color32::rgb(72, 209, 204);
pub const MEDIUM_VIOLET_RED: Color32 = Color32::rgb(199, 21, 133);
pub const MIDNIGHT_BLUE: Color32 = Color32::rgb(25, 25, 112);
pub const MINT_CREAM: Color32 = Color32::rgb(245, 255, 250);
pub const MISTY_ROSE: Color32 = Color32::rgb(255, 228, 225);
pub const MOCCASIN: Color32 = Color32::rgb(255, 228, 181);
pub const NAVAJO_WHITE: Color32 = Color32::rgb(255, 222, 173);
pub const OLD_LACE: Color32 = Color32::rgb(253, 245, 230);
pub const OLIVE_DRAB: Color32 = Color32::rgb(107, 142, 35);
pub const ORANGE: Color32 = Color32::rgb(255, 165, 0);
pub const ORANGE_RED: Color32 = Color32::rgb(255, 69, 0);
pub const ORCHID: Color32 = Color32::rgb(218, 112, 214);
pub const PALE_GOLDEN_ROD: Color32 = Color32::rgb(238, 232, 170);
pub const PALE_GREEN: Color32 = Color32::rgb(152, 251, 152);
pub const PALE_TURQUOISE: Color32 = Color32::rgb(175, 238, 238);
pub const PALE_VIOLET_RED: Color32 = Color32::rgb(219, 112, 147);
pub const PAPAYAWHIP: Color32 = Color32::rgb(255, 239, 213);
pub const PEACH_PUFF: Color32 = Color32::rgb(255, 218, 185);
pub const PERU: Color32 = Color32::rgb(205, 133, 63);
pub const PINK: Color32 = Color32::rgb(255, 192, 203);
pub const PLUM: Color32 = Color32::rgb(221, 160, 221);
pub const POWDER_BLUE: Color32 = Color32::rgb(176, 224, 230);
pub const ROSY_BROWN: Color32 = Color32::rgb(188, 143, 143);
pub const ROYAL_BLUE: Color32 = Color32::rgb(65, 105, 225);
pub const SADDLE_BROWN: Color32 = Color32::rgb(139, 69, 19);
pub const SALMON: Color32 = Color32::rgb(250, 128, 114);
pub const SANDY_BROWN: Color32 = Color32::rgb(244, 164, 96);
pub const SEA_GREEN: Color32 = Color32::rgb(46, 139, 87);
pub const SEA_SHELL: Color32 = Color32::rgb(255, 245, 238);
pub const SIENNA: Color32 = Color32::rgb(160, 82, 45);
pub const SKY_BLUE: Color32 = Color32::rgb(135, 206, 235);
pub const SLATE_BLUE: Color32 = Color32::rgb(106, 90, 205);
pub const SLATE_GRAY: Color32 = Color32::rgb(112, 128, 144);
pub const SLATE_GREY: Color32 = Color32::rgb(112, 128, 144);
pub const SNOW: Color32 = Color32::rgb(255, 250, 250);
pub const SPRING_GREEN: Color32 = Color32::rgb(0, 255, 127);
pub const STEEL_BLUE: Color32 = Color32::rgb(70, 130, 180);
pub const TAN: Color32 = Color32::rgb(210, 180, 140);
pub const THISTLE: Color32 = Color32::rgb(216, 191, 216);
pub const TOMATO: Color32 = Color32::rgb(255, 99, 71);
pub const TURQUOISE: Color32 = Color32::rgb(64, 224, 208);
pub const VIOLET: Color32 = Color32::rgb(238, 130, 238);
pub const WHEAT: Color32 = Color32::rgb(245, 222, 179);
pub const WHITE_SMOKE: Color32 = Color32::rgb(245, 245, 245);
pub const YELLOW_GREEN: Color32 = Color32::rgb(154, 205, 50);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u32)]
pub enum DeviceTracking {
None = 0,
Dof3 = 1,
Dof6 = 2,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u32)]
pub enum DisplayType {
None = 0,
Stereo = 1,
Flatscreen = 2,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct FovInfo {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
pub struct Device;
unsafe extern "C" {
pub fn device_display_get_type() -> DisplayType;
pub fn device_display_get_blend() -> DisplayBlend;
pub fn device_display_set_blend(blend: DisplayBlend) -> Bool32T;
pub fn device_display_valid_blend(blend: DisplayBlend) -> Bool32T;
pub fn device_display_get_refresh_rate() -> f32;
pub fn device_display_get_width() -> i32;
pub fn device_display_get_height() -> i32;
pub fn device_display_get_fov() -> FovInfo;
pub fn device_get_tracking() -> DeviceTracking;
pub fn device_get_name() -> *const c_char;
pub fn device_get_runtime() -> *const c_char;
pub fn device_get_gpu() -> *const c_char;
pub fn device_has_eye_gaze() -> Bool32T;
pub fn device_has_hand_tracking() -> Bool32T;
}
impl Device {
pub fn display_blend(blend: DisplayBlend) -> bool {
unsafe { device_display_set_blend(blend) != 0 }
}
pub fn get_display_blend() -> DisplayBlend {
unsafe { device_display_get_blend() }
}
pub fn get_display_type() -> DisplayType {
unsafe { device_display_get_type() }
}
pub fn get_runtime<'a>() -> Result<&'a str, StereoKitError> {
unsafe { CStr::from_ptr(device_get_runtime()) }
.to_str()
.map_err(|e| StereoKitError::CStrError(e.to_string()))
}
pub fn get_gpu<'a>() -> Result<&'a str, StereoKitError> {
unsafe { CStr::from_ptr(device_get_gpu()) }
.to_str()
.map_err(|e| StereoKitError::CStrError(e.to_string()))
}
pub fn has_eye_gaze() -> bool {
unsafe { device_has_eye_gaze() != 0 }
}
pub fn has_hand_tracking() -> bool {
unsafe { device_has_hand_tracking() != 0 }
}
pub fn get_name<'a>() -> Result<&'a str, StereoKitError> {
unsafe { CStr::from_ptr(device_get_name()) }
.to_str()
.map_err(|e| StereoKitError::CStrError(e.to_string()))
}
pub fn get_tracking() -> DeviceTracking {
unsafe { device_get_tracking() }
}
pub fn valid_blend(blend: DisplayBlend) -> bool {
unsafe { device_display_valid_blend(blend) != 0 }
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
pub struct GradientKey {
pub color: Color128,
pub position: f32,
}
impl GradientKey {
pub fn new(color_linear: impl Into<Color128>, position: f32) -> Self {
Self { color: color_linear.into(), position }
}
}
pub struct Gradient(pub NonNull<_GradientT>);
impl Drop for Gradient {
fn drop(&mut self) {
unsafe { gradient_destroy(self.0.as_ptr()) };
}
}
impl AsRef<Gradient> for Gradient {
fn as_ref(&self) -> &Gradient {
self
}
}
#[repr(C)]
#[derive(Debug)]
pub struct _GradientT {
_unused: [u8; 0],
}
pub type GradientT = *mut _GradientT;
unsafe extern "C" {
pub fn gradient_create() -> GradientT;
pub fn gradient_create_keys(in_arr_keys: *const GradientKey, count: i32) -> GradientT;
pub fn gradient_add(gradient: GradientT, color_linear: Color128, position: f32);
pub fn gradient_set(gradient: GradientT, index: i32, color_linear: Color128, position: f32);
pub fn gradient_remove(gradient: GradientT, index: i32);
pub fn gradient_count(gradient: GradientT) -> i32;
pub fn gradient_get(gradient: GradientT, at: f32) -> Color128;
pub fn gradient_get32(gradient: GradientT, at: f32) -> Color32;
pub fn gradient_destroy(gradient: GradientT);
}
impl Gradient {
pub fn new(keys: Option<&[GradientKey]>) -> Self {
match keys {
Some(keys) => {
Gradient(NonNull::new(unsafe { gradient_create_keys(keys.as_ptr(), keys.len() as i32) }).unwrap())
}
None => Gradient(NonNull::new(unsafe { gradient_create() }).unwrap()),
}
}
pub fn add(&mut self, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
unsafe { gradient_add(self.0.as_ptr(), color_linear.into(), position) };
self
}
pub fn set(&mut self, index: i32, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
if index < 0 || index >= self.get_count() {
return self;
}
unsafe { gradient_set(self.0.as_ptr(), index, color_linear.into(), position) };
self
}
pub fn remove(&mut self, index: i32) -> &mut Self {
if index < 0 || index >= self.get_count() {
return self;
}
unsafe { gradient_remove(self.0.as_ptr(), index) };
self
}
pub fn get_count(&self) -> i32 {
unsafe { gradient_count(self.0.as_ptr()) }
}
pub fn get(&self, at: f32) -> Color128 {
unsafe { gradient_get(self.0.as_ptr(), at) }
}
pub fn get32(&self, at: f32) -> Color32 {
unsafe { gradient_get32(self.0.as_ptr(), at) }
}
}
#[derive(Debug, Default, Copy, Clone)]
#[repr(C)]
pub struct FileFilter {
ext: [c_char; 32usize],
}
impl FileFilter {
pub fn new(str: impl AsRef<str>) -> Self {
let mut value: [c_char; 32usize] = [0; 32usize];
let c_array = CString::new(str.as_ref()).unwrap();
let c_array = c_array.as_bytes_with_nul();
let mut c_array_iter = c_array.iter().map(|v| *v as c_char);
value[0..c_array.len()].fill_with(|| c_array_iter.next().unwrap());
Self { ext: value }
}
}
pub struct Hash;
unsafe extern "C" {
pub fn hash_string(str_utf8: *const c_char) -> IdHashT;
pub fn hash_string_with(str_utf8: *const c_char, root: IdHashT) -> IdHashT;
pub fn hash_int(val: i32) -> IdHashT;
pub fn hash_int_with(val: i32, root: IdHashT) -> IdHashT;
}
impl Hash {
pub fn string(str: impl AsRef<str>) -> IdHashT {
let c_str = CString::new(str.as_ref()).unwrap();
unsafe { hash_string(c_str.as_ptr()) }
}
pub fn string_with(str: impl AsRef<str>, root: IdHashT) -> IdHashT {
let c_str = CString::new(str.as_ref()).unwrap();
unsafe { hash_string_with(c_str.as_ptr(), root) }
}
pub fn int(val: i32) -> IdHashT {
unsafe { hash_int(val) }
}
pub fn int_with(int: i32, root: IdHashT) -> IdHashT {
unsafe { hash_int_with(int, root) }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u32)]
pub enum PickerMode {
Open = 0,
Save = 1,
}
pub struct Platform;
unsafe extern "C" {
pub fn platform_file_picker(
mode: PickerMode,
callback_data: *mut c_void,
picker_callback: ::std::option::Option<
unsafe extern "C" fn(callback_data: *mut c_void, confirmed: Bool32T, filename: *const c_char),
>,
filters: *const FileFilter,
filter_count: i32,
);
pub fn platform_file_picker_sz(
mode: PickerMode,
callback_data: *mut c_void,
picker_callback_sz: ::std::option::Option<
unsafe extern "C" fn(
callback_data: *mut c_void,
confirmed: Bool32T,
filename_ptr: *const c_char,
filename_length: i32,
),
>,
in_arr_filters: *const FileFilter,
filter_count: i32,
);
pub fn platform_file_picker_close();
pub fn platform_file_picker_visible() -> Bool32T;
pub fn platform_read_file(
filename_utf8: *const c_char,
out_data: *mut *mut c_void,
out_size: *mut usize,
) -> Bool32T;
pub fn platform_write_file(filename_utf8: *const c_char, data: *mut c_void, size: usize) -> Bool32T;
pub fn platform_write_file_text(filename_utf8: *const c_char, text_utf8: *const c_char) -> Bool32T;
pub fn platform_keyboard_get_force_fallback() -> Bool32T;
pub fn platform_keyboard_set_force_fallback(force_fallback: Bool32T);
pub fn platform_keyboard_show(visible: Bool32T, type_: TextContext);
pub fn platform_keyboard_visible() -> Bool32T;
pub fn platform_keyboard_set_layout(
type_: TextContext,
keyboard_layout: *const *const c_char,
layouts_num: i32,
) -> Bool32T;
}
unsafe extern "C" fn fp_trampoline<FS: FnMut(&str), FC: FnMut()>(
user_data: *mut c_void,
confirmed: Bool32T,
filename: *const c_char,
) {
let data = unsafe { &mut *(user_data as *mut (&mut FS, &mut FC)) };
let (update, cancel) = data;
if confirmed != 0 {
let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
update(c_str)
} else {
cancel()
}
}
unsafe extern "C" fn fp_sz_trampoline<F: FnMut(bool, &str)>(
user_data: *mut c_void,
confirmed: Bool32T,
filename: *const c_char,
filename_length: i32,
) {
let closure = unsafe { &mut *(user_data as *mut &mut F) };
if confirmed != 0 && filename_length > 0 {
let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
closure(true, c_str)
} else {
let c_str = "";
closure(false, c_str)
}
}
impl Platform {
pub fn force_fallback_keyboard(force_fallback: bool) {
unsafe { platform_keyboard_set_force_fallback(force_fallback as Bool32T) }
}
pub fn file_picker<FS: FnMut(&str), FC: FnMut()>(
mode: PickerMode,
mut on_select_file: FS,
mut on_cancel: FC,
filters: &[impl AsRef<str>],
) {
let mut c_filters = Vec::new();
for filter in filters {
c_filters.push(FileFilter::new(filter));
}
let mut closure = (&mut on_select_file, &mut on_cancel);
unsafe {
platform_file_picker(
mode,
&mut closure as *mut _ as *mut c_void,
Some(fp_trampoline::<FS, FC>),
c_filters.as_slice().as_ptr(),
c_filters.len() as i32,
)
}
}
pub fn file_picker_sz<F: FnMut(bool, &str)>(mode: PickerMode, mut on_complete: F, filters: &[impl AsRef<str>]) {
let mut c_filters = Vec::new();
for filter in filters {
c_filters.push(FileFilter::new(filter));
}
let mut closure = &mut on_complete;
unsafe {
platform_file_picker_sz(
mode,
&mut closure as *mut _ as *mut c_void,
Some(fp_sz_trampoline::<F>),
c_filters.as_slice().as_ptr(),
c_filters.len() as i32,
)
}
}
pub fn file_picker_close() {
unsafe { platform_file_picker_close() }
}
pub fn keyboard_show(show: bool, input_type: TextContext) {
unsafe { platform_keyboard_show(show as Bool32T, input_type) }
}
pub fn keyboard_set_layout(keyboard_type: TextContext, keyboard_layouts: &Vec<&str>) -> bool {
let mut keyboard_layouts_c = vec![];
for str in keyboard_layouts {
let c_str = CString::new(*str).unwrap().into_raw() as *const c_char;
keyboard_layouts_c.push(c_str);
}
unsafe {
platform_keyboard_set_layout(
keyboard_type,
keyboard_layouts_c.as_slice().as_ptr(),
keyboard_layouts_c.len() as i32,
) != 0
}
}
pub fn read_file_text<'a>(filename: impl AsRef<Path>) -> Result<&'a str, StereoKitError> {
let path_buf = filename.as_ref().to_path_buf();
let c_str = CString::new(
path_buf
.clone()
.to_str() .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
)?;
let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
let mut len = 0usize;
let len_ptr: *mut usize = &mut len;
if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
unsafe { CStr::from_ptr(*out_data as *const c_char) }
.to_str()
.map_err(|e| StereoKitError::ReadFileError(path_buf.clone(), e.to_string()))
} else {
Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
}
}
pub fn read_file<'a>(filename: impl AsRef<Path>) -> Result<&'a [u8], StereoKitError> {
let path_buf = filename.as_ref().to_path_buf();
let c_str = CString::new(
path_buf
.clone()
.to_str() .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
)?;
let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
let mut len = 0usize;
let len_ptr: *mut usize = &mut len;
if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
Ok(unsafe { std::slice::from_raw_parts(*out_data as *const u8, len) })
} else {
Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
}
}
pub fn write_file_text<S: AsRef<str>>(filename: impl AsRef<Path>, text: S) -> Result<bool, StereoKitError> {
let path_buf = filename.as_ref().to_path_buf();
let c_str = CString::new(
path_buf
.clone()
.to_str() .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
)?;
let in_data = CString::new(text.as_ref())?.into_raw() as *const c_char;
if unsafe { platform_write_file_text(c_str.as_ptr(), in_data) != 0 } {
Ok(true)
} else {
Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
}
}
pub fn write_file(filename: impl AsRef<Path>, data: &[u8]) -> Result<bool, StereoKitError> {
let path_buf = filename.as_ref().to_path_buf();
let c_str = CString::new(
path_buf
.clone()
.to_str() .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
)?;
if unsafe { platform_write_file(c_str.as_ptr(), data.as_ptr() as *mut c_void, data.len()) != 0 } {
Ok(true)
} else {
Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
}
}
pub fn get_file_picker_visible() -> bool {
unsafe { platform_file_picker_visible() != 0 }
}
pub fn get_force_fallback_keyboard() -> bool {
unsafe { platform_keyboard_get_force_fallback() != 0 }
}
pub fn is_keyboard_visible() -> bool {
unsafe { platform_keyboard_visible() != 0 }
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
pub struct SHLight {
pub dir_to: Vec3,
pub color: Color128,
}
impl SHLight {
pub fn new(dir_to: impl Into<Vec3>, color: impl Into<Color128>) -> Self {
Self { dir_to: dir_to.into(), color: color.into() }
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq)]
#[repr(C)]
pub struct SphericalHarmonics {
pub coefficients: [Vec3; 9usize],
}
unsafe extern "C" {
pub fn sh_create(in_arr_lights: *const SHLight, light_count: i32) -> SphericalHarmonics;
pub fn sh_brightness(ref_harmonics: *mut SphericalHarmonics, scale: f32);
pub fn sh_add(ref_harmonics: *mut SphericalHarmonics, light_dir: Vec3, light_color: Vec3);
pub fn sh_lookup(harmonics: *const SphericalHarmonics, normal: Vec3) -> Color128;
pub fn sh_dominant_dir(harmonics: *const SphericalHarmonics) -> Vec3;
}
impl SphericalHarmonics {
pub fn from_lights(lights: &[SHLight]) -> Self {
unsafe { sh_create(lights.as_ptr(), lights.len() as i32) }
}
pub fn new(coefficients: [Vec3; 9]) -> Self {
SphericalHarmonics { coefficients }
}
pub fn add(&mut self, light_dir: impl Into<Vec3>, light_color: impl Into<Color128>) -> &mut Self {
let light_dir = light_dir.into();
let color = light_color.into();
unsafe { sh_add(self, light_dir, Vec3 { x: color.r, y: color.g, z: color.b }) };
self
}
pub fn brightness(&mut self, scale: f32) -> &mut Self {
unsafe { sh_brightness(self, scale) };
self
}
pub fn get_sample(&self, normal: impl Into<Vec3>) -> Color128 {
unsafe { sh_lookup(self, normal.into()) }
}
pub fn get_dominent_light_direction(&self) -> Vec3 {
unsafe { sh_dominant_dir(self) }
}
pub fn to_array(&self) -> [Vec3; 9] {
self.coefficients
}
}
pub struct Time;
unsafe extern "C" {
pub fn time_total_raw() -> f64;
pub fn time_totalf_unscaled() -> f32;
pub fn time_total_unscaled() -> f64;
pub fn time_totalf() -> f32;
pub fn time_total() -> f64;
pub fn time_stepf_unscaled() -> f32;
pub fn time_step_unscaled() -> f64;
pub fn time_stepf() -> f32;
pub fn time_step() -> f64;
pub fn time_scale(scale: f64);
pub fn time_set_time(total_seconds: f64, frame_elapsed_seconds: f64);
pub fn time_frame() -> u64;
}
impl Time {
pub fn scale(factor: f64) {
unsafe { time_scale(factor) }
}
pub fn set_time(total_seconds: f64, frame_elapsed_seconds: f64) {
unsafe { time_set_time(total_seconds, frame_elapsed_seconds) }
}
pub fn get_frame() -> u64 {
unsafe { time_frame() }
}
pub fn get_step() -> f64 {
unsafe { time_step() }
}
pub fn get_stepf() -> f32 {
unsafe { time_stepf() }
}
pub fn get_step_unscaled() -> f64 {
unsafe { time_step_unscaled() }
}
pub fn get_step_unscaledf() -> f32 {
unsafe { time_stepf_unscaled() }
}
pub fn get_total() -> f64 {
unsafe { time_total() }
}
pub fn get_totalf() -> f32 {
unsafe { time_totalf() }
}
pub fn get_total_unscaled() -> f64 {
unsafe { time_total_unscaled() }
}
pub fn get_total_unscaledf() -> f32 {
unsafe { time_totalf_unscaled() }
}
}