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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
use core::str;
use cstr_core::{CStr, CString};
use ndless::hw::has_colors;
use ndless::prelude::*;

// Setup linking for all targets.
#[cfg(target_os = "macos")]
mod mac {
	#[cfg(mac_framework)]
	#[link(name = "SDL", kind = "framework")]
	extern "C" {}

	#[cfg(not(mac_framework))]
	#[link(name = "SDL")]
	extern "C" {}
}

#[cfg(not(target_os = "macos"))]
mod others {
	#[link(name = "SDL")]
	extern "C" {}
}

pub mod ll {
	#![allow(non_camel_case_types)]

	use cty::{c_char as c_schar, c_int, c_uint, uint32_t};
	pub type SDL_errorcode = c_uint;

	pub const SDL_ENOMEM: SDL_errorcode = 0;
	pub const SDL_EFREAD: SDL_errorcode = 1;
	pub const SDL_EFWRITE: SDL_errorcode = 2;
	pub const SDL_EFSEEK: SDL_errorcode = 3;
	pub const SDL_UNSUPPORTED: SDL_errorcode = 4;
	pub const SDL_LASTERROR: SDL_errorcode = 5;

	pub type SDL_InitFlag = uint32_t;

	pub const SDL_INIT_TIMER: SDL_InitFlag = 0x0000_0001;
	pub const SDL_INIT_AUDIO: SDL_InitFlag = 0x0000_0010;
	pub const SDL_INIT_VIDEO: SDL_InitFlag = 0x0000_0020;
	pub const SDL_INIT_CDROM: SDL_InitFlag = 0x0000_0100;
	pub const SDL_INIT_JOYSTICK: SDL_InitFlag = 0x0000_0200;
	pub const SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x0010_0000;
	pub const SDL_INIT_EVENTTHREAD: SDL_InitFlag = 0x0100_0000;
	pub const SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000_FFFF;

	extern "C" {
		pub fn SDL_ClearError();
		pub fn SDL_Error(code: SDL_errorcode);
		pub fn SDL_SetError(fmt: *const c_schar);
		pub fn SDL_GetError() -> *mut c_schar;
		pub fn SDL_Quit();
		pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
		pub fn SDL_Init(flags: uint32_t) -> c_int;
		pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
		pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
		pub fn SDL_GetTicks() -> uint32_t;
	}
}

#[repr(C)]
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
pub struct Rect {
	pub x: i16,
	pub y: i16,
	pub w: u16,
	pub h: u16,
}

#[allow(non_snake_case)]
pub fn Rect(x: i16, y: i16, w: u16, h: u16) -> Rect {
	Rect { x, y, w, h }
}

impl Rect {
	pub fn new(x: i16, y: i16, w: u16, h: u16) -> Rect {
		Rect { x, y, w, h }
	}
}

#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
pub enum InitFlag {
	Timer = ll::SDL_INIT_TIMER as isize,
	Audio = ll::SDL_INIT_AUDIO as isize,
	Video = ll::SDL_INIT_VIDEO as isize,
	CDRom = ll::SDL_INIT_CDROM as isize,
	Joystick = ll::SDL_INIT_JOYSTICK as isize,
	NoParachute = ll::SDL_INIT_NOPARACHUTE as isize,
	EventThread = ll::SDL_INIT_EVENTTHREAD as isize,
	Everything = ll::SDL_INIT_EVERYTHING as isize,
}

#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
pub enum Error {
	NoMem = ll::SDL_ENOMEM as isize,
	Read = ll::SDL_EFREAD as isize,
	Write = ll::SDL_EFWRITE as isize,
	Seek = ll::SDL_EFSEEK as isize,
	Unsupported = ll::SDL_UNSUPPORTED as isize,
}

pub fn init(flags: &[InitFlag]) -> bool {
	unsafe {
		ll::SDL_Init(
			flags
				.iter()
				.fold(0u32, |flags, &flag| flags | flag as ll::SDL_InitFlag),
		) == 0
	}
}

pub fn init_default() -> Result<crate::video::Surface, String> {
	if !init(&[crate::InitFlag::Video]) {
		return Err("Failed to initialize video".to_string());
	}
	crate::video::set_video_mode(
		320,
		240,
		if has_colors() { 16 } else { 8 },
		&[crate::video::SurfaceFlag::SWSurface],
		&[],
	)
}

pub fn init_subsystem(flags: &[InitFlag]) -> bool {
	unsafe {
		ll::SDL_InitSubSystem(
			flags
				.iter()
				.fold(0u32, |flags, &flag| flags | flag as ll::SDL_InitFlag),
		) == 0
	}
}

pub fn quit_subsystem(flags: &[InitFlag]) {
	let flags = flags
		.iter()
		.fold(0u32, |flags, &flag| flags | flag as ll::SDL_InitFlag);

	unsafe {
		ll::SDL_QuitSubSystem(flags);
	}
}

pub fn quit() {
	unsafe {
		ll::SDL_Quit();
	}
}

pub fn was_inited(flags: &[InitFlag]) -> Vec<InitFlag> {
	let flags = flags
		.iter()
		.fold(0u32, |flags, &flag| flags | flag as ll::SDL_InitFlag);
	let bitflags = unsafe { ll::SDL_WasInit(flags) };

	let flags = [
		InitFlag::Timer,
		InitFlag::Audio,
		InitFlag::Video,
		InitFlag::CDRom,
		InitFlag::Joystick,
		InitFlag::NoParachute,
		InitFlag::EventThread,
		InitFlag::Everything,
	];

	flags
		.iter()
		.filter_map(|&flag| {
			if bitflags & (flag as ll::SDL_InitFlag) != 0 {
				Some(flag)
			} else {
				None
			}
		})
		.collect()
}

pub fn get_error() -> String {
	unsafe {
		let cstr = ll::SDL_GetError() as *const cty::c_char;
		let slice = CStr::from_ptr(cstr).to_bytes();

		str::from_utf8(slice).unwrap().to_string()
	}
}

pub fn set_error(err: &str) {
	unsafe {
		ll::SDL_SetError(CString::new(err.as_bytes()).unwrap().as_ptr());
	}
}

pub fn set_error_from_code(err: Error) {
	unsafe { ll::SDL_Error(err as ll::SDL_errorcode) }
}

pub fn clear_error() {
	unsafe {
		ll::SDL_ClearError();
	}
}

pub fn get_ticks() -> usize {
	unsafe { ll::SDL_GetTicks() as usize }
}