1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#![cfg_attr(not(any(target_os = "macos", target_os = "ios")), no_std)]
#![warn(missing_docs)]
#![allow(unused_imports)]

//! A wrapper lib for SDL2, hereafter referred to as just "SDL" for simplicity.
//!
//! The bindings themselves are provided by
//! [`fermium`](https://docs.rs/fermium), this crate attempts to make it safe
//! and easy to use from Rust.
//!
//! ## Restrictions
//!
//! * The library is very incomplete
//! * The library only lets you have a single window because that's all that I
//!   personally need and it's a lot easier to make all the safety stuff work
//!   out that way. If you want more than one window you can fork this and try
//!   to figure all that out.
//!
//! ## `no_std` Support
//!
//! Yes on Win/Linux, no on Mac. On Windows and Linux you can start SDL from any
//! thread as long as you stick to controlling it from just that thread. On Mac
//! you _must_ start SDL from the main thread, which this library checks, which
//! requires the standard library because of how the `objc` crate is written.

/*

Current TODO:

?

STRETCH GOALS:

Make SDL_WasInit better
message boxes?
window flags newtype

Ideas:

struct CStrFormatter(*const c_char);
impl CStrFormatter { pub unsafe fn new(ptr: *const c_char) -> Self { .. }
impl core::fmt::Debug for CStrFormatter { ... }

NEXT FERMIUM:

Expose `SDL_GetErrorMsg`, which is apparently thread safe?
Fix mouse button definitions.

*/

pub use fermium;
pub(crate) use fermium::{c_char, c_void};

extern crate alloc;
use alloc::{borrow::Cow, format, string::String, sync::Arc, vec, vec::Vec};

use core::{
  convert::TryFrom,
  marker::PhantomData,
  mem::ManuallyDrop,
  ptr::null_mut,
  sync::atomic::{AtomicBool, Ordering},
};

// Declaration MUST stay before all modules because Rust is stupid.
macro_rules! cow_str {
  ($l:literal) => {
    alloc::borrow::Cow::Borrowed($l)
  };
  ($i:ident) => {
    alloc::borrow::Cow::Borrowed($i)
  };
  ($($tokens:tt)*) => {
    alloc::borrow::Cow::Owned(format!($($tokens)*))
  };
}

mod initialization;
pub use initialization::InitFlags;
pub(crate) use initialization::*;
mod sdl;
pub use sdl::*;
mod window;
pub use window::*;
mod event;
pub use event::*;
mod audio;
pub use audio::*;
mod controller;
pub use controller::*;

/// Clone On Write, specific to `&str` and `String`.
///
/// Used where possible to save on allocations.
pub type CowStr = Cow<'static, str>;

trait StrExt {
  fn alloc_c_str(&self) -> Vec<c_char>;
}
impl StrExt for str {
  fn alloc_c_str(&self) -> Vec<c_char> {
    self
      .bytes()
      .map(|c| c as c_char)
      .take_while(|&c| c != 0)
      .chain(Some(0))
      .collect()
  }
}

/// Obtains the current SDL error message.
///
/// ## Safety
///
/// * This is an unsynchronized global. Data races and such.
/// * For the safe version see [`SDL::get_error`].
pub unsafe fn get_error_unchecked() -> String {
  // SDL_ERRBUFIZE is 1024
  let mut v = Vec::with_capacity(1024);
  let mut err_p = fermium::SDL_GetError();
  while *err_p != 0 {
    v.push(*err_p as u8);
    err_p = err_p.offset(1);
  }
  String::from_utf8_lossy(&v).into_owned()
}