use image::{self, GenericImage};
use sdl2::pixels;
use sdl2::surface;
use sdl2::{self, Sdl};
use std::fmt;
use std::io::Read;
use audio;
use conf;
use event;
use filesystem::Filesystem;
use graphics;
use input;
use mouse;
use timer;
use GameError;
use GameResult;
pub struct Context {
pub conf: conf::Conf,
pub sdl_context: Sdl,
pub filesystem: Filesystem,
pub(crate) gfx_context: graphics::GraphicsContext,
pub event_context: sdl2::EventSubsystem,
pub timer_context: timer::TimeContext,
pub audio_context: audio::AudioContext,
pub gamepad_context: input::GamepadContext,
pub mouse_context: mouse::MouseContext,
pub default_font: graphics::Font,
#[allow(dead_code)]
debug_id: DebugId,
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Context: {:p}>", self)
}
}
fn set_window_icon(context: &mut Context) -> GameResult<()> {
let icon = &context.conf.window_setup.icon.clone();
if !icon.is_empty() {
let mut f = context.filesystem.open(icon)?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
let image = image::load_from_memory(&buf)?;
let image_data = &mut image.to_rgba();
let surface = surface::Surface::from_data(
image_data,
image.width(),
image.height(),
image.width() * 4,
pixels::PixelFormatEnum::ABGR8888,
)?;
let window = graphics::get_window_mut(context);
window.set_icon(surface);
};
Ok(())
}
impl Context {
fn from_conf(conf: conf::Conf, fs: Filesystem, sdl_context: Sdl) -> GameResult<Context> {
let debug_id = DebugId::new();
let video = sdl_context.video()?;
let audio_context = audio::AudioContext::new()?;
let event_context = sdl_context.event()?;
let timer_context = timer::TimeContext::new();
let font = graphics::Font::default_font()?;
let backend_spec = graphics::GlBackendSpec::from(conf.backend);
let graphics_context = graphics::GraphicsContext::new(
&video,
&conf.window_setup,
conf.window_mode,
backend_spec,
debug_id,
)?;
let gamepad_context = input::GamepadContext::new(&sdl_context)?;
let mouse_context = mouse::MouseContext::new();
let mut ctx = Context {
conf,
sdl_context,
filesystem: fs,
gfx_context: graphics_context,
event_context,
timer_context,
audio_context,
gamepad_context,
mouse_context,
default_font: font,
debug_id,
};
set_window_icon(&mut ctx)?;
Ok(ctx)
}
pub fn load_from_conf(
game_id: &'static str,
author: &'static str,
default_config: conf::Conf,
) -> GameResult<Context> {
let sdl_context = sdl2::init()?;
let mut fs = Filesystem::new(game_id, author)?;
let config = match fs.read_config() {
Ok(config) => {
info!("Loading conf.toml");
config
}
Err(e) => {
info!("Could not load conf.toml, using default: {:?}", e);
default_config
}
};
Context::from_conf(config, fs, sdl_context)
}
pub fn print_resource_stats(&mut self) {
self.filesystem.print_all();
}
pub fn quit(&mut self) -> GameResult<()> {
let now_dur = timer::get_time_since_start(self);
let now = timer::duration_to_f64(now_dur);
let e = sdl2::event::Event::Quit {
timestamp: now as u32,
};
self.event_context.push_event(e).map_err(GameError::from)
}
pub fn process_event(&mut self, event: &event::Event) {
match *event {
event::Event::MouseMotion { x, y, .. } => {
use graphics::Point2;
self.mouse_context
.set_last_position(Point2::new(x as f32, y as f32));
}
event::Event::Window {
win_event: sdl2::event::WindowEvent::Resized(_, _),
..
} => {
self.gfx_context.resize_viewport();
}
_ => {}
}
}
}
use std::path;
#[derive(Debug)]
pub struct ContextBuilder {
game_id: &'static str,
author: &'static str,
conf: conf::Conf,
paths: Vec<path::PathBuf>,
load_conf_file: bool,
}
impl ContextBuilder {
pub fn new(game_id: &'static str, author: &'static str) -> Self {
Self {
game_id,
author,
conf: conf::Conf::default(),
paths: vec![],
load_conf_file: true,
}
}
pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
self.conf.window_setup = setup;
self
}
pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
self.conf.window_mode = mode;
self
}
pub fn backend(mut self, backend: conf::Backend) -> Self {
self.conf.backend = backend;
self
}
pub fn add_resource_path<T>(mut self, path: T) -> Self
where
T: Into<path::PathBuf>,
{
self.paths.push(path.into());
self
}
pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
self.load_conf_file = load_conf_file;
self
}
pub fn build(self) -> GameResult<Context> {
let sdl_context = sdl2::init()?;
let mut fs = Filesystem::new(self.game_id, self.author)?;
let config = if self.load_conf_file {
fs.read_config().unwrap_or(self.conf)
} else {
self.conf
};
for path in &self.paths {
fs.mount(path, true);
}
Context::from_conf(config, fs, sdl_context)
}
}
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(debug_assertions)]
static DEBUG_ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(debug_assertions)]
pub(crate) struct DebugId(u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(not(debug_assertions))]
pub(crate) struct DebugId;
#[cfg(debug_assertions)]
impl DebugId {
pub fn new() -> Self {
let id = DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u32;
assert!(DEBUG_ID_COUNTER.load(Ordering::SeqCst) as u32 > id);
DebugId(id)
}
pub fn get(ctx: &Context) -> Self {
DebugId(ctx.debug_id.0)
}
pub fn assert(&self, ctx: &Context) {
if *self != ctx.debug_id {
panic!("Tried to use a resource with a Context that did not create it; this should never happen!");
}
}
}
#[cfg(not(debug_assertions))]
impl DebugId {
pub fn new() -> Self {
DebugId
}
pub fn get(_ctx: &Context) -> Self {
DebugId
}
pub fn assert(&self, _ctx: &Context) {
}
}