fix bitflags with janky hack

master
Avril 4 years ago
parent e5c3f0deb0
commit f79f186dc5
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -5,7 +5,16 @@
ABI-compatable types and functions are all prefixed with `GHOST_` and are exported in the `c` module (TODO). ABI-compatable types and functions are all prefixed with `GHOST_` and are exported in the `c` module (TODO).
I've attempted to provide more Rust-idiomatic API variants as well, which have non-prefixed names. I've attempted to provide more Rust-idiomatic API variants as well, which have non-prefixed names.
** Implementation log ** Notes
Eventually I intend to have /most/ (if not all) C compatable types either reimplemented or aliased to non-prefixed Rust types.
At present though this is not the case, and prefixed names that can double as ergonomic Rust types are not aliased to them, so you'll have to use both often.
The reimplementation is undergoing, the aliasing not yet.
I've elected to leave out implementing ~Copy~ for /most/ FFI types, because I think the ownership model will work well here.
This may be changed in the future, as (virtually) all implement ~Clone~ anyway.
I did this basically just because I want people to think about it when and why they copy something, instead of it being default behaviour.
** Implementation
*** Implemented C compatable ABI *** Implemented C compatable ABI
Note: List is in-progress and doesn't show everything Note: List is in-progress and doesn't show everything
@ -49,17 +58,18 @@
- [X] GHOST_DialogOptions - [X] GHOST_DialogOptions
- [X] GHOST_TabletData - [X] GHOST_TabletData
- [X] /Constants/ - [X] /Constants/
- [X] =GHOST_TABLET_DATA_NONE= - [X] =GHOST_TABLET_DATA_NONE= (reimpl as ~const fn GHOST_TabletData::none()~)
* reimpl as ~const fn~ (~GHOST_TabletData::none()~)
*** Native Rust API *** Native Rust API
I provide more Rust idiomatic API for some things. They may or may not share ABI, if they do they will have type aliases to the corresponding ~GHOST_~ identifier, so always use those when ABI compatability is desired. I provide more Rust idiomatic API for some things.
They may or may not share ABI, if they do they will have type aliases to the corresponding ~GHOST_~ identifier, so always use those when ABI compatability is desired.
**** Overview **** Overview
List and implementation status of features List and implementation status of features
- [-] Types - [-] Types
- [X] [[Handles]]: ~handle.rs~ - [X] [[Handles]]: ~handle.rs~
- [X] [[Error handling]]: ~error.rs~
- [ ] Events: ~event.rs~ - [ ] Events: ~event.rs~
**** Handles **** Handles
@ -70,12 +80,37 @@
Since handles are just types pointers, they are only ever used as such and shouldn't exist themselves. Since handles are just types pointers, they are only ever used as such and shouldn't exist themselves.
***** Example ***** Example
#+BEGIN_SRC rust #+BEGIN_SRC rust
extern "C" unsafe fn internal_call(window: GHOST_WindowHandle) -> GHOST_TSuccess; extern "C" {fn some_internal_call(window: GHOST_WindowHandle) -> GHOST_TSuccess;}
fn do_something_to_window(window: &mut Handle<Window>) -> Result<()> fn do_something_to_window(window: &mut Handle<Window>) -> GhostResult<()>
{ {
unsafe { some_internal_call(window as GHOST_WindowHandle).into()
internal_call(window as GHOST_WindowHandle).into()
}
} }
#+END_SRC #+END_SRC
**** Error handling
I have provided an idiomatic Rust error handling module, with conversion and interop between the C ABI ~GHOST_TSuccess~ and the Rust ~error::GhostError~.
There is also the type alias ~GhostResult~ provided.
On =nightly= Rust versions, values of type ~GHOST_TSuccess~ can be propagated directly with ~?~, but on stable they must be propagated through ~GhostResult~:
#+BEGIN_SRC rust
extern "C" {fn returns_tsuccess() -> GHOST_TSuccess;}
fn try_things_internal() -> GhostResult // On nightly, we can propagage `GHOST_TSuccess` directly here, but it's still more desireable for us to have a `Result<T,E>` instead of an integer in Rust, so this is still preferrable.
{
returns_tsuccess()?;
Ok(())
}
fn caller()
{
match try_things_internal() {
Ok(_) => println!("Yay!"),
Err(_) => panic!("Fug"),
}
}
#+END_SRC
Keeping such a style is /usually/ preferred, anyhow.
See [[./src/error.rs][error.rs]] for more details.

