1

basic window management

This commit is contained in:
Evan Pratten 2022-12-09 13:00:57 -05:00
parent 9b1a591e22
commit a13194be71
6 changed files with 209 additions and 13 deletions

View File

@ -21,7 +21,17 @@ exclude = [
[dependencies]
rgb = "^0.8.34"
cgmath = "^0.18.0"
cstr = "^0.2.11"
num-traits = "^0.2.14"
printf = "^0.1.0"
log = "^0.4.14"
num-derive = "^0.3.3"
vsprintf = "^2.0.0"
lazy_static = "^1.4.0"
[build-dependencies]
bindgen = "^0.63.0"
cmake = "^0.1.49"
[dev-dependencies]
env_logger = "^0.10.0"

View File

@ -1,4 +1,17 @@
use std::path::PathBuf;
use std::{path::PathBuf, collections::HashSet};
#[derive(Debug)]
struct IgnoreMacros(HashSet<String>);
impl bindgen::callbacks::ParseCallbacks for IgnoreMacros {
fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior {
if self.0.contains(name) {
bindgen::callbacks::MacroParsingBehavior::Ignore
} else {
bindgen::callbacks::MacroParsingBehavior::Default
}
}
}
/// Compiles raylib
fn compile_raylib(raylib_path: &str) {
@ -23,9 +36,18 @@ fn compile_raylib(raylib_path: &str) {
let destination = cmake_config.build();
// Tell cargo where the libraries might be
println!("cargo:rustc-link-search=native={}", destination.join("lib").display());
println!("cargo:rustc-link-search=native={}", destination.join("lib32").display());
println!("cargo:rustc-link-search=native={}", destination.join("lib64").display());
println!(
"cargo:rustc-link-search=native={}",
destination.join("lib").display()
);
println!(
"cargo:rustc-link-search=native={}",
destination.join("lib32").display()
);
println!(
"cargo:rustc-link-search=native={}",
destination.join("lib64").display()
);
}
/// Links libraries
@ -62,10 +84,25 @@ fn link_libs() {
/// Generates `bindings.rs` file
fn generate_bindings(header_file: &str) {
// Tell bindgen to ignore these macros
let ignored_macros = IgnoreMacros(
vec![
"FP_INFINITE".into(),
"FP_NAN".into(),
"FP_NORMAL".into(),
"FP_SUBNORMAL".into(),
"FP_ZERO".into(),
"IPPORT_RESERVED".into(),
]
.into_iter()
.collect(),
);
// Generate the data
let bindings = bindgen::Builder::default()
.header(header_file)
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.parse_callbacks(Box::new(ignored_macros))
.generate()
.expect("Unable to generate bindings");

View File

@ -1,10 +1,14 @@
use renderkit::raylib::window::Window;
pub fn main() {
unsafe {
// Set up logging
env_logger::init();
// Create a window
renderkit::raylib::ffi::InitWindow(
800,
450,
"raylib-ffi example - basic window\0".as_ptr() as *const i8,
let window = Window::new(
cgmath::Vector2::new(800, 450),
"raylib-ffi example - basic window",
);
// Render the window
@ -20,7 +24,7 @@ pub fn main() {
// Render text and a background
renderkit::raylib::ffi::ClearBackground(renderkit::raylib::palette::RAYWHITE.into());
renderkit::raylib::ffi::DrawText(
"Congrats! You created your first window!\0".as_ptr() as *const i8,
cstr::cstr!("Congrats! You created your first window!").as_ptr(),
190,
200,
20,
@ -30,8 +34,5 @@ pub fn main() {
// End the draw call
renderkit::raylib::ffi::EndDrawing();
}
// Clean up
renderkit::raylib::ffi::CloseWindow();
}
}

87
src/raylib/logging.rs Normal file
View File

@ -0,0 +1,87 @@
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use vsprintf::vsprintf;
/// Direct mapping of raylib's log levels
/// See: https://github.com/raysan5/raylib/blob/d875891a3c2621ab40733ca3569eca9e054a6506/parser/raylib_api.json#L985-L1026
#[derive(FromPrimitive)]
enum RaylibLogLevel {
All = 0,
Trace = 1,
Debug = 2,
Info = 3,
Warning = 4,
Error = 5,
Fatal = 6,
None = 7,
}
#[no_mangle]
unsafe extern "C" fn rl_log_callback(
level: i32,
message: *const i8,
args: *mut super::ffi::__va_list_tag,
) {
// Format the message
let formatted_message = vsprintf(message, args);
// If all is well, we can continue logging
if let Ok(formatted_message) = formatted_message {
// Convert the string prefix to a target if possible
let mut target = "raylib";
let final_message;
if formatted_message.starts_with("RLGL: ") {
target = "raylib::rlgl";
final_message =
std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("RLGL: "));
}
// else if formatted_message.starts_with("DISPLAY: ") {
// target = "raylib::display";
// final_message = std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("DISPLAY: "));
// }
// else if formatted_message.starts_with("GL: ") {
// target = "raylib::gl";
// final_message = std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("GL: "));
// }
else if formatted_message.starts_with("GLAD: ") {
target = "raylib::glad";
final_message =
std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("GLAD: "));
} else if formatted_message.starts_with("TEXTURE: ") {
target = "raylib::texture";
final_message =
std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("TEXTURE: "));
} else if formatted_message.starts_with("SHADER: ") {
target = "raylib::shader";
final_message =
std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("SHADER: "));
} else if formatted_message.starts_with("FONT: ") {
target = "raylib::font";
final_message =
std::borrow::Cow::Borrowed(formatted_message.trim_start_matches("FONT: "));
} else {
final_message = std::borrow::Cow::Borrowed(&formatted_message);
}
// Handle the log level and fall back on info!
match RaylibLogLevel::from_u32(level as u32) {
Some(level) => match level {
RaylibLogLevel::Trace => log::trace!(target: target, "{}", final_message),
RaylibLogLevel::Debug => log::debug!(target: target, "{}", final_message),
RaylibLogLevel::Warning => log::warn!(target: target, "{}", final_message),
RaylibLogLevel::Error => log::error!(target: target, "{}", final_message),
RaylibLogLevel::Fatal => log::error!(target: target, "{}", final_message),
_ => log::info!(target: target, "{}", final_message),
},
None => {
log::info!(target:"raylib", "{}", final_message)
}
}
}
}
/// Configures raylib to use our custom logging code
pub unsafe fn rl_use_rust_logging() {
log::debug!("Configuring raylib to use the `log` crate");
super::ffi::SetTraceLogCallback(Some(rl_log_callback));
}

View File

@ -2,4 +2,7 @@
pub mod xlat;
pub mod ffi;
pub mod palette;
pub mod palette;
pub mod window;
pub(crate) mod logging;

58
src/raylib/window.rs Normal file
View File

@ -0,0 +1,58 @@
use cgmath::Vector2;
use super::ffi as raylib;
use super::logging::rl_use_rust_logging;
lazy_static::lazy_static! {
static ref WINDOW_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
}
/// A window handle
#[derive(Debug)]
pub struct Window {}
impl Window {
/// Construct a new `Window`
pub fn new(size: Vector2<i32>, title: &str) -> Self {
// Make sure we only have one window
if WINDOW_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst) > 0 {
panic!("Only one window is allowed at a time");
}
// Perform FFI work
unsafe {
// Hook our rusty logging system into raylib
rl_use_rust_logging();
// Set some reasonable defaults
raylib::SetWindowState(
raylib::ConfigFlags_FLAG_VSYNC_HINT
| raylib::ConfigFlags_FLAG_WINDOW_RESIZABLE
| raylib::ConfigFlags_FLAG_MSAA_4X_HINT,
);
// Create a window
raylib::InitWindow(
size.x as i32,
size.y as i32,
format!("{}\0", title).as_ptr() as *const i8,
);
}
Self {}
}
}
impl Drop for Window {
fn drop(&mut self) {
// Perform FFI work
unsafe {
// Clean up
crate::raylib::ffi::CloseWindow();
}
// Decrement the window count
WINDOW_COUNT.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
}
}