Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 58 additions & 34 deletions examples/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extern crate sdl3;
use std::ptr;

use sdl3::properties::*;
use std::ffi::c_char;

#[derive(Debug, Clone)]
#[allow(dead_code)]
Expand All @@ -16,54 +17,70 @@ impl<'a> Drop for TestData<'a> {
}
}

macro_rules! property {
($name:expr, $ty:expr) => {
PropertyName {
module: "example",
name: $name,
short_name: $name,
value: $name,
raw: concat!($name, "\0").as_ptr() as *const c_char,
ty: $ty,
doc: None,
available_since: None,
}
};
}

pub fn main() -> Result<(), Box<dyn std::error::Error>> {
sdl3::init().ok();

let mut properties = Properties::new().unwrap();

let bprop = "bool";
let fprop = "float";
let nprop = "number";
let sprop = "string";
properties.set(bprop, true).ok();
properties.set(fprop, 6.9).ok();
properties.set(nprop, 420).ok();
properties.set(sprop, "blazeit").ok();

println!("Property {bprop}: {:?}", properties.get(bprop, false));
if let Ok(()) = properties.clear(bprop) {
println!("Cleared {bprop}");
let bprop = property!("bool", PropertyType::BOOLEAN);
let fprop = property!("float", PropertyType::FLOAT);
let nprop = property!("number", PropertyType::NUMBER);
let sprop = property!("string", PropertyType::STRING);
properties.set(&bprop, true).ok();
properties.set(&fprop, 6.9f32).ok();
properties.set(&nprop, 420i64).ok();
properties.set(&sprop, "blazeit").ok();

println!("Property bool: {:?}", properties.get(&bprop, false));
if let Ok(()) = properties.clear("bool") {
println!("Cleared bool");
} else {
println!("Failed to clear {bprop}");
println!("Failed to clear bool");
}
if let Ok(()) = properties.clear(bprop) {
println!("Cleared {bprop}");
if let Ok(()) = properties.clear("bool") {
println!("Cleared bool");
} else {
println!("Failed to clear {bprop}");
println!("Failed to clear bool");
}
println!("Property {bprop}: {:?}", properties.get(bprop, false));
println!("Property {fprop}: {:?}", properties.get(fprop, 3.333));
println!("Property {nprop}: {:?}", properties.get(nprop, -1));
println!("Property nodefault: {:?}", properties.get("nodefault", -1));
println!("Property nodefault: {:?}", properties.get("nodefault", 42));
println!("Property bool: {:?}", properties.get(&bprop, false));
println!("Property float: {:?}", properties.get(&fprop, 3.333));
println!("Property number: {:?}", properties.get(&nprop, -1));

println!(
"Property {sprop}: {:?}",
properties.get_string(sprop, "bad")
"Property string: {:?}",
properties.get_string("string", "bad")
);

let test = TestData {
hello: "hello",
goodbye: "goodbye",
};

let pointer_prop = property!("pointer", PropertyType::POINTER);
let autopointer_prop = property!("autopointer", PropertyType::POINTER);

// You can set a pointer by yourself, but you have to clean it up
properties
.set("pointer", Box::into_raw(Box::new(test.clone())))
.set(&pointer_prop, Box::into_raw(Box::new(test.clone())))
.ok();

// Will get a pointer to the data
if let Ok(pointer) = properties.get("pointer", ptr::null_mut() as *mut TestData) {
if let Ok(pointer) = properties.get(&pointer_prop, ptr::null_mut() as *mut TestData) {
unsafe {
println!("Pointer: {:?}", *pointer);
}
Expand All @@ -73,7 +90,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {

// Will get a pointer to the data, then claim ownership of it and clear it from the properties object
// You must clear a pointer property if you claim it
if let Ok(pointer) = properties.get("pointer", ptr::null_mut() as *mut TestData) {
if let Ok(pointer) = properties.get(&pointer_prop, ptr::null_mut() as *mut TestData) {
unsafe {
properties.clear("pointer").ok();
let value = Box::from_raw(pointer);
Expand All @@ -84,7 +101,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}

// The previous get cleared the property, so this will safely fail
if let Ok(pointer) = properties.get("pointer", ptr::null_mut() as *mut TestData) {
if let Ok(pointer) = properties.get(&pointer_prop, ptr::null_mut() as *mut TestData) {
unsafe {
println!("Pointer: {:?}", *pointer);
}
Expand All @@ -93,9 +110,11 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}

// Alternatively to setting a raw pointer, you can set a box which will be automatically cleaned up
properties.set("autopointer", Box::new(test.clone())).ok();
properties
.set(&autopointer_prop, Box::new(test.clone()))
.ok();
// Will get a pointer to the data
if let Ok(pointer) = properties.get("autopointer", ptr::null_mut() as *mut TestData) {
if let Ok(pointer) = properties.get(&autopointer_prop, ptr::null_mut() as *mut TestData) {
unsafe {
println!("autopointer: {:?}", *pointer);
}
Expand All @@ -105,7 +124,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
// The box will be reclaimed and dropped at this point
properties.clear("autopointer").ok();
// This will fail
if let Ok(pointer) = properties.get("autopointer", ptr::null_mut() as *mut TestData) {
if let Ok(pointer) = properties.get(&autopointer_prop, ptr::null_mut() as *mut TestData) {
unsafe {
println!("autopointer: {:?}", *pointer);
}
Expand All @@ -114,15 +133,19 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}

// Set the autopointer again
properties.set("autopointer", Box::new(test.clone())).ok();
properties
.set(&autopointer_prop, Box::new(test.clone()))
.ok();
// semi-safely borrow a pointer property by holding a lock on properties
properties
.with("autopointer", |value: &TestData| {
.with(&autopointer_prop, |value: &TestData| {
println!("Borrowed value: {value:?}");
})
.ok();
// Overwrite the property, this will drop the previous value
properties.set("autopointer", Box::new(test.clone())).ok();
properties
.set(&autopointer_prop, Box::new(test.clone()))
.ok();

properties
.enumerate(Box::new(|properties, name| match name {
Expand All @@ -144,8 +167,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
.ok();

// Global properties are not destroyed
let persistent_prop = property!("persistent", PropertyType::STRING);
let global = Properties::global().unwrap();
global.set("persistent", "rawr x3").ok();
global.set(&persistent_prop, "rawr x3").ok();
drop(global);

let global = Properties::global().unwrap();
Expand Down
67 changes: 44 additions & 23 deletions src/sdl3/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum PropertiesError {
StringError(Utf8Error),
NullPointer,
SdlError(Error),
TypeMismatch,
}

#[derive(Clone)]
Expand Down Expand Up @@ -281,7 +282,7 @@ impl Properties {
}

#[doc(alias = "SDL_GetPointerProperty")]
pub fn with<T>(&mut self, name: &str, with: fn(&T)) -> Result<(), PropertiesError> {
pub fn with<T>(&mut self, name: &PropertyName, with: fn(&T)) -> Result<(), PropertiesError> {
self.lock()?;
let pointer: *mut T = self.get(name, ptr::null_mut())?;
if pointer.is_null() {
Expand All @@ -295,13 +296,15 @@ impl Properties {
}

pub trait Setter<T> {
fn set(&self, name: &str, value: T) -> Result<(), PropertiesError>;
fn set(&self, name: &PropertyName, value: T) -> Result<(), PropertiesError>;
}

impl Setter<bool> for Properties {
#[doc(alias = "SDL_SetBooleanProperty")]
fn set(&self, name: &str, value: bool) -> Result<(), PropertiesError> {
cstring!(name);
fn set(&self, name: &PropertyName, value: bool) -> Result<(), PropertiesError> {
if name.ty != PropertyType::BOOLEAN {
return Err(PropertiesError::TypeMismatch);
}
if unsafe { sys::properties::SDL_SetBooleanProperty(self.internal, name.as_ptr(), value) } {
Ok(())
} else {
Expand All @@ -312,8 +315,10 @@ impl Setter<bool> for Properties {

impl Setter<f32> for Properties {
#[doc(alias = "SDL_SetFloatProperty")]
fn set(&self, name: &str, value: f32) -> Result<(), PropertiesError> {
cstring!(name);
fn set(&self, name: &PropertyName, value: f32) -> Result<(), PropertiesError> {
if name.ty != PropertyType::FLOAT {
return Err(PropertiesError::TypeMismatch);
}
if unsafe { sys::properties::SDL_SetFloatProperty(self.internal, name.as_ptr(), value) } {
Ok(())
} else {
Expand All @@ -324,8 +329,10 @@ impl Setter<f32> for Properties {

impl Setter<i64> for Properties {
#[doc(alias = "SDL_SetNumberProperty")]
fn set(&self, name: &str, value: i64) -> Result<(), PropertiesError> {
cstring!(name);
fn set(&self, name: &PropertyName, value: i64) -> Result<(), PropertiesError> {
if name.ty != PropertyType::NUMBER {
return Err(PropertiesError::TypeMismatch);
}
if unsafe { sys::properties::SDL_SetNumberProperty(self.internal, name.as_ptr(), value) } {
Ok(())
} else {
Expand All @@ -336,8 +343,10 @@ impl Setter<i64> for Properties {

impl Setter<&str> for Properties {
#[doc(alias = "SDL_SetStringProperty")]
fn set(&self, name: &str, value: &str) -> Result<(), PropertiesError> {
cstring!(name);
fn set(&self, name: &PropertyName, value: &str) -> Result<(), PropertiesError> {
if name.ty != PropertyType::STRING {
return Err(PropertiesError::TypeMismatch);
}
// Have to transform the value into a cstring, SDL makes an internal copy
cstring!(value);
if unsafe {
Expand All @@ -352,8 +361,10 @@ impl Setter<&str> for Properties {

impl<T> Setter<*mut T> for Properties {
#[doc(alias = "SDL_SetPointerProperty")]
fn set(&self, name: &str, value: *mut T) -> Result<(), PropertiesError> {
cstring!(name);
fn set(&self, name: &PropertyName, value: *mut T) -> Result<(), PropertiesError> {
if name.ty != PropertyType::POINTER {
return Err(PropertiesError::TypeMismatch);
}
if unsafe {
sys::properties::SDL_SetPointerProperty(
self.internal,
Expand All @@ -370,8 +381,10 @@ impl<T> Setter<*mut T> for Properties {

impl<T> Setter<Box<T>> for Properties {
#[doc(alias = "SDL_SetPointerPropertyWithCleanup")]
fn set(&self, name: &str, value: Box<T>) -> Result<(), PropertiesError> {
cstring!(name);
fn set(&self, name: &PropertyName, value: Box<T>) -> Result<(), PropertiesError> {
if name.ty != PropertyType::POINTER {
return Err(PropertiesError::TypeMismatch);
}
let value_ptr: *mut c_void = Box::into_raw(value) as *mut c_void;
let cleanup: CleanupBox = |value: *mut c_void| {
let value = value as *mut T;
Expand All @@ -397,13 +410,15 @@ impl<T> Setter<Box<T>> for Properties {
}

pub trait Getter<T> {
fn get(&self, name: &str, default: T) -> Result<T, PropertiesError>;
fn get(&self, name: &PropertyName, default: T) -> Result<T, PropertiesError>;
}

impl Getter<bool> for Properties {
#[doc(alias = "SDL_GetBooleanProperty")]
fn get(&self, name: &str, default: bool) -> Result<bool, PropertiesError> {
cstring!(name);
fn get(&self, name: &PropertyName, default: bool) -> Result<bool, PropertiesError> {
if name.ty != PropertyType::BOOLEAN {
return Err(PropertiesError::TypeMismatch);
}
unsafe {
Ok(sys::properties::SDL_GetBooleanProperty(
self.internal,
Expand All @@ -416,8 +431,10 @@ impl Getter<bool> for Properties {

impl Getter<f32> for Properties {
#[doc(alias = "SDL_GetFloatProperty")]
fn get(&self, name: &str, default: f32) -> Result<f32, PropertiesError> {
cstring!(name);
fn get(&self, name: &PropertyName, default: f32) -> Result<f32, PropertiesError> {
if name.ty != PropertyType::FLOAT {
return Err(PropertiesError::TypeMismatch);
}
unsafe {
Ok(sys::properties::SDL_GetFloatProperty(
self.internal,
Expand All @@ -430,8 +447,10 @@ impl Getter<f32> for Properties {

impl Getter<i64> for Properties {
#[doc(alias = "SDL_GetNumberProperty")]
fn get(&self, name: &str, default: i64) -> Result<i64, PropertiesError> {
cstring!(name);
fn get(&self, name: &PropertyName, default: i64) -> Result<i64, PropertiesError> {
if name.ty != PropertyType::NUMBER {
return Err(PropertiesError::TypeMismatch);
}
unsafe {
Ok(sys::properties::SDL_GetNumberProperty(
self.internal,
Expand All @@ -444,8 +463,10 @@ impl Getter<i64> for Properties {

impl<T> Getter<*mut T> for Properties {
#[doc(alias = "SDL_GetPointerProperty")]
fn get(&self, name: &str, default: *mut T) -> Result<*mut T, PropertiesError> {
cstring!(name);
fn get(&self, name: &PropertyName, default: *mut T) -> Result<*mut T, PropertiesError> {
if name.ty != PropertyType::POINTER {
return Err(PropertiesError::TypeMismatch);
}
let pointer = unsafe {
sys::properties::SDL_GetPointerProperty(
self.internal,
Expand Down
Loading