@ -0,0 +1,152 @@
//! Error handling, reporting and propagating.
//!
//! Allows us to take advantage of `Result<T,E>` and interop idiomatic Rust error reporing with the ffi C functions' error reporting.
//! Functions returning or constructing `GHOST_TSuccess` will be able to be taken advantage of to be propagated and reported better.
//!
//! See [`GhostResult`] for more information.
use super::*;
use std::{
error,
fmt,
};
/// A result that returns potentially an error. In the form of `GHOST_TSuccess`.
///
/// # Usage
/// Its function is to provide more ergonomic error handling for Rust code, while still being able to easily interface with the C code.
/// # Propagating
/// Can be used to use `?` operator on functions returning `GHOST_TSuccess`.
///
/// When building on nightly, the following is possible.
/// ```
/// # use ghost::{error::GhostResult, c::GHOST_TSuccess};
/// extern "C" {fn returns_tsuccess() -> GHOST_TSuccess;}
///
/// fn try_things_internal() -> GHOST_TSuccess
/// {
/// unsafe {
/// returns_tsuccess()?;
/// }
///
/// GHOST_TSuccess::Success
/// }
///
/// fn caller()
/// {
/// match try_things_internal() {
/// GHOST_TSuccess::Success => println!("Yay!"),
/// GHOST_TSuccess::Failure => panic!("Fug"),
/// }
/// }
/// ```
///
/// On stable, it must be rewritten a bit differently, however:
///
/// ```
/// # use ghost::{error::GhostResult, c::GHOST_TSuccess};
/// extern "C" {fn returns_tsuccess() -> GHOST_TSuccess;}
///
/// fn try_things_internal() -> GhostResult
/// {
/// unsafe {
/// returns_tsuccess()?;
/// }
///
/// Ok(())
/// }
///
/// fn caller()
/// {
/// match try_things_internal() {
/// Ok(_) => println!("Yay!"),
/// Err(_) => panic!("Fug"),
/// }
/// }
/// ```
/// The bottom scenario is preferrable for Rust, as we are using `Result<T,E>` instead of an integer.
/// # When to use
/// This type should be preferred in Rust code, and `GHOST_TSuccess` preferred when doing a lot of FFI at once.
/// # Notes
/// *Usually* the internal result will be `()`. But idk if that will always be the case so it's still possible to set.
pub type GhostResult<T = ()> = Result<T, GhostError>;
/// 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(());
impl error::Error for GhostError{}
impl fmt::Display for GhostError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "GHOST function call failed")
}
}
impl From<types::GHOST_TSuccess> for GhostResult
{
#[inline] fn from(from: types::GHOST_TSuccess) -> Self
{
from.into_result()
}
}
impl From<GhostResult> for types::GHOST_TSuccess
{
#[inline] fn from(from: GhostResult) -> Self
{
Self::from_result(from)
}
}
impl fmt::Display for types::GHOST_TSuccess
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Failure => write!(f, "Failure"),
Self::Success => write!(f, "Success"),
}
}
}
#[cfg(nightly)]
impl std::ops::Try for types::GHOST_TSuccess
{
type Ok = ();
type Error = GhostError;
fn into_result(self) -> Result<Self::Ok, Self::Error>
{
self.into()
}
fn from_error(_: Self::Error) -> Self
{
Self::Failure
}
fn from_ok(_: Self::Ok) -> Self
{
Self::Success
}
}
impl types::GHOST_TSuccess
{
/// Consume this instance into `Result<(), GhostError>`.
///
pub const fn into_result(self) -> GhostResult
{
match self {
types::GHOST_TSuccess::Success => Ok(()),
types::GHOST_TSuccess::Failure => Err(GhostError(())),
}
}
/// Convert a `GhostResult` into `GHOST_TSuccess`.
pub const fn from_result(from: GhostResult) -> Self
{
match from {
Ok(_) => Self::Success,
Err(_) => Self::Failure,
}
}
}

