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
127
128
129
130
131
132
133
134
135
136
137
138
use std::io;
use std::error;
use std::fmt;
use std::os::raw::{c_int, c_long};
use std::path::Path;
use ::get_error;
use ::rwops::RWops;
use ::version::Version;
use sys::ttf;

use super::font::{
    internal_load_font,
    internal_load_font_at_index,
    internal_load_font_from_ll,
    Font,
};

/// A context manager for `SDL2_TTF` to manage C code initialization and clean-up.
#[must_use]
pub struct Sdl2TtfContext;

// Clean up the context once it goes out of scope
impl Drop for Sdl2TtfContext {
    fn drop(&mut self) {
        unsafe { ttf::TTF_Quit(); }
    }
}

impl Sdl2TtfContext {
    /// Loads a font from the given file with the given size in points.
    pub fn load_font<'ttf, P: AsRef<Path>>(&'ttf self, path: P, point_size: u16) -> Result<Font<'ttf,'static>, String> {
        internal_load_font(path, point_size)
    }

    /// Loads the font at the given index of the file, with the given
    /// size in points.
    pub fn load_font_at_index<'ttf, P: AsRef<Path>>(&'ttf self, path: P, index: u32, point_size: u16)
            -> Result<Font<'ttf,'static>, String> {
        internal_load_font_at_index(path, index, point_size)
    }

    /// Loads a font from the given SDL2 rwops object with the given size in
    /// points.
    pub fn load_font_from_rwops<'ttf,'r>(&'ttf self, rwops: RWops<'r>, point_size: u16)
            -> Result<Font<'ttf,'r>, String> {
        let raw = unsafe {
            ttf::TTF_OpenFontRW(rwops.raw(), 0, point_size as c_int)
        };
        if (raw as *mut ()).is_null() {
            Err(get_error())
        } else {
            Ok(internal_load_font_from_ll(raw, Some(rwops)))
        }
    }

    /// Loads the font at the given index of the SDL2 rwops object with
    /// the given size in points.
    pub fn load_font_at_index_from_rwops<'ttf,'r>(&'ttf self, rwops: RWops<'r>, index: u32,
            point_size: u16) -> Result<Font<'ttf,'r>, String> {
        let raw = unsafe {
            ttf::TTF_OpenFontIndexRW(rwops.raw(), 0, point_size as c_int,
                index as c_long)
        };
        if (raw as *mut ()).is_null() {
            Err(get_error())
        } else {
            Ok(internal_load_font_from_ll(raw, Some(rwops)))
        }
    }
}

/// Returns the version of the dynamically linked `SDL_TTF` library
pub fn get_linked_version() -> Version {
    unsafe {
        Version::from_ll(*ttf::TTF_Linked_Version())
    }
}

/// An error for when `sdl2_ttf` is attempted initialized twice
/// Necessary for context management, unless we find a way to have a singleton
#[derive(Debug)]
pub enum InitError {
    InitializationError(io::Error),
    AlreadyInitializedError,
}

impl error::Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::AlreadyInitializedError => {
                "SDL2_TTF has already been initialized"
            },
            InitError::InitializationError(ref error) => {
                error.description()
            },
        }
    }

    fn cause(&self) -> Option<&dyn error::Error> {
        match *self {
            InitError::AlreadyInitializedError => {
                None
            },
            InitError::InitializationError(ref error) => {
                Some(error)
            },
        }
    }
}

impl fmt::Display for InitError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        formatter.write_str("SDL2_TTF has already been initialized")
    }
}

/// Initializes the truetype font API and returns a context manager which will
/// clean up the library once it goes out of scope.
pub fn init() -> Result<Sdl2TtfContext, InitError> {
    unsafe {
        if ttf::TTF_WasInit() == 1 {
            Err(InitError::AlreadyInitializedError)
        } else if ttf::TTF_Init() == 0 {
            Ok(Sdl2TtfContext)
        } else {
            Err(InitError::InitializationError(
                io::Error::last_os_error()
                    ))
        }
    }
}

/// Returns whether library has been initialized already.
pub fn has_been_initialized() -> bool {
    unsafe {
        ttf::TTF_WasInit() == 1
    }
}