diff --git a/README.org b/README.org index d374a9b..1ca4834 100644 --- a/README.org +++ b/README.org @@ -59,7 +59,14 @@ - [X] GHOST_TabletData - [X] /Constants/ - [X] =GHOST_TABLET_DATA_NONE= (reimpl. as ~const fn GHOST_TabletData::none()~) - - [ ] ~GHOST_C-api.h~ + - [-] ~GHOST_C-api.h~ + - [-] /Functions/ + - [X] GHOST_CreateSystem + - [X] GHOST_SystemInitDebug + - [X] GHOST_DisposeSystem + - [ ] GHOST_ShowMessageBox + - [X] /Function pointers/ + - [X] GHOST_EventCallbackProcPtr *** Native Rust API Rust native APIs are provided for things where the C API bindings are cumbersome or unsafe. @@ -73,7 +80,11 @@ - [X] [[Handles]]: ~handle.rs~ - [X] [[Error handling]]: ~error.rs~ - [ ] Events: ~event.rs~ - - [ ] Aliases + - [ ] Modules + - [ ] [[System]] ~system.rs~ + +**** TODO System + Provides a Rust wrapper around the GHOST system. **** Handles Handles keep the C ABI, but are re-wrtten with traits to be more idiomatic. diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..410e953 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,18 @@ +//! C API functions and interop +use super::{ + *, + handle::*, + c::*, +}; +use libc::{ + c_int, +}; +use std::{ + ptr::NonNull, +}; + +extern "C" { + pub fn GHOST_CreateSystem() -> GHOST_SystemHandle; + pub fn GHOST_SystemInitDebug(_: GHOST_SystemHandle, _: c_int); + pub fn GHOST_DisposeSystem(_: GHOST_SystemHandle) -> GHOST_TSuccess; +} diff --git a/src/c.rs b/src/c.rs index 1e815e0..6b333e8 100644 --- a/src/c.rs +++ b/src/c.rs @@ -27,6 +27,11 @@ pub use super::{ GHOST_DialogOptions, GHOST_TabletData, }, + api::{ + GHOST_CreateSystem, + GHOST_DisposeSystem, + GHOST_SystemInitDebug, + }, handle::{ GHOST_SystemHandle, GHOST_TimerTaskHandle, diff --git a/src/error.rs b/src/error.rs index 6274a1e..658e2c7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -73,7 +73,7 @@ pub type GhostResult = Result; /// Error type for failed GHOST calls. /// As of yet it doesn't contain any information and is just an opaque zero-sized structure. #[derive(Debug)] -pub struct GhostError(()); +pub struct GhostError(pub(crate) ()); impl error::Error for GhostError{} impl fmt::Display for GhostError { diff --git a/src/handle.rs b/src/handle.rs index 1909866..288fcb4 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -5,6 +5,23 @@ use super::*; pub use types::{Handle,GhostHandle}; +pub(crate) trait AsHandle +{ + fn as_handle(&mut self) -> &mut Handle; + #[inline] fn as_raw(&mut self) -> *mut Handle + { + self.as_handle() as *mut Handle + } +} +impl AsHandle for Handle +{ + #[inline] fn as_handle(&mut self) -> &mut Handle + { + self + } + +} + #[cfg(not(nightly))] enum HandleInner{} macro_rules! handle { @@ -25,3 +42,10 @@ handle!(GHOST_RectangleHandle, Rectangle); handle!(GHOST_EventConsumerHandle, EventConsumer); handle!(GHOST_ContextHandle, Context); handle!(GHOST_XrContextHandle, XrContextHandle); + +// This is very unsafe. +/*#[inline] pub(crate) unsafe fn coerce<'a, T>(from: *mut Handle) -> &'a mut Handle + where T: GhostHandle +{ + std::mem::transmute::<*mut _, &'a mut _>(from) //this is needed because we can't safely dereference `Handle`. +}*/ diff --git a/src/lib.rs b/src/lib.rs index 642a001..c34e6f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,48 @@ pub mod handle; pub mod event; pub mod types; pub mod error; +pub mod api; + + +macro_rules! from_null { + ($ptr:expr) => { + { + let ptr = $ptr; + if ptr.is_null() { + return Err($crate::error::GhostError(()))?; + } else { + ptr + } + } + }; + (return $ptr:expr) => { + { + + let ptr = $ptr; + if ptr.is_null() { + Err($crate::error::GhostError(())) + } else { + Ok(ptr) + } + } + } +} + +macro_rules! debug { + ($debug:expr; $prod:expr) => { + { + #[cfg(debug_assertions)] $debug; + #[cfg(not(debug_assertions))] $prod; + } + }; + ($debug:expr) => { + { + #[cfg(debug_assertions)] $debug + } + }; +} + +pub mod system; pub mod c; diff --git a/src/system.rs b/src/system.rs new file mode 100644 index 0000000..6746a27 --- /dev/null +++ b/src/system.rs @@ -0,0 +1,84 @@ +//! Container for `GHOST_SystemHandle` +use super::*; +use handle::*; +use std::{ + ptr::NonNull, + ops::Drop, +}; + +/// Represents the active GHOST system. +/// +/// This is global state, and only one can exist at once time. +/// # Usage +/// Create with the `create()` function. When the instance is dropped, the system is attempted to be disposed of. +/// This is not an associated function, to specify that creating the system is a global event, not an associated one. +/// +/// # Panics +/// On debug builds a failed dispose on drop will panic. You can call `try_drop()` to attempt the dispose manually. +#[derive(Debug)] +pub struct System(NonNull>); + +impl System +{ + pub(crate) unsafe fn new_unchecked(from: *mut Handle) -> Self + { + Self(NonNull::new_unchecked(from)) + } + + /// Specifies whether debug messages are to be enabled for the specific system handle. + pub fn debug_messages(&mut self, enable: bool) + { + unsafe { + api::GHOST_SystemInitDebug(self.as_raw(), enable as libc::c_int); + } + } + + /// Try to dispose the system now, returning the result. + pub fn try_drop(self) -> error::GhostResult + { + let res = unsafe { + api::GHOST_DisposeSystem(self.0.as_ptr()) + }; + std::mem::forget(self); + res.into() + } + + #[inline] pub fn inner_handle(&mut self) -> GHOST_SystemHandle + { + self.0.as_ptr() + } +} + +impl handle::AsHandle for System +{ + #[inline] fn as_handle(&mut self) -> &mut Handle { + unsafe{self.0.as_mut()} + } + #[inline] fn as_raw(&mut self) -> *mut Handle { + self.inner_handle() + } +} + +impl Drop for System +{ + fn drop(&mut self) + { + unsafe { + let ptr = self.0.as_ptr(); + debug!(api::GHOST_DisposeSystem(ptr).into_result().expect("Failed to dispose system"); + {let _ = api::GHOST_DisposeSystem(ptr);}); + } + } +} + +/// Create the one and only system. +/// +/// You must not call `create()` again as long as the system lives. After dropping the system it may or may not be safe to call this again. +pub fn create() -> error::GhostResult +{ + unsafe { + let ptr = from_null!(api::GHOST_CreateSystem()); + + Ok(System::new_unchecked(ptr)) + } +} diff --git a/src/types.rs b/src/types.rs index c8e031e..8b438df 100644 --- a/src/types.rs +++ b/src/types.rs @@ -168,6 +168,9 @@ enum_flags!(GHOST_DialogOptions DialogError = 1<<1, }); +/// Value returned by some GHOST functions to indicate a success or failure. +/// +/// For mapping this behaviour onto Rust, see ['GhostResult`](./error/type.GhostResult.html). #[repr(C)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum GHOST_TSuccess @@ -375,3 +378,10 @@ pub enum GHOST_TEventType { NumEventTypes } + +/** + * Definition of a callback routine that receives events. + * * The event received. + * * The callback's user data, supplied to GHOST_CreateSystem. + */ +pub type GHOST_EventCallbackProcPtr = Option libc::c_int>;