@@ -0,0 +1,153 @@
extern crate sdl2;

use sdl2::event::Event;
use sdl2::image::{LoadTexture, INIT_PNG, INIT_JPG};
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::render::{TextureCreator, Texture};
use sdl2::ttf::{Font, Sdl2TtfContext};

use std::env;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::Hash;
use std::rc::Rc;

fn main() {
let args: Vec<_> = env::args().collect();

if args.len() < 3 {
println!("Usage: cargo run /path/to/image.(png|jpg) /path/to/font.(ttf|ttc|fon)")
} else {
let image_path = &args[1];
let font_path = &args[2];

let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let font_context = sdl2::ttf::init().unwrap();
let _image_context = sdl2::image::init(INIT_PNG | INIT_JPG).unwrap();
let window = video_subsystem
.window("rust-sdl2 resource-manager demo", 800, 600)
.position_centered()
.build()
.unwrap();
let mut canvas = window.into_canvas().software().build().unwrap();
let texture_creator = canvas.texture_creator();
let mut texture_manager = TextureManager::new(&texture_creator);
let mut font_manager = FontManager::new(&font_context);
let details = FontDetails {
path: font_path.clone(),
size: 32,
};

'mainloop: loop {
for event in sdl_context.event_pump().unwrap().poll_iter() {
match event {
Event::Quit { .. } => break 'mainloop,
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => break 'mainloop,
_ => {}
}
}
// will load the image texture + font only once
let texture = texture_manager.load(image_path).unwrap();
let font = font_manager.load(&details).unwrap();

// not recommended to create a texture from the font each iteration
// but it is the simplest thing to do for this example
let surface = font.render("Hello Rust!")
.blended(Color::RGBA(255, 0, 0, 255))
.unwrap();
let font_texture = texture_creator
.create_texture_from_surface(&surface)
.unwrap();

//draw all
canvas.clear();
canvas.copy(&texture, None, None).unwrap();
canvas.copy(&font_texture, None, None).unwrap();
canvas.present();
}
}
}

type TextureManager<'l, T> = ResourceManager<'l, String, Texture<'l>, TextureCreator<T>>;
type FontManager<'l> = ResourceManager<'l, FontDetails, Font<'l, 'static>, Sdl2TtfContext>;

// Generic struct to cache any resource loaded by a ResourceLoader
pub struct ResourceManager<'l, K, R, L>
where K: Hash + Eq,
L: 'l + ResourceLoader<'l, R>
{
loader: &'l L,
cache: HashMap<K, Rc<R>>,
}

impl<'l, K, R, L> ResourceManager<'l, K, R, L>
where K: Hash + Eq,
L: ResourceLoader<'l, R>
{
pub fn new(loader: &'l L) -> Self {
ResourceManager {
cache: HashMap::new(),
loader: loader,
}
}

// Generics magic to allow a HashMap to use String as a key
// while allowing it to use &str for gets
pub fn load<D>(&mut self, details: &D) -> Result<Rc<R>, String>
where L: ResourceLoader<'l, R, Args = D>,
D: Eq + Hash + ?Sized,
K: Borrow<D> + for<'a> From<&'a D>
{
self.cache
.get(details)
.cloned()
.map_or_else(|| {
let resource = Rc::new(self.loader.load(details)?);
self.cache.insert(details.into(), resource.clone());
Ok(resource)
},
Ok)
}
}

// TextureCreator knows how to load Textures
impl<'l, T> ResourceLoader<'l, Texture<'l>> for TextureCreator<T> {
type Args = str;
fn load(&'l self, path: &str) -> Result<Texture, String> {
println!("LOADED A TEXTURE");
self.load_texture(path)
}
}

// Font Context knows how to load Fonts
impl<'l> ResourceLoader<'l, Font<'l, 'static>> for Sdl2TtfContext {
type Args = FontDetails;
fn load(&'l self, details: &FontDetails) -> Result<Font<'l, 'static>, String> {
println!("LOADED A FONT");
self.load_font(&details.path, details.size)
}
}

// Generic trait to Load any Resource Kind
pub trait ResourceLoader<'l, R> {
type Args: ?Sized;
fn load(&'l self, data: &Self::Args) -> Result<R, String>;
}

// Information needed to load a Font
#[derive(PartialEq, Eq, Hash)]
pub struct FontDetails {
pub path: String,
pub size: u16,
}

