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
use static_assertions::assert_not_impl_all;
use std::{cell::Cell, ffi::CStr, marker::PhantomData};

use crate::bind;

/// A version for SDL2.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SdlVersion {
    /// A major number in the version.
    pub major: u8,
    /// A minor number in the version.
    pub minor: u8,
    /// A patch number in the version.
    pub patch: u8,
}

impl From<bind::SDL_version> for SdlVersion {
    fn from(
        bind::SDL_version {
            major,
            minor,
            patch,
        }: bind::SDL_version,
    ) -> Self {
        Self {
            major,
            minor,
            patch,
        }
    }
}

/// A root controller for SDL2. But some sub-feature does not require `Sdl`.
pub struct Sdl {
    _phantom: PhantomData<Cell<u8>>,
}

assert_not_impl_all!(Sdl: Send, Sync);

impl Sdl {
    /// Setup the SDL2 system.
    ///
    /// # Panics
    ///
    /// Panics if SDL2 system is already initialized such as there are existing `Sdl` instances
    #[must_use]
    pub fn new() -> Self {
        let ret = unsafe {
            bind::SDL_SetMainReady();
            bind::SDL_Init(0)
        };
        if ret != 0 {
            Self::error_then_panic("Sdl")
        }
        Self {
            _phantom: PhantomData,
        }
    }

    /// Returns the version of SDL2.
    #[must_use]
    pub fn version() -> SdlVersion {
        use bind::SDL_version;
        let mut ver = SDL_version {
            major: 0,
            minor: 0,
            patch: 0,
        };
        unsafe { bind::SDL_GetVersion(&mut ver) }
        ver.into()
    }

    /// Returns the revision string.
    #[must_use]
    pub fn revision_str() -> &'static str {
        let raw_str = unsafe { bind::SDL_GetRevision() };
        unsafe { std::ffi::CStr::from_ptr(raw_str) }
            .to_str()
            .expect("Getting revision failed")
    }

    /// Returns the revision number.
    #[must_use]
    pub fn revision_num() -> u32 {
        (unsafe { bind::SDL_GetRevisionNumber() }) as u32
    }

    /// Gets the platform string.
    #[must_use]
    pub fn platform() -> &'static str {
        let cstr = unsafe { CStr::from_ptr(bind::SDL_GetPlatform()) };
        cstr.to_str().unwrap()
    }

    /// On an unrecoverable error detected, panics with the provided `context`.
    #[inline(always)]
    pub fn error_then_panic(context: &'static str) -> ! {
        eprintln!("{} error: {}", context, Self::error());
        panic!("Unrecoverable Sdl error occurred");
    }

    /// Reads the error string from SDL2.
    #[must_use]
    pub fn error() -> String {
        let raw_str = unsafe { bind::SDL_GetError() };
        let error = unsafe { std::ffi::CStr::from_ptr(raw_str) }
            .to_str()
            .expect("Getting error failed")
            .to_owned();
        unsafe { bind::SDL_ClearError() }
        error
    }
}

impl Default for Sdl {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for Sdl {
    fn drop(&mut self) {
        unsafe { bind::SDL_Quit() }
    }
}