#[macro_use]
mod macros;
pub mod audio;
pub mod automation;
pub mod callbacks;
#[cfg(not(feature = "nobuild"))]
pub mod camera;
pub mod collision;
pub mod color {
#[allow(unused_imports)]
pub use crate::ffi::Color;
}
pub mod data;
pub mod databuf;
pub mod drawing;
pub mod error;
pub mod file;
pub mod hashes;
pub mod input;
pub mod logging;
pub mod math;
pub mod misc;
pub mod models;
pub mod pixel;
pub mod shaders;
pub mod text;
pub mod texture;
pub mod vr;
pub mod window;
use raylib_sys::TraceLogLevel;
#[cfg(test)]
mod color_tests {
use crate::ffi::Color;
#[test]
fn rgba_fields() {
let c = Color::new(10, 20, 30, 40);
assert_eq!((c.r, c.g, c.b, c.a), (10, 20, 30, 40));
}
#[test]
fn from_hex_red() {
let c = Color::from_hex("ff0000").unwrap();
assert_eq!((c.r, c.g, c.b, c.a), (255, 0, 0, 255));
}
#[test]
fn from_hex_white() {
let c = Color::from_hex("ffffff").unwrap();
assert_eq!((c.r, c.g, c.b, c.a), (255, 255, 255, 255));
}
#[test]
fn from_hex_invalid_returns_err() {
assert!(Color::from_hex("zzzzzz").is_err());
}
#[test]
fn get_color_opaque_red() {
let c = Color::get_color(0xFF0000FF);
assert_eq!((c.r, c.g, c.b, c.a), (255, 0, 0, 255));
}
#[test]
fn color_to_int_roundtrip() {
let c = Color::new(100, 150, 200, 255);
let packed = c.color_to_int();
let back = Color::get_color(packed as u32);
assert_eq!((back.r, back.g, back.b, back.a), (c.r, c.g, c.b, c.a));
}
#[test]
fn normalize_roundtrip() {
let c = Color::new(51, 102, 153, 204); let norm = c.color_normalize();
let back = Color::color_from_normalized(norm);
assert!((back.r as i32 - c.r as i32).abs() <= 1);
assert!((back.g as i32 - c.g as i32).abs() <= 1);
assert!((back.b as i32 - c.b as i32).abs() <= 1);
assert!((back.a as i32 - c.a as i32).abs() <= 1);
}
#[test]
fn hsv_roundtrip_red() {
let c = Color::new(255, 0, 0, 255);
let hsv = c.color_to_hsv();
assert!(
(hsv.y - 1.0).abs() < 1e-3,
"saturation should be 1.0, got {}",
hsv.y
);
assert!(
(hsv.z - 1.0).abs() < 1e-3,
"value should be 1.0, got {}",
hsv.z
);
let back = Color::color_from_hsv(hsv.x, hsv.y, hsv.z);
assert_eq!((back.r, back.g, back.b), (255, 0, 0));
}
#[test]
fn color_from_hsv_white() {
let c = Color::color_from_hsv(0.0, 0.0, 1.0);
assert_eq!((c.r, c.g, c.b), (255, 255, 255));
}
#[test]
fn alpha_fully_transparent() {
let c = Color::new(255, 0, 0, 255).alpha(0.0);
assert_eq!(c.a, 0);
}
#[test]
fn alpha_fully_opaque() {
let c = Color::new(255, 0, 0, 0).alpha(1.0);
assert_eq!(c.a, 255);
}
#[test]
fn color_equality() {
let a = Color::new(1, 2, 3, 4);
let b = Color::new(1, 2, 3, 4);
assert_eq!(a, b);
}
#[test]
fn color_inequality() {
let a = Color::new(1, 2, 3, 4);
let b = Color::new(5, 6, 7, 8);
assert_ne!(a, b);
}
}
use crate::ffi;
use std::ffi::CString;
use std::marker::PhantomData;
#[macro_export]
macro_rules! rstr {
($e:tt) => ({
#[allow(unused_unsafe)]
unsafe {
std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($e, "\0").as_bytes())
}
});
($e:tt, $($arg:tt)*) => ({
#[allow(unused_unsafe)]
unsafe {
std::ffi::CString::new(format!($e, $($arg)*)).unwrap()
}
})
}
#[derive(Clone, Debug)]
pub struct RaylibThread(PhantomData<*const ()>);
#[derive(Debug)]
pub struct RaylibHandle(());
impl Drop for RaylibHandle {
fn drop(&mut self) {
unsafe {
if ffi::IsWindowReady() {
ffi::CloseWindow();
}
}
}
}
#[derive(Debug, Default)]
pub struct RaylibBuilder {
fullscreen_mode: bool,
window_resizable: bool,
window_undecorated: bool,
window_transparent: bool,
msaa_4x_hint: bool,
vsync_hint: bool,
log_level: TraceLogLevel,
width: i32,
height: i32,
title: String,
}
#[inline]
#[must_use]
pub fn init() -> RaylibBuilder {
RaylibBuilder {
width: 640,
height: 480,
title: "raylib-rs".to_string(),
..Default::default()
}
}
impl RaylibBuilder {
pub fn fullscreen(&mut self) -> &mut Self {
self.fullscreen_mode = true;
self
}
pub fn log_level(&mut self, level: TraceLogLevel) -> &mut Self {
self.log_level = level;
self
}
pub fn resizable(&mut self) -> &mut Self {
self.window_resizable = true;
self
}
pub fn undecorated(&mut self) -> &mut Self {
self.window_undecorated = true;
self
}
pub fn transparent(&mut self) -> &mut Self {
self.window_transparent = true;
self
}
pub fn msaa_4x(&mut self) -> &mut Self {
self.msaa_4x_hint = true;
self
}
pub fn vsync(&mut self) -> &mut Self {
self.vsync_hint = true;
self
}
pub fn width(&mut self, w: i32) -> &mut Self {
self.width = w;
self
}
pub fn height(&mut self, h: i32) -> &mut Self {
self.height = h;
self
}
pub fn size(&mut self, w: i32, h: i32) -> &mut Self {
self.width = w;
self.height = h;
self
}
pub fn title(&mut self, text: &str) -> &mut Self {
self.title = text.to_string();
self
}
pub fn build(&self) -> (RaylibHandle, RaylibThread) {
use crate::consts::ConfigFlags::*;
let mut flags = 0u32;
if self.fullscreen_mode {
flags |= FLAG_FULLSCREEN_MODE as u32;
}
if self.window_resizable {
flags |= FLAG_WINDOW_RESIZABLE as u32;
}
if self.window_undecorated {
flags |= FLAG_WINDOW_UNDECORATED as u32;
}
if self.window_transparent {
flags |= FLAG_WINDOW_TRANSPARENT as u32;
}
if self.msaa_4x_hint {
flags |= FLAG_MSAA_4X_HINT as u32;
}
if self.vsync_hint {
flags |= FLAG_VSYNC_HINT as u32;
}
unsafe {
ffi::SetConfigFlags(flags);
}
unsafe {
ffi::SetTraceLogLevel(self.log_level as i32);
}
let rl = init_window(self.width, self.height, &self.title);
(rl, RaylibThread(PhantomData))
}
}
fn init_window(width: i32, height: i32, title: &str) -> RaylibHandle {
if unsafe { ffi::IsWindowReady() } {
panic!("Attempted to initialize raylib-rs more than once!");
} else {
unsafe {
let c_title = CString::new(title).unwrap();
ffi::InitWindow(width, height, c_title.as_ptr());
}
if !unsafe { ffi::IsWindowReady() } {
panic!("Attempting to create window failed!");
}
RaylibHandle(())
}
}