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).
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
Note: List is in-progress and doesn't show everything
@ -49,17 +58,18 @@
- [X] GHOST_DialogOptions
- [X] GHOST_TabletData
- [X] /Constants/
- [X] =GHOST_TABLET_DATA_NONE=
* reimpl as ~const fn~ (~GHOST_TabletData::none()~)
- [X] =GHOST_TABLET_DATA_NONE= (reimpl as ~const fn GHOST_TabletData::none()~)
*** 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
List and implementation status of features
- [-] Types
- [X] [[Handles]]: ~handle.rs~
- [X] [[Error handling]]: ~error.rs~
- [ ] Events: ~event.rs~
**** Handles
@ -70,12 +80,37 @@
Since handles are just types pointers, they are only ever used as such and shouldn't exist themselves.
***** Example
#+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 {
internal_call(window as GHOST_WindowHandle).into()
}
some_internal_call(window as GHOST_WindowHandle).into()
}
#+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 types::{Handle,GhostHandle};
pub use types::{Handle,GhostHandle};
macro_rules! handle {
($name:ident, $inner_name:ident) => {
#[cfg(nightly)] pub struct $inner_name(!);
#[cfg(not(nightly))] pub struct $inner_name(());
impl private::Sealed for $inner_name{}
impl GhostHandle for $inner_name{}
pub type $name = *mut Handle<$inner_name>;
};

@ -1,22 +1,34 @@
//! Rust bindings and interop for GHOST.
//!
//! # 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.
//!
//! # 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(non_camel_case_types)]
#[cfg(nightly)] pub type PVoid = *const !;
#[cfg(not(nightly))] pub type PVoid = *const libc::c_void;
#[cfg(nightly)] pub type PVoidMut = *mut !;
#[cfg(not(nightly))] pub type PVoidMut = *mut libc::c_void;
#[cfg(nightly)] pub(crate) type PVoid = *const !;
#[cfg(not(nightly))] pub(crate) type PVoid = *const libc::c_void;
#[cfg(nightly)] pub(crate) type PVoidMut = *mut !;
#[cfg(not(nightly))] pub(crate) type PVoidMut = *mut libc::c_void;
mod private
{
pub trait Sealed{}
}
pub mod handle;
pub mod event;
pub mod types;
pub mod error;
pub mod c;
@ -27,4 +39,25 @@ mod tests {
fn it_works() {
// 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 libc::{
c_int,
@ -9,12 +9,31 @@ use std::{
},
};
/// Trait for opaque types that represent GHOST handles
/// API maps vaguely to `GHOST_DECLARE_HANDLE`.
pub trait GhostHandle{}
/// Opaque handle type for GHOST C++ object.
/// Trait for opaque types that represent GHOST handles.
///
/// API maps to `GHOST_DECLARE_HANDLE`, and ABI maps exactly for `Handle<T>` where `T` is an implementor of this.
///
/// 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)]
#[derive(Debug)]
pub struct Handle<T>(c_int, PhantomData<T>)
where T: GhostHandle;
@ -29,29 +48,117 @@ pub type GHOST_TUns64 = libc::c_ulonglong;
pub type GHOST_TUserDataPtr = PVoidMut;
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct GHOST_GLSettings
{
pub flags: c_int,
pub flags: GHOST_GLFlags,
}
#[repr(C)]
pub enum GHOST_GLFlags
{
/// dumb hack for bitflags
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,
DebugContext = 1<<1,
AlphaBackground = 1<<2,
}
});
#[repr(C)]
pub enum GHOST_DialogOptions
enum_flags!(GHOST_DialogOptions
{
DialogWarning = 1<<0,
DialogError = 1<<1,
}
pub type DialogOptions = GHOST_DialogOptions;
});
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TSuccess
{
Failure = 0,
@ -59,6 +166,7 @@ pub enum GHOST_TSuccess
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TTabletMode
{
None = 0,
@ -77,6 +185,7 @@ impl Default for GHOST_TTabletMode
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TTabletAPI
{
None = 0,
@ -95,6 +204,7 @@ impl Default for GHOST_TTabletAPI
#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct GHOST_TabletData
{
pub active: GHOST_TTabletMode,
@ -105,6 +215,7 @@ pub struct GHOST_TabletData
impl GHOST_TabletData
{
/// We use `const fn` initialiser here instead of `GHOST_TABLET_DATA_NONE` constant.
pub const fn none() -> Self{
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)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TVisibility
{
Not =0,
@ -136,13 +248,23 @@ impl Default for GHOST_TVisibility
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Copy)]
pub enum GHOST_TFireTimeConstant
{
Never = 0xFFFFFFFF,
}
impl Default for GHOST_TFireTimeConstant
{
#[inline]
fn default() -> Self
{
Self::Never
}
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TModifierKeyMask {
LeftShift = 0,
RightShift,
@ -155,17 +277,19 @@ pub enum GHOST_TModifierKeyMask {
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TWindowState {
Normal = 0,
Maximized,
Minimized,
FullScreen,
Embedded,
// Modified,
// UnModified,
Normal = 0,
Maximized,
Minimized,
FullScreen,
Embedded,
// Modified,
// UnModified,
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TWindowOrder
{
Top =0,
@ -173,67 +297,70 @@ pub enum GHOST_TWindowOrder
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TDrawingContextType {
None = 0,
OpenGL,
//D3D, //lol nope
None = 0,
OpenGL,
//D3D, //lol nope
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TButtonMask{
Left = 0,
Middle,
Right,
Button4,
Button5,
/* Trackballs and programmable buttons */
Button6,
Button7,
NumMasks // Should we even keep this? I guess yeah for ABI compatability
Left = 0,
Middle,
Right,
Button4,
Button5,
/* Trackballs and programmable buttons */
Button6,
Button7,
NumMasks // Should we even keep this? I guess yeah for ABI compatability
}
#[repr(C)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum GHOST_TEventType {
Unknown = 0,
Unknown = 0,
CursorMove,
ButtonDown,
ButtonUp,
Wheel,
Trackpad,
CursorMove,
ButtonDown,
ButtonUp,
Wheel,
Trackpad,
//#ifdef WITH_INPUT_NDOF
// NDOFMotion, /// N degree of freedom device motion event
// NDOFButton, /// N degree of freedom device button event
//#endif
//#ifdef WITH_INPUT_NDOF
// NDOFMotion, /// N degree of freedom device motion event
// NDOFButton, /// N degree of freedom device button event
//#endif
KeyDown,
KeyUp,
// KeyAuto,
KeyDown,
KeyUp,
// KeyAuto,
QuitRequest,
QuitRequest,
WindowClose,
WindowActivate,
WindowDeactivate,
WindowUpdate,
WindowSize,
WindowMove,
WindowDPIHintChanged,
WindowClose,
WindowActivate,
WindowDeactivate,
WindowUpdate,
WindowSize,
WindowMove,
WindowDPIHintChanged,
DraggingEntered,
DraggingUpdated,
DraggingExited,
DraggingDropDone,
DraggingEntered,
DraggingUpdated,
DraggingExited,
DraggingDropDone,
OpenMainFile, // Needed for Cocoa to open double-clicked .blend file at startup
NativeResolutionChange, // Needed for Cocoa when window moves to other display
OpenMainFile, // Needed for Cocoa to open double-clicked .blend file at startup
NativeResolutionChange, // Needed for Cocoa when window moves to other display
Timer,
Timer,
ImeCompositionStart,
ImeComposition,
ImeCompositionEnd,
ImeCompositionStart,
ImeComposition,
ImeCompositionEnd,
NumEventTypes
NumEventTypes
}

Loading…
Cancel
Save