@ -1,12 +1,15 @@
//! GHOST_Types.h //! Strongly typed safe opaque pointers to GHOST handles.
//!
//! We define opaque ZST structures for each handle type, these then implement the [`GhostHandle`] trait, which allows them to be used for type arguments to [`Handle<T>`](Handle).
use super::*; use super::*;
use types::{Handle,GhostHandle}; pub use types::{Handle,GhostHandle};
macro_rules! handle { macro_rules! handle {
($name:ident, $inner_name:ident) => { ($name:ident, $inner_name:ident) => {
#[cfg(nightly)] pub struct $inner_name(!); #[cfg(nightly)] pub struct $inner_name(!);
#[cfg(not(nightly))] pub struct $inner_name(()); #[cfg(not(nightly))] pub struct $inner_name(());
impl private::Sealed for $inner_name{}
impl GhostHandle for $inner_name{} impl GhostHandle for $inner_name{}
pub type $name = *mut Handle<$inner_name>; pub type $name = *mut Handle<$inner_name>;
}; };

@ -1,22 +1,34 @@
//! Rust bindings and interop for GHOST. //! Rust bindings and interop for GHOST.
//! //!
//! # Layout //! # Layout
//! ABI-compatable types and functions are all prefixed with `GHOST_` and are exported in the `c` module (TODO). //! ABI-compatable types and functions are all prefixed with `GHOST_` and are exported in the `c` module.
//! I've attempted to provide more Rust-idiomatic API variants as well, which have non-prefixed names. //! I've attempted to provide more Rust-idiomatic API variants as well, which have non-prefixed names.
//!
//! # Namespace polution and interface stablility
//! Eventually I intend to have /most/ (if not all) C compatable types either reimplemented or aliased to non-prefixed Rust types.
//! At present though this is not the case, and prefixed names that can double as ergonomic Rust types are not aliased to them, so you'll have to use both often.
//! The reimplementation is undergoing, the aliasing not yet.
#![cfg_attr(nightly, feature(never_type))] #![cfg_attr(nightly, feature(never_type))]
#![cfg_attr(nightly, feature(try_trait))]
#![allow(dead_code)] #![allow(dead_code)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#[cfg(nightly)] pub type PVoid = *const !; #[cfg(nightly)] pub(crate) type PVoid = *const !;
#[cfg(not(nightly))] pub type PVoid = *const libc::c_void; #[cfg(not(nightly))] pub(crate) type PVoid = *const libc::c_void;
#[cfg(nightly)] pub type PVoidMut = *mut !; #[cfg(nightly)] pub(crate) type PVoidMut = *mut !;
#[cfg(not(nightly))] pub type PVoidMut = *mut libc::c_void; #[cfg(not(nightly))] pub(crate) type PVoidMut = *mut libc::c_void;
mod private
{
pub trait Sealed{}
}
pub mod handle; pub mod handle;
pub mod event; pub mod event;
pub mod types; pub mod types;
pub mod error;
pub mod c; pub mod c;
@ -27,4 +39,25 @@ mod tests {
fn it_works() { fn it_works() {
// nothing yet. // nothing yet.
} }
macro_rules! static_assert {
($expression:expr $(, $message:literal)?) => {
const _: [u8; 1] = [0u8; (!!($expression)) as usize];
};
}
#[repr(C)]
enum DummyCEnum
{
One = 0,
Two,
Three,
}
// validate the dumb bitflags hack
static_assert!(std::mem::size_of::<types::GHOST_GLFlags>() == std::mem::size_of::<DummyCEnum>(),
"C enum is not of type `int`. Either that or #[repr(transparent)] is broken. Aborting.");
static_assert!(std::mem::align_of::<types::GHOST_GLFlags>() == std::mem::align_of::<DummyCEnum>(),
"C enum has unexpected alignment differing from type `int`. Either that or #[repr(transparent)] is broken. Aborting.");
// ..are there others?
} }

@ -1,4 +1,4 @@
//! GHOST_Types.h //! `GHOST_Types.h`
use super::*; use super::*;
use libc::{ use libc::{
c_int, c_int,
@ -9,12 +9,31 @@ use std::{
}, },
}; };
/// Trait for opaque types that represent GHOST handles /// Trait for opaque types that represent GHOST handles.
/// API maps vaguely to `GHOST_DECLARE_HANDLE`. ///
pub trait GhostHandle{} /// API maps to `GHOST_DECLARE_HANDLE`, and ABI maps exactly for `Handle<T>` where `T` is an implementor of this.
///
/// Opaque handle type for GHOST C++ object. /// See [`handle`](../handle/index.html) for implementors.
pub trait GhostHandle: private::Sealed{}
/// Opaque handle type for GHOST C++ object pointers.
///
/// These have the same ABI as structs defined with `GHOST_DECLARE_HANDLE`, but it is irrelevant as its use is just for typing pointers. This field should not be instantiated itself, but instead passed as `&Handle<T>` or `&mut Handle<T>` to provide safe interfaces for these C APIs.
///
/// The following is safe:
/// ```
/// # use ghost::{error::GhostResult, c::*, handle::*};
/// extern "C" {fn some_internal_call(window: GHOST_WindowHandle) -> GHOST_TSuccess;}
///
/// fn do_something_to_window(window: &mut Handle<Window>) -> GhostResult
/// {
/// unsafe {
/// some_internal_call(window as GHOST_WindowHandle).into() // `GHOST_WindowHandle` is an alias for `*mut Handle<Window>`
/// }
/// }
/// ```
#[repr(C)] #[repr(C)]
#[derive(Debug)]
pub struct Handle<T>(c_int, PhantomData<T>) pub struct Handle<T>(c_int, PhantomData<T>)
where T: GhostHandle; where T: GhostHandle;
@ -29,29 +48,117 @@ pub type GHOST_TUns64 = libc::c_ulonglong;
pub type GHOST_TUserDataPtr = PVoidMut; pub type GHOST_TUserDataPtr = PVoidMut;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct GHOST_GLSettings pub struct GHOST_GLSettings
{ {
pub flags: c_int, pub flags: GHOST_GLFlags,
} }
#[repr(C)] /// dumb hack for bitflags
pub enum GHOST_GLFlags macro_rules! enum_flags {
{ ($name:ident {
$($const_name:ident = $value:expr,)*
}) => {
#[repr(transparent)] //this seems dodgy to me
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
pub struct $name(libc::c_int);
impl $name {
$(
#[allow(non_upper_case_globals)] pub const $const_name: $name = $name($value);
)*
pub const fn from_int(int: libc::c_int) -> Self
{
Self(int)
}
pub const fn as_int(&self) -> libc::c_int
{
self.0
}
pub const fn has(&self, other: &Self) -> bool
{
(self.0 & other.0) == other.0
}
}
impl From<libc::c_int> for $name
{
#[inline] fn from(from: libc::c_int) -> Self
{
Self(from)
}
}
impl From<$name> for libc::c_int
{
#[inline] fn from(from: $name) -> Self
{
from.0
}
}
impl ::std::ops::BitAnd for $name
{
type Output = Self;
#[inline] fn bitand(self, rhs: Self) -> Self {
Self(rhs.0 & self.0)
}
}
impl ::std::ops::BitOr for $name
{
type Output = Self;
#[inline] fn bitor(self, rhs: Self) -> Self {
Self(rhs.0 | self.0)
}
}
impl ::std::ops::BitXor for $name
{
type Output = Self;
#[inline] fn bitxor(self, rhs: Self) -> Self {
Self(rhs.0 ^ self.0)
}
}
impl ::std::ops::Not for $name
{
type Output = Self;
#[inline] fn not(self,) -> Self {
Self(!self.0)
}
}
impl ::std::ops::BitAndAssign for $name {
#[inline] fn bitand_assign(&mut self, rhs: Self) { self.0 &= rhs.0; }
}
impl ::std::ops::BitOrAssign for $name {
#[inline] fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; }
}
impl ::std::ops::BitXorAssign for $name{
#[inline] fn bitxor_assign(&mut self, rhs: Self) { self.0 ^= rhs.0; }
}
};
}
enum_flags!(GHOST_GLFlags {
StereoVisual = 1<<0, StereoVisual = 1<<0,
DebugContext = 1<<1, DebugContext = 1<<1,
AlphaBackground = 1<<2, AlphaBackground = 1<<2,
} });
#[repr(C)] enum_flags!(GHOST_DialogOptions
pub enum GHOST_DialogOptions
{ {
DialogWarning = 1<<0, DialogWarning = 1<<0,
DialogError = 1<<1, DialogError = 1<<1,
} });
pub type DialogOptions = GHOST_DialogOptions;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TSuccess pub enum GHOST_TSuccess
{ {
Failure = 0, Failure = 0,
@ -59,6 +166,7 @@ pub enum GHOST_TSuccess
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TTabletMode pub enum GHOST_TTabletMode
{ {
None = 0, None = 0,
@ -77,6 +185,7 @@ impl Default for GHOST_TTabletMode
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TTabletAPI pub enum GHOST_TTabletAPI
{ {
None = 0, None = 0,
@ -95,6 +204,7 @@ impl Default for GHOST_TTabletAPI
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct GHOST_TabletData pub struct GHOST_TabletData
{ {
pub active: GHOST_TTabletMode, pub active: GHOST_TTabletMode,
@ -105,6 +215,7 @@ pub struct GHOST_TabletData
impl GHOST_TabletData impl GHOST_TabletData
{ {
/// We use `const fn` initialiser here instead of `GHOST_TABLET_DATA_NONE` constant.
pub const fn none() -> Self{ pub const fn none() -> Self{
Self{active: GHOST_TTabletMode::None, pressure: 1.0, xtilt: 0.0, ytilt: 0.0} Self{active: GHOST_TTabletMode::None, pressure: 1.0, xtilt: 0.0, ytilt: 0.0}
} }
@ -119,6 +230,7 @@ impl Default for GHOST_TabletData
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TVisibility pub enum GHOST_TVisibility
{ {
Not =0, Not =0,
@ -136,13 +248,23 @@ impl Default for GHOST_TVisibility
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Copy)]
pub enum GHOST_TFireTimeConstant pub enum GHOST_TFireTimeConstant
{ {
Never = 0xFFFFFFFF, Never = 0xFFFFFFFF,
} }
impl Default for GHOST_TFireTimeConstant
{
#[inline]
fn default() -> Self
{
Self::Never
}
}
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TModifierKeyMask { pub enum GHOST_TModifierKeyMask {
LeftShift = 0, LeftShift = 0,
RightShift, RightShift,
@ -155,17 +277,19 @@ pub enum GHOST_TModifierKeyMask {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TWindowState { pub enum GHOST_TWindowState {
Normal = 0, Normal = 0,
Maximized, Maximized,
Minimized, Minimized,
FullScreen, FullScreen,
Embedded, Embedded,
// Modified, // Modified,
// UnModified, // UnModified,
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TWindowOrder pub enum GHOST_TWindowOrder
{ {
Top =0, Top =0,
@ -173,67 +297,70 @@ pub enum GHOST_TWindowOrder
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TDrawingContextType { pub enum GHOST_TDrawingContextType {
None = 0, None = 0,
OpenGL, OpenGL,
//D3D, //lol nope //D3D, //lol nope
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TButtonMask{ pub enum GHOST_TButtonMask{
Left = 0, Left = 0,
Middle, Middle,
Right, Right,
Button4, Button4,
Button5, Button5,
/* Trackballs and programmable buttons */ /* Trackballs and programmable buttons */
Button6, Button6,
Button7, Button7,
NumMasks // Should we even keep this? I guess yeah for ABI compatability NumMasks // Should we even keep this? I guess yeah for ABI compatability
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TEventType { pub enum GHOST_TEventType {
Unknown = 0, Unknown = 0,
CursorMove, CursorMove,
ButtonDown, ButtonDown,
ButtonUp, ButtonUp,
Wheel, Wheel,
Trackpad, Trackpad,
//#ifdef WITH_INPUT_NDOF //#ifdef WITH_INPUT_NDOF
// NDOFMotion, /// N degree of freedom device motion event // NDOFMotion, /// N degree of freedom device motion event
// NDOFButton, /// N degree of freedom device button event // NDOFButton, /// N degree of freedom device button event
//#endif //#endif
KeyDown, KeyDown,
KeyUp, KeyUp,
// KeyAuto, // KeyAuto,
QuitRequest, QuitRequest,
WindowClose, WindowClose,
WindowActivate, WindowActivate,
WindowDeactivate, WindowDeactivate,
WindowUpdate, WindowUpdate,
WindowSize, WindowSize,
WindowMove, WindowMove,
WindowDPIHintChanged, WindowDPIHintChanged,
DraggingEntered, DraggingEntered,
DraggingUpdated, DraggingUpdated,
DraggingExited, DraggingExited,
DraggingDropDone, DraggingDropDone,
OpenMainFile, // Needed for Cocoa to open double-clicked .blend file at startup OpenMainFile, // Needed for Cocoa to open double-clicked .blend file at startup
NativeResolutionChange, // Needed for Cocoa when window moves to other display NativeResolutionChange, // Needed for Cocoa when window moves to other display
Timer, Timer,
ImeCompositionStart, ImeCompositionStart,
ImeComposition, ImeComposition,
ImeCompositionEnd, ImeCompositionEnd,
NumEventTypes NumEventTypes
} }

Loading…
Cancel
Save