impl<'a> From<&'a FontDetails> for FontDetails {
fn from(details: &'a FontDetails) -> FontDetails {
FontDetails {
path: details.path.clone(),
size: details.size,
}
}
}
@@ -54,28 +54,29 @@ fn run(font_path: &Path) {
.build()
.unwrap();

let mut renderer = window.renderer().build().unwrap();
let mut canvas = window.into_canvas().build().unwrap();
let texture_creator = canvas.texture_creator();

// Load a font
let mut font = ttf_context.load_font(font_path, 128).unwrap();
font.set_style(sdl2::ttf::STYLE_BOLD);

// render a surface, and convert it to a texture bound to the renderer
// render a surface, and convert it to a texture bound to the canvas
let surface = font.render("Hello Rust!")
.blended(Color::RGBA(255, 0, 0, 255)).unwrap();
let mut texture = renderer.create_texture_from_surface(&surface).unwrap();
let mut texture = texture_creator.create_texture_from_surface(&surface).unwrap();

renderer.set_draw_color(Color::RGBA(195, 217, 255, 255));
renderer.clear();
canvas.set_draw_color(Color::RGBA(195, 217, 255, 255));
canvas.clear();

let TextureQuery { width, height, .. } = texture.query();

// If the example text is too big for the screen, downscale it (and center irregardless)
let padding = 64;
let target = get_centered_rect(width, height, SCREEN_WIDTH - padding, SCREEN_HEIGHT - padding);

renderer.copy(&mut texture, None, Some(target)).unwrap();
renderer.present();
canvas.copy(&mut texture, None, Some(target)).unwrap();
canvas.present();

'mainloop: loop {
for event in sdl_context.event_pump().unwrap().poll_iter() {
@@ -14,7 +14,7 @@ pub fn main() {
.build()
.unwrap();

let mut renderer = window.renderer().present_vsync().build().unwrap();
let mut canvas = window.into_canvas().present_vsync().build().unwrap();

let mut tick = 0;

@@ -31,7 +31,7 @@ pub fn main() {

{
// Update the window title.
let mut window = renderer.window_mut().unwrap();
let mut window = canvas.window_mut();

let position = window.position();
let size = window.size();
@@ -46,8 +46,8 @@ pub fn main() {
tick += 1;
}

renderer.set_draw_color(Color::RGB(0, 0, 0));
renderer.clear();
renderer.present();
canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear();
canvas.present();
}
}
@@ -6,7 +6,7 @@ use std::ptr;
use std::ffi::CString;
use num::traits::ToPrimitive;
use libc::{c_void, c_int, c_char};
use render::Renderer;
use render::Canvas;
use surface::Surface;
use pixels;
use get_error;
@@ -698,7 +698,7 @@ pub trait DrawRenderer {
fn string<C: ToColor>(&self, x: i16, y: i16, s: &str, color: C) -> Result<(), String>;
}

impl<'a> DrawRenderer for Renderer<'a> {
impl<T, TC> DrawRenderer for Canvas<T, TC> {
fn pixel<C: ToColor>(&self, x: i16, y: i16, color: C) -> Result<(), String> {
let ret = unsafe { ll::pixelColor(self.raw(), x, y, color.as_u32()) };
if ret == 0 { Ok(()) } else { Err(get_error()) }
@@ -24,7 +24,7 @@ use std::os::raw::{c_int, c_char};
use std::ffi::CString;
use std::path::Path;
use surface::Surface;
use render::{Renderer, Texture};
use render::{TextureCreator, Texture};
use rwops::RWops;
use version::Version;
use get_error;
@@ -152,12 +152,12 @@ impl<'a> SaveSurface for Surface<'a> {
}
}

/// Method extensions for creating Textures from a Renderer
/// Method extensions for creating Textures from a TextureCreator
pub trait LoadTexture {
fn load_texture<P: AsRef<Path>>(&self, filename: P) -> Result<Texture, String>;
}

impl<'a> LoadTexture for Renderer<'a> {
impl<T> LoadTexture for TextureCreator<T> {
fn load_texture<P: AsRef<Path>>(&self, filename: P) -> Result<Texture, String> {
//! Loads an SDL Texture from a file
unsafe {
@@ -166,7 +166,7 @@ impl<'a> LoadTexture for Renderer<'a> {
if (raw as *mut ()).is_null() {
Err(get_error())
} else {
Ok(Texture::from_ll(self, raw))
Ok(self.raw_create_texture(raw))
}
}
}
@@ -4,7 +4,7 @@ use std::fmt;
use std::ptr;
use std::os::raw::{c_char,c_int};

use video::WindowRef;
use video::Window;
use get_error;

use sys::messagebox as ll;
@@ -144,7 +144,7 @@ impl error::Error for ShowMessageError {
pub fn show_simple_message_box<'a, W>(flags: MessageBoxFlag, title: &str,
message: &str, window: W)
-> Result<(), ShowMessageError>
where W: Into<Option<&'a WindowRef>>
where W: Into<Option<&'a Window>>
{
use self::ShowMessageError::*;
let result = unsafe {
@@ -183,7 +183,7 @@ where W: Into<Option<&'a WindowRef>>
pub fn show_message_box<'a, 'b, W, M>(flags:MessageBoxFlag, buttons:&'a [ButtonData], title:&str,
message:&str, window: W, scheme: M)
-> Result<ClickedButton<'a>,ShowMessageError>
where W: Into<Option<&'b WindowRef>>,
where W: Into<Option<&'b Window>>,
M: Into<Option<MessageBoxColorScheme>>,
{
let window = window.into();
@@ -344,7 +344,7 @@ impl MouseUtil {
}
}

pub fn warp_mouse_in_window(&self, window: &video::WindowRef, x: i32, y: i32) {
pub fn warp_mouse_in_window(&self, window: &video::Window, x: i32, y: i32) {
unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
}

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -2,6 +2,8 @@ use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::rc::Rc;

use rect::Rect;
use get_error;
use std::ptr;
@@ -13,18 +15,31 @@ use rwops::RWops;

use sys::surface as ll;

pub struct Surface<'a> {
/// Holds a `SDL_Surface`
///
/// When the `SurfaceContext` is dropped, it frees the `SDL_Surface`
///
/// *INTERNAL USE ONLY*
pub struct SurfaceContext<'a> {
raw: *mut ll::SDL_Surface,
_marker: PhantomData<&'a ()>
}

impl<'a> Drop for Surface<'a> {
impl<'a> Drop for SurfaceContext<'a> {
#[inline]
fn drop(&mut self) {
unsafe { ll::SDL_FreeSurface(self.raw); }
}
}

/// Holds a `Rc<SurfaceContext>`.
///
/// Note: If a `Surface` goes out of scope but it cloned its context,
/// then the `SDL_Surface` will not be free'd until there are no more references to the `SurfaceContext`.
pub struct Surface<'a> {
context: Rc<SurfaceContext<'a>>,
}

/// An unsized Surface reference.
///
/// This type is used whenever Surfaces need to be borrowed from the SDL library, without concern
@@ -54,38 +69,39 @@ impl<'a> Deref for Surface<'a> {

#[inline]
fn deref(&self) -> &SurfaceRef {
unsafe { mem::transmute(self.raw) }
unsafe { mem::transmute(self.context.raw) }
}
}

impl<'a> DerefMut for Surface<'a> {
#[inline]
fn deref_mut(&mut self) -> &mut SurfaceRef {
unsafe { mem::transmute(self.raw) }
unsafe { mem::transmute(self.context.raw) }
}
}

impl<'a> AsRef<SurfaceRef> for Surface<'a> {
#[inline]
fn as_ref(&self) -> &SurfaceRef {
unsafe { mem::transmute(self.raw) }
unsafe { mem::transmute(self.context.raw) }
}
}

impl<'a> AsMut<SurfaceRef> for Surface<'a> {
#[inline]
fn as_mut(&mut self) -> &mut SurfaceRef {
unsafe { mem::transmute(self.raw) }
unsafe { mem::transmute(self.context.raw) }
}
}


impl<'a> Surface<'a> {
pub unsafe fn from_ll<'b>(raw: *mut ll::SDL_Surface) -> Surface<'b> {
Surface {
let context = SurfaceContext {
raw: raw,
_marker: PhantomData
}
_marker: PhantomData,
};
Surface { context: Rc::new(context) }
}

/// Creates a new surface using a pixel format.
@@ -123,10 +139,7 @@ impl<'a> Surface<'a> {
if raw.is_null() {
Err(get_error())
} else {
Ok(Surface {
raw: raw,
_marker: PhantomData
})
Ok(Surface::from_ll(raw))
}
}
}
@@ -153,10 +166,7 @@ impl<'a> Surface<'a> {
if raw.is_null() {
Err(get_error())
} else {
Ok(Surface {
raw: raw,
_marker: PhantomData
})
Ok(Surface::from_ll(raw))
}
}
}
@@ -170,17 +180,18 @@ impl<'a> Surface<'a> {
if raw.is_null() {
Err(get_error())
} else {
Ok(Surface {
raw: raw,
_marker: PhantomData
})
Ok( unsafe{ Surface::from_ll(raw) } )
}
}

pub fn load_bmp<P: AsRef<Path>>(path: P) -> Result<Surface<'static>, String> {
let mut file = try!(RWops::from_file(path, "rb"));
Surface::load_bmp_rw(&mut file)
}

pub fn context(&self) -> Rc<SurfaceContext<'a>> {
self.context.clone()
}
}

impl SurfaceRef {

Large diffs are not rendered by default.

Oops, something went wrong.