From 19c5d126028ab443b0d2ae2935ecaad1e5944653 Mon Sep 17 00:00:00 2001 From: Andres Date: Fri, 14 Apr 2017 13:06:18 -0700 Subject: [PATCH] Split the Renderer Created a Canvas + TextureCreator --- examples/animation.rs | 13 +- examples/demo.rs | 8 +- examples/gfx_demo.rs | 17 +- examples/image_demo.rs | 9 +- examples/message-box.rs | 10 +- examples/renderer-target.rs | 53 +++ examples/renderer-texture.rs | 13 +- examples/renderer-yuv.rs | 11 +- examples/resource_manager.rs | 153 ++++++ examples/ttf_demo.rs | 15 +- examples/window-properties.rs | 10 +- src/sdl2/gfx/primitives.rs | 4 +- src/sdl2/image/mod.rs | 8 +- src/sdl2/messagebox.rs | 6 +- src/sdl2/mouse/mod.rs | 2 +- src/sdl2/render.rs | 849 ++++++++++++++++++---------------- src/sdl2/surface.rs | 53 ++- src/sdl2/video.rs | 206 ++++----- 18 files changed, 844 insertions(+), 596 deletions(-) create mode 100644 examples/renderer-target.rs create mode 100644 examples/resource_manager.rs diff --git a/examples/animation.rs b/examples/animation.rs index 8399c12e..6a041836 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -14,17 +14,18 @@ fn main() { let window = video_subsystem.window("SDL2", 640, 480) .position_centered().build().unwrap(); - let mut renderer = window.renderer() + let mut canvas = window.into_canvas() .accelerated().build().unwrap(); + let texture_creator = canvas.texture_creator(); - renderer.set_draw_color(sdl2::pixels::Color::RGBA(0,0,0,255)); + canvas.set_draw_color(sdl2::pixels::Color::RGBA(0,0,0,255)); let mut timer = sdl_context.timer().unwrap(); let mut event_pump = sdl_context.event_pump().unwrap(); let temp_surface = sdl2::surface::Surface::load_bmp(Path::new("assets/animate.bmp")).unwrap(); - let texture = renderer.create_texture_from_surface(&temp_surface).unwrap(); + let texture = texture_creator.create_texture_from_surface(&temp_surface).unwrap(); let center = Point::new(320,240); let mut source_rect = Rect::new(0, 0, 128, 82); @@ -45,9 +46,9 @@ fn main() { let ticks = timer.ticks(); source_rect.set_x((128 * ((ticks / 100) % 6) ) as i32); - renderer.clear(); - renderer.copy_ex(&texture, Some(source_rect), Some(dest_rect), 10.0, None, true, false).unwrap(); - renderer.present(); + canvas.clear(); + canvas.copy_ex(&texture, Some(source_rect), Some(dest_rect), 10.0, None, true, false).unwrap(); + canvas.present(); std::thread::sleep(Duration::from_millis(100)); } diff --git a/examples/demo.rs b/examples/demo.rs index f1c39023..f50ca9df 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -14,11 +14,11 @@ pub fn main() { .build() .unwrap(); - let mut renderer = window.renderer().build().unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); - renderer.set_draw_color(Color::RGB(255, 0, 0)); - renderer.clear(); - renderer.present(); + canvas.set_draw_color(Color::RGB(255, 0, 0)); + canvas.clear(); + canvas.present(); let mut event_pump = sdl_context.event_pump().unwrap(); 'running: loop { diff --git a/examples/gfx_demo.rs b/examples/gfx_demo.rs index 909ccd53..7374fc4f 100644 --- a/examples/gfx_demo.rs +++ b/examples/gfx_demo.rs @@ -17,7 +17,6 @@ macro_rules! rect( ); fn main() { - let sdl_context = sdl2::init().unwrap(); let video_subsys = sdl_context.video().unwrap(); let window = video_subsys.window("rust-sdl2_gfx: draw line & FPSManager", SCREEN_WIDTH, SCREEN_HEIGHT) @@ -26,11 +25,11 @@ fn main() { .build() .unwrap(); - let mut renderer = window.renderer().build().unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); - renderer.set_draw_color(pixels::Color::RGB(0, 0, 0)); - renderer.clear(); - renderer.present(); + canvas.set_draw_color(pixels::Color::RGB(0, 0, 0)); + canvas.clear(); + canvas.present(); let mut lastx = 0; let mut lasty = 0; @@ -50,20 +49,20 @@ fn main() { } else if keycode == Keycode::Space { println!("space down"); for i in 0..400 { - renderer.pixel(i as i16, i as i16, 0xFF000FFu32).unwrap(); + canvas.pixel(i as i16, i as i16, 0xFF000FFu32).unwrap(); } - renderer.present(); + canvas.present(); } } Event::MouseButtonDown {x, y, ..} => { let color = pixels::Color::RGB(x as u8, y as u8, 255); - let _ = renderer.line(lastx, lasty, x as i16, y as i16, color); + let _ = canvas.line(lastx, lasty, x as i16, y as i16, color); lastx = x as i16; lasty = y as i16; println!("mouse btn down at ({},{})", x, y); - renderer.present(); + canvas.present(); } _ => {} diff --git a/examples/image_demo.rs b/examples/image_demo.rs index 8774af52..2fc16f78 100755 --- a/examples/image_demo.rs +++ b/examples/image_demo.rs @@ -16,11 +16,12 @@ pub fn run(png: &Path) { .build() .unwrap(); - let mut renderer = window.renderer().software().build().unwrap(); - let texture = renderer.load_texture(png).unwrap(); + let mut canvas = window.into_canvas().software().build().unwrap(); + let texture_creator = canvas.texture_creator(); + let texture = texture_creator.load_texture(png).unwrap(); - renderer.copy(&texture, None, None).expect("Render failed"); - renderer.present(); + canvas.copy(&texture, None, None).expect("Render failed"); + canvas.present(); 'mainloop: loop { for event in sdl_context.event_pump().unwrap().poll_iter() { diff --git a/examples/message-box.rs b/examples/message-box.rs index a2e5aceb..4708d797 100644 --- a/examples/message-box.rs +++ b/examples/message-box.rs @@ -15,11 +15,11 @@ pub fn main() { .build() .unwrap(); - let mut renderer = window.renderer().build().unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); - renderer.set_draw_color(Color::RGB(255, 0, 0)); - renderer.clear(); - renderer.present(); + canvas.set_draw_color(Color::RGB(255, 0, 0)); + canvas.clear(); + canvas.present(); let mut event_pump = sdl_context.event_pump().unwrap(); 'running: loop { @@ -30,7 +30,7 @@ pub fn main() { show_simple_message_box(MESSAGEBOX_ERROR, "Some title", "Some information inside the window", - renderer.window()); + canvas.window()); match res { Ok(_) => {} Err(ShowMessageError::SdlError(string)) => { diff --git a/examples/renderer-target.rs b/examples/renderer-target.rs new file mode 100644 index 00000000..c766271e --- /dev/null +++ b/examples/renderer-target.rs @@ -0,0 +1,53 @@ +extern crate sdl2; + +use sdl2::event::Event; +use sdl2::keyboard::Keycode; +use sdl2::pixels::{Color, PixelFormatEnum}; +use sdl2::rect::{Point, Rect}; + +fn main() { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().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 creator = canvas.texture_creator(); + let mut texture = creator + .create_texture_target(PixelFormatEnum::RGBA8888, 400, 300) + .unwrap(); + + let mut angle = 0.0; + + '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, + _ => {} + } + } + angle = (angle + 0.5) % 360.; + { + let mut target = canvas.with_target(&mut texture).unwrap(); + target.clear(); + target.set_draw_color(Color::RGBA(255, 0, 0, 255)); + target.fill_rect(Rect::new(0, 0, 400, 300)).unwrap(); + } // <- drops the `target` so that the `canvas` can be used again + canvas.set_draw_color(Color::RGBA(0, 0, 0, 255)); + let dst = Some(Rect::new(0, 0, 400, 300)); + canvas.clear(); + canvas + .copy_ex(&texture, + None, + dst, + angle, + Some(Point::new(400, 300)), + false, + false) + .unwrap(); + canvas.present(); + } +} diff --git a/examples/renderer-texture.rs b/examples/renderer-texture.rs index 6c587f18..7997bc27 100644 --- a/examples/renderer-texture.rs +++ b/examples/renderer-texture.rs @@ -15,9 +15,10 @@ pub fn main() { .build() .unwrap(); - let mut renderer = window.renderer().build().unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); + let texture_creator = canvas.texture_creator(); - let mut texture = renderer.create_texture_streaming( + let mut texture = texture_creator.create_texture_streaming( PixelFormatEnum::RGB24, 256, 256).unwrap(); // Create a red-green gradient texture.with_lock(None, |buffer: &mut [u8], pitch: usize| { @@ -31,11 +32,11 @@ pub fn main() { } }).unwrap(); - renderer.clear(); - renderer.copy(&texture, None, Some(Rect::new(100, 100, 256, 256))).unwrap(); - renderer.copy_ex(&texture, None, + canvas.clear(); + canvas.copy(&texture, None, Some(Rect::new(100, 100, 256, 256))).unwrap(); + canvas.copy_ex(&texture, None, Some(Rect::new(450, 100, 256, 256)), 30.0, None, false, false).unwrap(); - renderer.present(); + canvas.present(); let mut event_pump = sdl_context.event_pump().unwrap(); diff --git a/examples/renderer-yuv.rs b/examples/renderer-yuv.rs index 4fc1bcdf..9ba10037 100644 --- a/examples/renderer-yuv.rs +++ b/examples/renderer-yuv.rs @@ -15,9 +15,10 @@ pub fn main() { .build() .unwrap(); - let mut renderer = window.renderer().build().unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); + let texture_creator = canvas.texture_creator(); - let mut texture = renderer.create_texture_streaming( + let mut texture = texture_creator.create_texture_streaming( PixelFormatEnum::IYUV, 256, 256).unwrap(); // Create a U-V gradient texture.with_lock(None, |buffer: &mut [u8], pitch: usize| { @@ -48,9 +49,9 @@ pub fn main() { } }).unwrap(); - renderer.clear(); - renderer.copy(&texture, None, Some(Rect::new(100, 100, 256, 256))).unwrap(); - renderer.present(); + canvas.clear(); + canvas.copy(&texture, None, Some(Rect::new(100, 100, 256, 256))).unwrap(); + canvas.present(); let mut event_pump = sdl_context.event_pump().unwrap(); diff --git a/examples/resource_manager.rs b/examples/resource_manager.rs new file mode 100644 index 00000000..420c96ea --- /dev/null +++ b/examples/resource_manager.rs @@ -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>; +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>, +} + +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(&mut self, details: &D) -> Result, String> + where L: ResourceLoader<'l, R, Args = D>, + D: Eq + Hash + ?Sized, + K: Borrow + 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 { + type Args = str; + fn load(&'l self, path: &str) -> Result { + 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, 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; +} + +// 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, + } + } +} diff --git a/examples/ttf_demo.rs b/examples/ttf_demo.rs index 5633289a..9a2d264b 100644 --- a/examples/ttf_demo.rs +++ b/examples/ttf_demo.rs @@ -54,19 +54,20 @@ 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(); @@ -74,8 +75,8 @@ fn run(font_path: &Path) { 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() { diff --git a/examples/window-properties.rs b/examples/window-properties.rs index 77cad9ff..76acbc2b 100644 --- a/examples/window-properties.rs +++ b/examples/window-properties.rs @@ -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(); } } diff --git a/src/sdl2/gfx/primitives.rs b/src/sdl2/gfx/primitives.rs index 7343062e..983303a7 100644 --- a/src/sdl2/gfx/primitives.rs +++ b/src/sdl2/gfx/primitives.rs @@ -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(&self, x: i16, y: i16, s: &str, color: C) -> Result<(), String>; } -impl<'a> DrawRenderer for Renderer<'a> { +impl DrawRenderer for Canvas { fn pixel(&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()) } diff --git a/src/sdl2/image/mod.rs b/src/sdl2/image/mod.rs index fa092574..57fd1a34 100755 --- a/src/sdl2/image/mod.rs +++ b/src/sdl2/image/mod.rs @@ -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>(&self, filename: P) -> Result; } -impl<'a> LoadTexture for Renderer<'a> { +impl LoadTexture for TextureCreator { fn load_texture>(&self, filename: P) -> Result { //! 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)) } } } diff --git a/src/sdl2/messagebox.rs b/src/sdl2/messagebox.rs index 17b7057e..d331a96d 100644 --- a/src/sdl2/messagebox.rs +++ b/src/sdl2/messagebox.rs @@ -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> +where W: Into> { use self::ShowMessageError::*; let result = unsafe { @@ -183,7 +183,7 @@ where W: Into> pub fn show_message_box<'a, 'b, W, M>(flags:MessageBoxFlag, buttons:&'a [ButtonData], title:&str, message:&str, window: W, scheme: M) -> Result,ShowMessageError> -where W: Into>, +where W: Into>, M: Into>, { let window = window.into(); diff --git a/src/sdl2/mouse/mod.rs b/src/sdl2/mouse/mod.rs index aab9d565..135b817d 100644 --- a/src/sdl2/mouse/mod.rs +++ b/src/sdl2/mouse/mod.rs @@ -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); } } diff --git a/src/sdl2/render.rs b/src/sdl2/render.rs index dc80d708..cd1bb838 100644 --- a/src/sdl2/render.rs +++ b/src/sdl2/render.rs @@ -25,31 +25,77 @@ //! //! --- //! -//! None of the draw methods in `Renderer` are expected to fail. +//! None of the draw methods in `Canvas` are expected to fail. //! If they do, a panic is raised and the program is aborted. -use video::{Window, WindowRef}; +use video::{Window, WindowContext}; use surface; -use surface::{Surface, SurfaceRef}; +use surface::{Surface, SurfaceRef, SurfaceContext}; use pixels; use pixels::PixelFormatEnum; use get_error; use std::fmt; use std::error::Error; +use std::marker::PhantomData; use std::mem; +use std::ops::Deref; use std::ptr; +use std::rc::Rc; use libc::{c_int, uint32_t, c_double, c_void}; use rect::Point; use rect::Rect; -use std::cell::UnsafeCell; use std::ffi::CStr; use num::FromPrimitive; use std::vec::Vec; -use std::rc::Rc; use common::{validate_int, IntegerOrSdlError}; use sys::render as ll; +/// Contains the description of an error returned by SDL +#[derive(Debug)] +pub struct SdlError(String); + +/// Possible errors returned by targetting a `Canvas` to render to a `Texture` +#[derive(Debug)] +pub enum TargetRenderError { + SdlError(SdlError), + NotSupported, +} + +impl fmt::Display for SdlError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let &SdlError(ref e) = self; + write!(f, "SDL error: {}", e) + } +} + +impl Error for SdlError { + fn description(&self) -> &str { + let &SdlError(ref e) = self; + e + } +} + +impl fmt::Display for TargetRenderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::TargetRenderError::*; + match *self { + SdlError(ref e) => e.fmt(f), + NotSupported => write!(f, "The renderer does not support the use of render targets"), + } + } +} + +impl Error for TargetRenderError { + fn description(&self) -> &str { + use self::TargetRenderError::*; + match *self { + SdlError(ref e) => e.description(), + NotSupported => "The renderer does not support the use of render targets", + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[repr(i32)] pub enum TextureAccess { @@ -137,119 +183,23 @@ impl RendererInfo { } } -pub enum RendererParent<'a> { - Surface(Surface<'a>), - Window(Window), -} - -/// 2D rendering context -pub struct Renderer<'a> { +/// Manages what keeps a SDL_Renderer alive +/// +/// When the `RendererContext` is dropped, it destroys the `SDL_Renderer` +pub struct RendererContext { raw: *mut ll::SDL_Renderer, - parent: Option>, - is_alive: Rc>, + _target: Rc, } -impl<'a> Drop for Renderer<'a> { +impl Drop for RendererContext { fn drop(&mut self) { unsafe { - *self.is_alive.get() = false; ll::SDL_DestroyRenderer(self.raw); }; } } -/// The type that allows you to build Window-based renderers. -/// -/// By default, the renderer builder will prioritize for a hardware-accelerated -/// renderer. -pub struct RendererBuilder { - window: Window, - index: Option, - renderer_flags: u32, -} - -impl RendererBuilder { - /// Initializes a new `RendererBuilder`. - pub fn new(window: Window) -> RendererBuilder { - RendererBuilder { - window: window, - // -1 means to initialize the first rendering driver supporting the - // renderer flags - index: None, - // no flags gives priority to available SDL_RENDERER_ACCELERATED - // renderers - renderer_flags: 0, - } - } - - /// Builds the renderer. - pub fn build(self) -> Result, IntegerOrSdlError> { - use common::IntegerOrSdlError::*; - let index = match self.index { - None => -1, - Some(index) => try!(validate_int(index, "index")), - }; - let raw = unsafe { ll::SDL_CreateRenderer(self.window.raw(), index, self.renderer_flags) }; - - if raw.is_null() { - Err(SdlError(get_error())) - } else { - unsafe { Ok(Renderer::from_ll(raw, RendererParent::Window(self.window))) } - } - } - - /// Sets the index of the rendering driver to initialize. - /// If you desire the first rendering driver to support the flags provided, - /// or if you're translating code from C which passes -1 for the index, - /// **do not** invoke the `index` method. - pub fn index(mut self, index: u32) -> RendererBuilder { - self.index = Some(index); - self - } - - /// Set the renderer to a software fallback. - /// This flag is accumulative, and may be specified with other flags. - pub fn software(mut self) -> RendererBuilder { - self.renderer_flags |= ll::SDL_RENDERER_SOFTWARE as u32; - self - } - - /// Set the renderer to use hardware acceleration. - /// This flag is accumulative, and may be specified with other flags. - pub fn accelerated(mut self) -> RendererBuilder { - self.renderer_flags |= ll::SDL_RENDERER_ACCELERATED as u32; - self - } - - /// Synchronize renderer `present` method calls with the refresh rate. - /// This flag is accumulative, and may be specified with other flags. - pub fn present_vsync(mut self) -> RendererBuilder { - self.renderer_flags |= ll::SDL_RENDERER_PRESENTVSYNC as u32; - self - } - - /// Set the renderer to support rendering to a texture. - /// This flag is accumulative, and may be specified with other flags. - pub fn target_texture(mut self) -> RendererBuilder { - self.renderer_flags |= ll::SDL_RENDERER_TARGETTEXTURE as u32; - self - } -} - -impl<'a> Renderer<'a> { - /// Creates a 2D software rendering context for a surface. - /// - /// This method should only fail if SDL2 is not built with rendering - /// support, or there's an out-of-memory error. - pub fn from_surface(surface: surface::Surface<'a>) -> Result, String> { - let raw_renderer = unsafe { ll::SDL_CreateSoftwareRenderer(surface.raw()) }; - if raw_renderer != ptr::null_mut() { - unsafe { Ok(Renderer::from_ll(raw_renderer, RendererParent::Surface(surface))) } - } else { - Err(get_error()) - } - } - +impl RendererContext { /// Gets information about the rendering context. pub fn info(&self) -> RendererInfo { unsafe { @@ -263,86 +213,279 @@ impl<'a> Renderer<'a> { } } - #[inline] - fn parent(&self) -> &RendererParent { - self.parent.as_ref().unwrap() - } - - #[inline] - fn parent_mut(&mut self) -> &mut RendererParent<'a> { - self.parent.as_mut().unwrap() - } - - /// Gets the associated window reference of the Renderer, if there is one. - #[inline] - pub fn window(&self) -> Option<&WindowRef> { - match self.parent() { - &RendererParent::Window(ref window) => Some(window), - _ => None, - } - } - - /// Gets the associated window reference of the Renderer, if there is one. - #[inline] - pub fn window_mut(&mut self) -> Option<&mut WindowRef> { - match self.parent_mut() { - &mut RendererParent::Window(ref mut window) => Some(window), - _ => None, - } - } - - /// Gets the associated surface reference of the Renderer, if there is one. - #[inline] - pub fn surface(&self) -> Option<&SurfaceRef> { - match self.parent() { - &RendererParent::Surface(ref surface) => Some(surface), - _ => None, - } - } - - /// Gets the associated surface reference of the Renderer, if there is one. - #[inline] - pub fn surface_mut(&mut self) -> Option<&mut SurfaceRef> { - match self.parent_mut() { - &mut RendererParent::Surface(ref mut surface) => Some(surface), - _ => None, - } - } - - #[inline] - fn unwrap_parent(mut self) -> RendererParent<'a> { - use std::mem; - mem::replace(&mut self.parent, None).unwrap() - } - - #[inline] - pub fn into_window(self) -> Option { - match self.unwrap_parent() { - RendererParent::Window(window) => Some(window), - _ => None, - } - } - - #[inline] - pub fn into_surface(self) -> Option> { - match self.unwrap_parent() { - RendererParent::Surface(surface) => Some(surface), - _ => None, - } - } - - /// Unwraps the window or surface the rendering context was created from. + /// Gets the raw pointer to the SDL_Renderer pub unsafe fn raw(&self) -> *mut ll::SDL_Renderer { self.raw } - pub unsafe fn from_ll(raw: *mut ll::SDL_Renderer, parent: RendererParent) -> Renderer { - Renderer { + pub unsafe fn from_ll(raw: *mut ll::SDL_Renderer, target: Rc) -> Self { + RendererContext { raw: raw, - parent: Some(parent), - is_alive: Rc::new(UnsafeCell::new(true)), + _target: target, } } + + unsafe fn set_raw_target(&self, raw_texture: *mut ll::SDL_Texture) -> Result<(), SdlError> { + if ll::SDL_SetRenderTarget(self.raw, raw_texture) == 0 { + Ok(()) + } else { + Err(SdlError(get_error())) + } + } +} + +impl Deref for Canvas { + type Target = RendererContext; + + fn deref(&self) -> &RendererContext { + self.context.as_ref() + } +} + +/// Manages and owns a target (`Surface`, `Window`, or `Texture`) and allows drawing in it. +/// +/// If the `Window` manipulates the shell of the Window, `Canvas` allows you to +/// manipulate both the shell and the inside of the window; +/// you can manipulate pixel by pexel (*not recommended*), lines, colored rectangles, or paste +/// `Texture`s to this `Canvas`. +/// +/// Drawing to the `Canvas` does not take effect immediately, it draws to a buffer until you +/// call `present()`, where all the operations you did until the last `present()` +/// are updated to your target +/// +/// Its context may be shared with the `TextureCreator`. +/// +/// The context will not be dropped until all references of it are out of scope. +pub struct Canvas { + target: T, + context: Rc>, +} + +/// Alias for a `Canvas` that was created out of a `Surface` +pub type SurfaceCanvas<'s> = Canvas, SurfaceContext<'s>>; + +/// Methods for the `SurfaceCanvas`. +impl<'s> Canvas, SurfaceContext<'s>> { + /// Creates a 2D software rendering context for a surface. + /// + /// This method should only fail if SDL2 is not built with rendering + /// support, or there's an out-of-memory error. + pub fn from_surface(surface: surface::Surface<'s>) -> Result { + let raw_renderer = unsafe { ll::SDL_CreateSoftwareRenderer(surface.raw()) }; + if raw_renderer != ptr::null_mut() { + let context = + Rc::new(unsafe { RendererContext::from_ll(raw_renderer, surface.context()) }); + Ok(Canvas { + target: surface, + context: context, + }) + } else { + Err(get_error()) + } + } + + /// Gets a reference to the associated surface of the Canvas + #[inline] + pub fn surface(&self) -> &SurfaceRef { + &self.target + } + + /// Gets a mutable reference to the associated surface of the Canvas + #[inline] + pub fn surface_mut(&mut self) -> &mut SurfaceRef { + &mut self.target + } + + /// Gets the associated surface of the Canvas and destroys the Canvas + #[inline] + pub fn into_surface(self) -> Surface<'s> { + self.target + } + + /// Sets the render target to the provided texture + /// Returns a handle for rendering methods to the target + /// Returns `TargetRenderError::NotSupported` + /// if the renderer does not support the use of render targets, + /// Returns `TargetRenderError::SdlError` if SDL2 returned with an error code. + /// The texture must be created with the texture access: `sdl2::render::TextureAccess::Target`. + pub fn with_target<'r, 't, 'a> + (&'r mut self, + texture: &'t mut Texture<'a>) + -> Result>, TargetRenderError> { + self.internal_with_target(texture) + } + + /// Returns a `TextureCreator` that can create Textures to be drawn on this `Canvas` + /// + /// This `TextureCreator` will share a reference to the renderer and target context. + /// + /// The target (i.e., `Window`) will not be destroyed and the SDL_Renderer will not be + /// destroyed if the `TextureCreator` is still in scope. + pub fn texture_creator(&self) -> TextureCreator> { + TextureCreator { context: self.context.clone() } + } +} + +pub type WindowCanvas = Canvas; + +/// Methods for the `WindowCanvas`. +impl Canvas { + /// Gets a reference to the associated window of the Canvas + #[inline] + pub fn window(&self) -> &Window { + &self.target + } + + /// Gets a mutable reference to the associated window of the Canvas + #[inline] + pub fn window_mut(&mut self) -> &mut Window { + &mut self.target + } + + /// Gets the associated window of the Canvas and destroys the Canvas + #[inline] + pub fn into_window(self) -> Window { + self.target + } + + /// Sets the render target to the provided texture + /// Returns a handle for rendering methods to the target + /// Returns `TargetRenderError::NotSupported` + /// if the renderer does not support the use of render targets, + /// Returns `TargetRenderError::SdlError` if SDL2 returned with an error code. + /// The texture must be created with the texture access: `sdl2::render::TextureAccess::Target`. + pub fn with_target<'r, 't, 'a> + (&'r mut self, + texture: &'t mut Texture<'a>) + -> Result, TargetRenderError> { + self.internal_with_target(texture) + } + + /// Returns a `TextureCreator` that can create Textures to be drawn on this `Canvas` + /// + /// This `TextureCreator` will share a reference to the renderer and target context. + /// + /// The target (i.e., `Window`) will not be destroyed and the SDL_Renderer will not be + /// destroyed if the `TextureCreator` is still in scope. + pub fn texture_creator(&self) -> TextureCreator { + TextureCreator { context: self.context.clone() } + } +} + +impl Canvas { + /// Determine whether a window supports the use of render targets. + pub fn render_target_supported(&self) -> bool { + unsafe { ll::SDL_RenderTargetSupported(self.context.raw) == 1 } + } + + fn internal_with_target<'r, 't, 'a>(&'r mut self, + texture: &'t mut Texture<'a>) + -> Result, TargetRenderError> { + if self.render_target_supported() { + unsafe { self.set_raw_target(texture.raw) } + .map_err(|e| TargetRenderError::SdlError(e))?; + Ok(TextureCanvas { + context: self.context.clone(), + target: TextureTarget { + raw_renderer: &self.context.raw, + _texture_marker: PhantomData, + }, + }) + } else { + Err(TargetRenderError::NotSupported) + } + } +} + +/// Creates Textures that cannot outlive the creator +/// +/// The `TextureCreator` does not hold a lifetime to its Canvas by design choice. +/// If a `Canvas` is dropped before its `TextureCreator`, it is still safe to use. +/// It is, however, useless. Any `Texture` created here can only be drawn onto the original `Canvas` +pub struct TextureCreator { + context: Rc>, +} + +/// The type that allows you to build Window-based renderers. +/// +/// By default, the renderer builder will prioritize for a hardware-accelerated +/// renderer. +pub struct CanvasBuilder { + window: Window, + index: Option, + renderer_flags: u32, +} + +impl CanvasBuilder { + /// Initializes a new `CanvasBuilder`. + pub fn new(window: Window) -> CanvasBuilder { + CanvasBuilder { + window: window, + // -1 means to initialize the first rendering driver supporting the + // renderer flags + index: None, + // no flags gives priority to available SDL_RENDERER_ACCELERATED + // renderers + renderer_flags: 0, + } + } + + /// Builds the renderer. + pub fn build(self) -> Result { + use common::IntegerOrSdlError::*; + let index = match self.index { + None => -1, + Some(index) => try!(validate_int(index, "index")), + }; + let raw = unsafe { ll::SDL_CreateRenderer(self.window.raw(), index, self.renderer_flags) }; + + if raw.is_null() { + Err(SdlError(get_error())) + } else { + let context = Rc::new(unsafe { RendererContext::from_ll(raw, self.window.context()) }); + Ok(Canvas { + context: context, + target: self.window, + }) + } + } + + /// Sets the index of the rendering driver to initialize. + /// If you desire the first rendering driver to support the flags provided, + /// or if you're translating code from C which passes -1 for the index, + /// **do not** invoke the `index` method. + pub fn index(mut self, index: u32) -> CanvasBuilder { + self.index = Some(index); + self + } + + /// Set the renderer to a software fallback. + /// This flag is accumulative, and may be specified with other flags. + pub fn software(mut self) -> CanvasBuilder { + self.renderer_flags |= ll::SDL_RENDERER_SOFTWARE as u32; + self + } + + /// Set the renderer to use hardware acceleration. + /// This flag is accumulative, and may be specified with other flags. + pub fn accelerated(mut self) -> CanvasBuilder { + self.renderer_flags |= ll::SDL_RENDERER_ACCELERATED as u32; + self + } + + /// Synchronize renderer `present` method calls with the refresh rate. + /// This flag is accumulative, and may be specified with other flags. + pub fn present_vsync(mut self) -> CanvasBuilder { + self.renderer_flags |= ll::SDL_RENDERER_PRESENTVSYNC as u32; + self + } + + /// Set the renderer to support rendering to a texture. + /// This flag is accumulative, and may be specified with other flags. + pub fn target_texture(mut self) -> CanvasBuilder { + self.renderer_flags |= ll::SDL_RENDERER_TARGETTEXTURE as u32; + self + } } #[derive(Debug)] @@ -385,10 +528,12 @@ impl Error for TextureValueError { } /// Texture-creating methods for the renderer -impl<'a> Renderer<'a> { +impl TextureCreator { + pub unsafe fn raw(&self) -> *mut ll::SDL_Renderer { + self.context.raw() + } + /// Creates a texture for a rendering context. - /// - /// `size` is the width and height of the texture. pub fn create_texture(&self, format: pixels::PixelFormatEnum, access: TextureAccess, @@ -417,16 +562,17 @@ impl<'a> Renderer<'a> { _ => (), } - let result = - unsafe { ll::SDL_CreateTexture(self.raw, format as uint32_t, access as c_int, w, h) }; + let result = unsafe { + ll::SDL_CreateTexture(self.context.raw, format as uint32_t, access as c_int, w, h) + }; if result == ptr::null_mut() { Err(SdlError(get_error())) } else { - unsafe { Ok(Texture::from_ll(self, result)) } + unsafe { Ok(self.raw_create_texture(result)) } } } - /// Shorthand for `create_texture(format, TextureAccess::Static, size)` + /// Shorthand for `create_texture(format, TextureAccess::Static, width, height)` pub fn create_texture_static(&self, format: pixels::PixelFormatEnum, width: u32, @@ -435,7 +581,7 @@ impl<'a> Renderer<'a> { self.create_texture(format, TextureAccess::Static, width, height) } - /// Shorthand for `create_texture(format, TextureAccess::Streaming, size)` + /// Shorthand for `create_texture(format, TextureAccess::Streaming, width, height)` pub fn create_texture_streaming(&self, format: pixels::PixelFormatEnum, width: u32, @@ -444,7 +590,7 @@ impl<'a> Renderer<'a> { self.create_texture(format, TextureAccess::Streaming, width, height) } - /// Shorthand for `create_texture(format, TextureAccess::Target, size)` + /// Shorthand for `create_texture(format, TextureAccess::Target, width, height)` pub fn create_texture_target(&self, format: pixels::PixelFormatEnum, width: u32, @@ -461,39 +607,43 @@ impl<'a> Renderer<'a> { surface: S) -> Result { use self::TextureValueError::*; - let result = unsafe { ll::SDL_CreateTextureFromSurface(self.raw, surface.as_ref().raw()) }; + let result = + unsafe { ll::SDL_CreateTextureFromSurface(self.context.raw, surface.as_ref().raw()) }; if result == ptr::null_mut() { Err(SdlError(get_error())) } else { - unsafe { Ok(Texture::from_ll(self, result)) } + unsafe { Ok(self.raw_create_texture(result)) } + } + } + + pub unsafe fn raw_create_texture(&self, raw: *mut ll::SDL_Texture) -> Texture { + Texture { + raw: raw, + _marker: PhantomData, } } } -/// Render target methods -impl<'a> Renderer<'a> { - /// Determine whether a window supports the use of render targets. - pub fn render_target_supported(&self) -> bool { - unsafe { ll::SDL_RenderTargetSupported(self.raw) == 1 } - } +pub struct TextureTarget<'r, 't> { + raw_renderer: &'r *mut ll::SDL_Renderer, + _texture_marker: PhantomData<&'t ()>, +} - /// Gets the render target handle. - /// - /// Returns `None` if the window does not support the use of render targets. - pub fn render_target(&mut self) -> Option { - if self.render_target_supported() { - Some(RenderTarget { - raw: self.raw, - is_renderer_alive: &self.is_alive, - }) - } else { - None +impl<'r, 't> Drop for TextureTarget<'r, 't> { + // `Drop` cannot be specialized. Get around this through run-time check of Target Kind + fn drop(&mut self) { + unsafe { + ll::SDL_SetRenderTarget(*self.raw_renderer, ptr::null_mut()); } } } /// Drawing methods -impl<'a> Renderer<'a> { +impl Canvas { + pub unsafe fn raw(&self) -> *mut ll::SDL_Renderer { + self.context.raw() + } + /// Sets the color used for drawing operations (Rect, Line and Clear). pub fn set_draw_color(&mut self, color: pixels::Color) { let (r, g, b, a) = color.rgba(); @@ -507,7 +657,8 @@ impl<'a> Renderer<'a> { /// Gets the color used for drawing operations (Rect, Line and Clear). pub fn draw_color(&self) -> pixels::Color { let (mut r, mut g, mut b, mut a) = (0, 0, 0, 0); - let ret = unsafe { ll::SDL_GetRenderDrawColor(self.raw, &mut r, &mut g, &mut b, &mut a) }; + let ret = + unsafe { ll::SDL_GetRenderDrawColor(self.context.raw, &mut r, &mut g, &mut b, &mut a) }; // Should only fail on an invalid renderer if ret != 0 { panic!(get_error()) @@ -519,7 +670,8 @@ impl<'a> Renderer<'a> { /// Sets the blend mode used for drawing operations (Fill and Line). pub fn set_blend_mode(&mut self, blend: BlendMode) { let ret = unsafe { - ll::SDL_SetRenderDrawBlendMode(self.raw, FromPrimitive::from_i64(blend as i64).unwrap()) + ll::SDL_SetRenderDrawBlendMode(self.context.raw, + FromPrimitive::from_i64(blend as i64).unwrap()) }; // Should only fail on an invalid renderer if ret != 0 { @@ -530,7 +682,7 @@ impl<'a> Renderer<'a> { /// Gets the blend mode used for drawing operations. pub fn blend_mode(&self) -> BlendMode { let mut blend = 0; - let ret = unsafe { ll::SDL_GetRenderDrawBlendMode(self.raw, &mut blend) }; + let ret = unsafe { ll::SDL_GetRenderDrawBlendMode(self.context.raw, &mut blend) }; // Should only fail on an invalid renderer if ret != 0 { panic!(get_error()) @@ -541,7 +693,7 @@ impl<'a> Renderer<'a> { /// Clears the current rendering target with the drawing color. pub fn clear(&mut self) { - let ret = unsafe { ll::SDL_RenderClear(self.raw) }; + let ret = unsafe { ll::SDL_RenderClear(self.context.raw) }; if ret != 0 { panic!("Could not clear: {}", get_error()) } @@ -555,7 +707,7 @@ impl<'a> Renderer<'a> { /// As such, you compose your entire scene and present the composed /// backbuffer to the screen as a complete picture. pub fn present(&mut self) { - unsafe { ll::SDL_RenderPresent(self.raw) } + unsafe { ll::SDL_RenderPresent(self.context.raw) } } /// Gets the output size of a rendering context. @@ -563,7 +715,8 @@ impl<'a> Renderer<'a> { let mut width = 0; let mut height = 0; - let result = unsafe { ll::SDL_GetRendererOutputSize(self.raw, &mut width, &mut height) }; + let result = + unsafe { ll::SDL_GetRendererOutputSize(self.context.raw, &mut width, &mut height) }; if result == 0 { Ok((width as u32, height as u32)) @@ -577,7 +730,7 @@ impl<'a> Renderer<'a> { use common::IntegerOrSdlError::*; let width = try!(validate_int(width, "width")); let height = try!(validate_int(height, "height")); - let result = unsafe { ll::SDL_RenderSetLogicalSize(self.raw, width, height) }; + let result = unsafe { ll::SDL_RenderSetLogicalSize(self.context.raw, width, height) }; match result { 0 => Ok(()), _ => Err(SdlError(get_error())), @@ -589,7 +742,7 @@ impl<'a> Renderer<'a> { let mut width = 0; let mut height = 0; - unsafe { ll::SDL_RenderGetLogicalSize(self.raw, &mut width, &mut height) }; + unsafe { ll::SDL_RenderGetLogicalSize(self.context.raw, &mut width, &mut height) }; (width as u32, height as u32) } @@ -600,7 +753,7 @@ impl<'a> Renderer<'a> { Some(ref rect) => rect.raw(), None => ptr::null(), }; - let ret = unsafe { ll::SDL_RenderSetViewport(self.raw, ptr) }; + let ret = unsafe { ll::SDL_RenderSetViewport(self.context.raw, ptr) }; if ret != 0 { panic!("Could not set viewport: {}", get_error()) } @@ -609,7 +762,7 @@ impl<'a> Renderer<'a> { /// Gets the drawing area for the current target. pub fn viewport(&self) -> Rect { let mut rect = unsafe { mem::uninitialized() }; - unsafe { ll::SDL_RenderGetViewport(self.raw, &mut rect) }; + unsafe { ll::SDL_RenderGetViewport(self.context.raw, &mut rect) }; Rect::from_ll(rect) } @@ -618,7 +771,7 @@ impl<'a> Renderer<'a> { /// If the rectangle is `None`, clipping will be disabled. pub fn set_clip_rect>>(&mut self, rect: R) { let ret = unsafe { - ll::SDL_RenderSetClipRect(self.raw, + ll::SDL_RenderSetClipRect(self.context.raw, match rect.into() { Some(ref rect) => rect.raw(), None => ptr::null(), @@ -634,7 +787,7 @@ impl<'a> Renderer<'a> { /// Returns `None` if clipping is disabled. pub fn clip_rect(&self) -> Option { let mut raw = unsafe { mem::uninitialized() }; - unsafe { ll::SDL_RenderGetClipRect(self.raw, &mut raw) }; + unsafe { ll::SDL_RenderGetClipRect(self.context.raw, &mut raw) }; if raw.w == 0 || raw.h == 0 { None } else { @@ -644,7 +797,7 @@ impl<'a> Renderer<'a> { /// Sets the drawing scale for rendering on the current target. pub fn set_scale(&mut self, scale_x: f32, scale_y: f32) -> Result<(), String> { - let ret = unsafe { ll::SDL_RenderSetScale(self.raw, scale_x, scale_y) }; + let ret = unsafe { ll::SDL_RenderSetScale(self.context.raw, scale_x, scale_y) }; // Should only fail on an invalid renderer if ret != 0 { Err(get_error()) } else { Ok(()) } } @@ -653,7 +806,7 @@ impl<'a> Renderer<'a> { pub fn scale(&self) -> (f32, f32) { let mut scale_x = 0.0; let mut scale_y = 0.0; - unsafe { ll::SDL_RenderGetScale(self.raw, &mut scale_x, &mut scale_y) }; + unsafe { ll::SDL_RenderGetScale(self.context.raw, &mut scale_x, &mut scale_y) }; (scale_x, scale_y) } @@ -661,7 +814,7 @@ impl<'a> Renderer<'a> { /// Errors if drawing fails for any reason (e.g. driver failure) pub fn draw_point>(&mut self, point: P) -> Result<(), String> { let point = point.into(); - let result = unsafe { ll::SDL_RenderDrawPoint(self.raw, point.x(), point.y()) }; + let result = unsafe { ll::SDL_RenderDrawPoint(self.context.raw, point.x(), point.y()) }; if result != 0 { Err(get_error()) } else { @@ -671,10 +824,12 @@ impl<'a> Renderer<'a> { /// Draws multiple points on the current rendering target. /// Errors if drawing fails for any reason (e.g. driver failure) - pub fn draw_points>(&mut self, points: P) -> Result<(), String> { + pub fn draw_points<'a, P: Into<&'a [Point]>>(&mut self, points: P) -> Result<(), String> { let points = points.into(); let result = unsafe { - ll::SDL_RenderDrawPoints(self.raw, Point::raw_slice(points), points.len() as c_int) + ll::SDL_RenderDrawPoints(self.context.raw, + Point::raw_slice(points), + points.len() as c_int) }; if result != 0 { Err(get_error()) @@ -691,8 +846,9 @@ impl<'a> Renderer<'a> { -> Result<(), String> { let start = start.into(); let end = end.into(); - let result = - unsafe { ll::SDL_RenderDrawLine(self.raw, start.x(), start.y(), end.x(), end.y()) }; + let result = unsafe { + ll::SDL_RenderDrawLine(self.context.raw, start.x(), start.y(), end.x(), end.y()) + }; if result != 0 { Err(get_error()) } else { @@ -702,10 +858,12 @@ impl<'a> Renderer<'a> { /// Draws a series of connected lines on the current rendering target. /// Errors if drawing fails for any reason (e.g. driver failure) - pub fn draw_lines>(&mut self, points: P) -> Result<(), String> { + pub fn draw_lines<'a, P: Into<&'a [Point]>>(&mut self, points: P) -> Result<(), String> { let points = points.into(); let result = unsafe { - ll::SDL_RenderDrawLines(self.raw, Point::raw_slice(points), points.len() as c_int) + ll::SDL_RenderDrawLines(self.context.raw, + Point::raw_slice(points), + points.len() as c_int) }; if result != 0 { Err(get_error()) @@ -717,7 +875,7 @@ impl<'a> Renderer<'a> { /// Draws a rectangle on the current rendering target. /// Errors if drawing fails for any reason (e.g. driver failure) pub fn draw_rect(&mut self, rect: Rect) -> Result<(), String> { - let result = unsafe { ll::SDL_RenderDrawRect(self.raw, rect.raw()) }; + let result = unsafe { ll::SDL_RenderDrawRect(self.context.raw, rect.raw()) }; if result != 0 { Err(get_error()) } else { @@ -729,7 +887,9 @@ impl<'a> Renderer<'a> { /// Errors if drawing fails for any reason (e.g. driver failure) pub fn draw_rects(&mut self, rects: &[Rect]) -> Result<(), String> { let result = unsafe { - ll::SDL_RenderDrawRects(self.raw, Rect::raw_slice(rects), rects.len() as c_int) + ll::SDL_RenderDrawRects(self.context.raw, + Rect::raw_slice(rects), + rects.len() as c_int) }; if result != 0 { Err(get_error()) @@ -744,7 +904,7 @@ impl<'a> Renderer<'a> { /// Errors if drawing fails for any reason (e.g. driver failure) pub fn fill_rect>>(&mut self, rect: R) -> Result<(), String> { let result = unsafe { - ll::SDL_RenderFillRect(self.raw, + ll::SDL_RenderFillRect(self.context.raw, rect.into() .as_ref() .map(|r| r.raw()) @@ -762,7 +922,9 @@ impl<'a> Renderer<'a> { /// Errors if drawing fails for any reason (e.g. driver failure) pub fn fill_rects(&mut self, rects: &[Rect]) -> Result<(), String> { let result = unsafe { - ll::SDL_RenderFillRects(self.raw, Rect::raw_slice(rects), rects.len() as c_int) + ll::SDL_RenderFillRects(self.context.raw, + Rect::raw_slice(rects), + rects.len() as c_int) }; if result != 0 { Err(get_error()) @@ -783,10 +945,8 @@ impl<'a> Renderer<'a> { where R1: Into>, R2: Into> { - texture.check_renderer(); - let ret = unsafe { - ll::SDL_RenderCopy(self.raw, + ll::SDL_RenderCopy(self.context.raw, texture.raw, match src.into() { Some(ref rect) => rect.raw(), @@ -827,8 +987,6 @@ impl<'a> Renderer<'a> { R2: Into>, P: Into> { - texture.check_renderer(); - let flip = match (flip_horizontal, flip_vertical) { (false, false) => ll::SDL_FLIP_NONE, (true, false) => ll::SDL_FLIP_HORIZONTAL, @@ -837,7 +995,7 @@ impl<'a> Renderer<'a> { }; let ret = unsafe { - ll::SDL_RenderCopyEx(self.raw, + ll::SDL_RenderCopyEx(self.context.raw, texture.raw, match src.into() { Some(ref rect) => rect.raw(), @@ -882,7 +1040,7 @@ impl<'a> Renderer<'a> { // Pass the interior of `pixels: Vec` to SDL let ret = { - ll::SDL_RenderReadPixels(self.raw, + ll::SDL_RenderReadPixels(self.context.raw, actual_rect, format as uint32_t, pixels.as_mut_ptr() as *mut c_void, @@ -904,117 +1062,46 @@ impl<'a> Renderer<'a> { /// ```no_run /// use sdl2::pixels::{Color, PixelFormatEnum}; /// use sdl2::rect::Rect; -/// use sdl2::render::{Renderer, Texture}; +/// use sdl2::video::WindowContext; +/// use sdl2::render::{Texture, TextureCreator, WindowCanvas}; /// /// // Draw a red rectangle to a new texture -/// fn draw_to_texture(r: &mut Renderer) -> Texture { -/// r.render_target() -/// .expect("This platform doesn't support render targets") -/// .create_and_set(PixelFormatEnum::RGBA8888, 512, 512); +/// fn draw_to_texture<'c>(t: &'c TextureCreator, c: &mut WindowCanvas) +/// -> Texture<'c> { +/// let mut texture = t.create_texture_target(PixelFormatEnum::RGBA8888, 512, 512) +/// .unwrap(); +/// { +/// let mut target = c.with_target(&mut texture) +/// .expect("This platform doesn't support render targets"); /// -/// // Start drawing -/// r.clear(); -/// r.set_draw_color(Color::RGB(255, 0, 0)); -/// r.fill_rect(Rect::new(100, 100, 256, 256)); -/// -/// let texture: Option = r.render_target().unwrap().reset().unwrap(); -/// texture.unwrap() +/// // Start drawing +/// target.clear(); +/// target.set_draw_color(Color::RGB(255, 0, 0)); +/// target.fill_rect(Rect::new(100, 100, 256, 256)); +/// } +/// texture /// } /// ``` -pub struct RenderTarget<'renderer> { - raw: *mut ll::SDL_Renderer, - is_renderer_alive: &'renderer Rc>, -} +pub type TextureCanvas<'r, 't, TC> = Canvas, TC>; -impl<'renderer> RenderTarget<'renderer> { - /// Resets the render target to the default render target. +impl<'r, 't, TC> Canvas, TC> { + /// Replace the target of the `TextureCanvas` with a different `Texture` /// - /// The old render target is returned if the function is successful. - pub fn reset(&mut self) -> Result, String> { - unsafe { - let old_texture_raw = ll::SDL_GetRenderTarget(self.raw); - - if ll::SDL_SetRenderTarget(self.raw, ptr::null_mut()) == 0 { - Ok(match old_texture_raw.is_null() { - true => None, - false => { - Some(Texture { - raw: old_texture_raw, - is_renderer_alive: self.is_renderer_alive.clone(), - }) - } - }) - } else { - Err(get_error()) - } - } - } - - /// Sets the render target to the provided texture. - /// The texture must be created with the texture access: `sdl2::render::TextureAccess::Target`. - /// - /// The old render target is returned if the function is successful. - pub fn set(&mut self, texture: Texture) -> Result, String> { - texture.check_renderer(); - - unsafe { - let old_texture_raw = ll::SDL_GetRenderTarget(self.raw); - - if ll::SDL_SetRenderTarget(self.raw, texture.raw) == 0 { - texture.forget(); - Ok(match old_texture_raw.is_null() { - true => None, - false => { - Some(Texture { - raw: old_texture_raw, - is_renderer_alive: self.is_renderer_alive.clone(), - }) - } - }) - } else { - Err(get_error()) - } - } - } - - /// Creates a new texture and sets it as the render target. - /// - /// The old render target is returned if the function is successful. - pub fn create_and_set(&mut self, - format: pixels::PixelFormatEnum, - width: u32, - height: u32) - -> Result, IntegerOrSdlError> { - use common::IntegerOrSdlError::*; - let width = try!(validate_int(width, "width")); - let height = try!(validate_int(height, "height")); - - let new_texture_raw = unsafe { - let access = ll::SDL_TEXTUREACCESS_TARGET; - ll::SDL_CreateTexture(self.raw, format as uint32_t, access as c_int, width, height) - }; - - if new_texture_raw == ptr::null_mut() { - Err(SdlError(get_error())) - } else { - unsafe { - let old_texture_raw = ll::SDL_GetRenderTarget(self.raw); - - if ll::SDL_SetRenderTarget(self.raw, new_texture_raw) == 0 { - Ok(match old_texture_raw.is_null() { - true => None, - false => { - Some(Texture { - raw: old_texture_raw, - is_renderer_alive: self.is_renderer_alive.clone(), - }) - } - }) - } else { - Err(SdlError(get_error())) - } - } - } + /// Returns the new `TextureCanvas` and releases the `&mut` borrow on the old `Texture` + pub fn with_target<'nt, 'a>(mut self, + texture: &'nt mut Texture<'a>) + -> Result, SdlError> { + unsafe { self.set_raw_target(texture.raw) }?; + let context = mem::replace(&mut self.context, unsafe { mem::zeroed() }); + let raw_renderer = mem::replace(&mut self.target.raw_renderer, unsafe { mem::zeroed() }); + mem::forget(self); + Ok(TextureCanvas { + context: context, + target: TextureTarget { + raw_renderer: raw_renderer, + _texture_marker: PhantomData, + }, + }) } } @@ -1028,22 +1115,19 @@ pub struct TextureQuery { /// A texture for a rendering context. /// -/// Every Texture is owned by a Renderer. -/// If a Texture is accessed after the corresponding Renderer is dropped, then -/// the program will panic (clarification: will not crash). +/// Every Texture is owned by a `TextureCreator`. +/// A `Texture` cannot outlive the `TextureCreator` /// -/// A Texture can be safely dropped before or after the Renderer is dropped. -pub struct Texture { +/// A `Texture` can be safely accessed after the `Canvas` is dropped. +pub struct Texture<'r> { raw: *mut ll::SDL_Texture, - is_renderer_alive: Rc>, + _marker: PhantomData<&'r ()>, } -impl Drop for Texture { +impl<'r> Drop for Texture<'r> { fn drop(&mut self) { unsafe { - if *self.is_renderer_alive.get() { - ll::SDL_DestroyTexture(self.raw); - } + ll::SDL_DestroyTexture(self.raw); } } } @@ -1189,30 +1273,9 @@ impl Error for UpdateTextureYUVError { } } -impl Texture { - #[inline] - fn check_renderer(&self) { - let alive = unsafe { *self.is_renderer_alive.get() }; - if !alive { - panic!("renderer has been destroyed; cannot use Texture"); - } - } - - /// Doesn't free the Texture, but decrements its `is_renderer_alive` box. - fn forget(self) { - unsafe { - let _is_renderer_alive: Rc> = - mem::transmute_copy(&self.is_renderer_alive); - mem::forget(self); - - // is_renderer_alive gets deref'd - } - } - +impl<'r> Texture<'r> { /// Queries the attributes of the texture. pub fn query(&self) -> TextureQuery { - self.check_renderer(); - let mut format = 0; let mut access = 0; let mut width = 0; @@ -1236,8 +1299,6 @@ impl Texture { /// Sets an additional color value multiplied into render copy operations. pub fn set_color_mod(&mut self, red: u8, green: u8, blue: u8) { - self.check_renderer(); - let ret = unsafe { ll::SDL_SetTextureColorMod(self.raw, red, green, blue) }; if ret != 0 { @@ -1247,8 +1308,6 @@ impl Texture { /// Gets the additional color value multiplied into render copy operations. pub fn color_mod(&self) -> (u8, u8, u8) { - self.check_renderer(); - let (mut r, mut g, mut b) = (0, 0, 0); let ret = unsafe { ll::SDL_GetTextureColorMod(self.raw, &mut r, &mut g, &mut b) }; @@ -1262,8 +1321,6 @@ impl Texture { /// Sets an additional alpha value multiplied into render copy operations. pub fn set_alpha_mod(&mut self, alpha: u8) { - self.check_renderer(); - let ret = unsafe { ll::SDL_SetTextureAlphaMod(self.raw, alpha) }; if ret != 0 { @@ -1273,8 +1330,6 @@ impl Texture { /// Gets the additional alpha value multiplied into render copy operations. pub fn alpha_mod(&self) -> u8 { - self.check_renderer(); - let mut alpha = 0; let ret = unsafe { ll::SDL_GetTextureAlphaMod(self.raw, &mut alpha) }; @@ -1284,8 +1339,6 @@ impl Texture { /// Sets the blend mode for a texture, used by `Renderer::copy()`. pub fn set_blend_mode(&mut self, blend: BlendMode) { - self.check_renderer(); - let ret = unsafe { ll::SDL_SetTextureBlendMode(self.raw, FromPrimitive::from_i64(blend as i64).unwrap()) }; @@ -1297,8 +1350,6 @@ impl Texture { /// Gets the blend mode used for texture copy operations. pub fn blend_mode(&self) -> BlendMode { - self.check_renderer(); - let mut blend = 0; let ret = unsafe { ll::SDL_GetTextureBlendMode(self.raw, &mut blend) }; @@ -1324,8 +1375,6 @@ impl Texture { where R: Into> { use self::UpdateTextureError::*; - self.check_renderer(); - let rect = rect.into(); let rect_raw_ptr = match rect { Some(ref rect) => rect.raw(), @@ -1392,7 +1441,6 @@ impl Texture { where R: Into> { use self::UpdateTextureYUVError::*; - self.check_renderer(); let rect = rect.into(); @@ -1523,8 +1571,6 @@ impl Texture { where F: FnOnce(&mut [u8], usize) -> R, R2: Into> { - self.check_renderer(); - // Call to SDL to populate pixel data let loaded = unsafe { let q = self.query(); @@ -1562,8 +1608,6 @@ impl Texture { /// Binds an OpenGL/ES/ES2 texture to the current /// context for use with when rendering OpenGL primitives directly. pub unsafe fn gl_bind_texture(&mut self) -> (f32, f32) { - self.check_renderer(); - let mut texw = 0.0; let mut texh = 0.0; @@ -1576,8 +1620,6 @@ impl Texture { /// Unbinds an OpenGL/ES/ES2 texture from the current context. pub unsafe fn gl_unbind_texture(&mut self) { - self.check_renderer(); - if ll::SDL_GL_UnbindTexture(self.raw) != 0 { panic!("OpenGL texture unbinding not supported"); } @@ -1585,8 +1627,6 @@ impl Texture { /// Binds and unbinds an OpenGL/ES/ES2 texture from the current context. pub fn gl_with_bind R>(&mut self, f: F) -> R { - self.check_renderer(); - unsafe { let mut texw = 0.0; let mut texh = 0.0; @@ -1606,13 +1646,6 @@ impl Texture { } } - pub unsafe fn from_ll(renderer: &Renderer, raw: *mut ll::SDL_Texture) -> Texture { - Texture { - raw: raw, - is_renderer_alive: renderer.is_alive.clone(), - } - } - pub unsafe fn raw(&self) -> *mut ll::SDL_Texture { self.raw } diff --git a/src/sdl2/surface.rs b/src/sdl2/surface.rs index e1995f84..37741d67 100644 --- a/src/sdl2/surface.rs +++ b/src/sdl2/surface.rs @@ -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`. +/// +/// 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>, +} + /// 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 for Surface<'a> { #[inline] fn as_ref(&self) -> &SurfaceRef { - unsafe { mem::transmute(self.raw) } + unsafe { mem::transmute(self.context.raw) } } } impl<'a> AsMut 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,10 +180,7 @@ impl<'a> Surface<'a> { if raw.is_null() { Err(get_error()) } else { - Ok(Surface { - raw: raw, - _marker: PhantomData - }) + Ok( unsafe{ Surface::from_ll(raw) } ) } } @@ -181,6 +188,10 @@ impl<'a> Surface<'a> { let mut file = try!(RWops::from_file(path, "rb")); Surface::load_bmp_rw(&mut file) } + + pub fn context(&self) -> Rc> { + self.context.clone() + } } impl SurfaceRef { diff --git a/src/sdl2/video.rs b/src/sdl2/video.rs index 8e76ecb2..2511f0f1 100644 --- a/src/sdl2/video.rs +++ b/src/sdl2/video.rs @@ -1,11 +1,11 @@ use libc::{c_int, c_float, uint32_t, c_char}; use std::ffi::{CStr, CString, NulError}; use std::{mem, ptr, fmt}; -use std::ops::{Deref, DerefMut}; +use std::rc::Rc; use std::error::Error; use rect::Rect; -use render::RendererBuilder; +use render::CanvasBuilder; use surface::SurfaceRef; use pixels::PixelFormatEnum; use VideoSubsystem; @@ -449,27 +449,50 @@ impl GLContext { } } -pub struct Window { +/// Holds a `SDL_Window` +/// +/// When the `WindowContext` is dropped, it destroys the `SDL_Window` +pub struct WindowContext { subsystem: VideoSubsystem, raw: *mut ll::SDL_Window, } -/// An unsized Window reference. -/// -/// This type is used whenever Windows need to be borrowed from the SDL library, without concern -/// for freeing the Window. -pub struct WindowRef { - // It's nothing! (it gets transmuted to SDL_Window later). - // The empty private field is need to a) make `std::mem::swap()` copy nothing instead of - // clobbering two windows (SDL_Window's size could change in the future), - // and b) prevent user initialization of this type. - _raw: () +impl Drop for WindowContext { + #[inline] + fn drop(&mut self) { + unsafe { ll::SDL_DestroyWindow(self.raw) }; + } } -#[test] -fn test_window_ref_size() { - // `WindowRef` must be 0 bytes. - assert_eq!(::std::mem::size_of::(), 0); +impl WindowContext { + #[inline] + /// Unsafe if the `*mut SDL_Window` is used after the `WindowContext` is dropped + pub unsafe fn from_ll(subsystem: VideoSubsystem, raw: *mut ll::SDL_Window) -> WindowContext { + WindowContext { + subsystem: subsystem.clone(), + raw: raw, + } + } +} + +/// Represents the "shell" of a `Window`. +/// +/// You can set get and set many of the SDL_Window properties (i.e., border, size, `PixelFormat`, etc) +/// +/// However, you cannot directly access the pixels of the `Window`. +/// It needs to be converted to a `Canvas` to access the rendering functions. +/// +/// Note: If a `Window` goes out of scope but it cloned its context, +/// then the `SDL_Window` will not be destroyed until there are no more references to the `WindowContext`. +/// This may happen when a `TextureCreator` outlives the `Canvas` +pub struct Window { + context: Rc, +} + +impl From for Window { + fn from(context: WindowContext) -> Window { + Window { context: Rc::new(context) } + } } impl_raw_accessors!( @@ -692,13 +715,6 @@ impl VideoSubsystem { } } -impl Drop for Window { - #[inline] - fn drop(&mut self) { - unsafe { ll::SDL_DestroyWindow(self.raw) }; - } -} - #[derive(Debug)] pub enum WindowBuildError { HeightOverflows(u32), @@ -790,10 +806,7 @@ impl WindowBuilder { if raw == ptr::null_mut() { Err(SdlError(get_error())) } else { - Ok(Window { - subsystem: self.subsystem.clone(), - raw: raw, - }) + Ok(Window::from_ll(self.subsystem.clone(), raw)) } } } @@ -883,65 +896,46 @@ impl WindowBuilder { } } +impl From for CanvasBuilder { + fn from(window: Window) -> CanvasBuilder { + CanvasBuilder::new(window) + } +} + impl Window { #[inline] - pub fn raw(&self) -> *mut ll::SDL_Window { self.raw } + pub fn raw(&self) -> *mut ll::SDL_Window { self.context.raw } #[inline] pub unsafe fn from_ll(subsystem: VideoSubsystem, raw: *mut ll::SDL_Window) -> Window { - Window { - subsystem: subsystem, - raw: raw - } + let context = WindowContext::from_ll(subsystem, raw); + context.into() } #[inline] - pub fn subsystem(&self) -> &VideoSubsystem { &self.subsystem } - - /// Initializes a new `RendererBuilder`; a convenience method that calls `RendererBuilder::new()`. - pub fn renderer(self) -> RendererBuilder { - RendererBuilder::new(self) - } -} - -impl Deref for Window { - type Target = WindowRef; - - #[inline] - fn deref(&self) -> &WindowRef { - unsafe { mem::transmute(self.raw) } - } -} - -impl DerefMut for Window { - #[inline] - fn deref_mut(&mut self) -> &mut WindowRef { - unsafe { mem::transmute(self.raw) } - } -} - -impl WindowRef { - #[inline] - pub fn raw(&self) -> *mut ll::SDL_Window { - unsafe { mem::transmute(self) } + /// Create a new `Window` without taking ownership of the `WindowContext` + pub unsafe fn from_ref(context: Rc) -> Window { + Window { context: context } } #[inline] - pub unsafe fn from_ll<'a>(raw: *mut ll::SDL_Window) -> &'a WindowRef { - mem::transmute(raw) + pub fn subsystem(&self) -> &VideoSubsystem { &self.context.subsystem } + + /// Initializes a new `CanvasBuilder`; a convenience method that calls `CanvasBuilder::new()`. + pub fn into_canvas(self) -> CanvasBuilder { + self.into() } - #[inline] - pub unsafe fn from_ll_mut<'a>(raw: *mut ll::SDL_Window) -> &'a mut WindowRef { - mem::transmute(raw) + pub fn context(&self) -> Rc { + self.context.clone() } pub fn id(&self) -> u32 { - unsafe { ll::SDL_GetWindowID(self.raw()) } + unsafe { ll::SDL_GetWindowID(self.context.raw) } } pub fn gl_create_context(&self) -> Result { - let result = unsafe { ll::SDL_GL_CreateContext(self.raw()) }; + let result = unsafe { ll::SDL_GL_CreateContext(self.context.raw) }; if result == ptr::null_mut() { Err(get_error()) } else { @@ -957,7 +951,7 @@ impl WindowRef { if context_raw.is_null() { Err(get_error()) } else { - if ll::SDL_GL_MakeCurrent(self.raw(), context_raw) == 0 { + if ll::SDL_GL_MakeCurrent(self.context.raw, context_raw) == 0 { Ok(()) } else { Err(get_error()) @@ -968,7 +962,7 @@ impl WindowRef { pub fn gl_make_current(&self, context: &GLContext) -> Result<(), String> { unsafe { - if ll::SDL_GL_MakeCurrent(self.raw(), context.raw) == 0 { + if ll::SDL_GL_MakeCurrent(self.context.raw, context.raw) == 0 { Ok(()) } else { Err(get_error()) @@ -977,11 +971,11 @@ impl WindowRef { } pub fn gl_swap_window(&self) { - unsafe { ll::SDL_GL_SwapWindow(self.raw()) } + unsafe { ll::SDL_GL_SwapWindow(self.context.raw) } } pub fn display_index(&self) -> Result { - let result = unsafe { ll::SDL_GetWindowDisplayIndex(self.raw()) }; + let result = unsafe { ll::SDL_GetWindowDisplayIndex(self.context.raw) }; if result < 0 { return Err(get_error()) } else { @@ -994,7 +988,7 @@ impl WindowRef { { unsafe { let result = ll::SDL_SetWindowDisplayMode( - self.raw(), + self.context.raw, match display_mode.into() { Some(ref mode) => &mode.to_ll(), None => ptr::null() @@ -1013,7 +1007,7 @@ impl WindowRef { let result = unsafe { ll::SDL_GetWindowDisplayMode( - self.raw(), + self.context.raw, &mut dm ) == 0 }; @@ -1026,25 +1020,25 @@ impl WindowRef { } pub fn window_pixel_format(&self) -> PixelFormatEnum { - unsafe{ FromPrimitive::from_u64(ll::SDL_GetWindowPixelFormat(self.raw()) as u64).unwrap() } + unsafe{ FromPrimitive::from_u64(ll::SDL_GetWindowPixelFormat(self.context.raw) as u64).unwrap() } } pub fn window_flags(&self) -> u32 { unsafe { - ll::SDL_GetWindowFlags(self.raw()) + ll::SDL_GetWindowFlags(self.context.raw) } } pub fn set_title(&mut self, title: &str) -> Result<(), NulError> { let title = try!(CString::new(title)); Ok(unsafe { - ll::SDL_SetWindowTitle(self.raw(), title.as_ptr() as *const c_char); + ll::SDL_SetWindowTitle(self.context.raw, title.as_ptr() as *const c_char); }) } pub fn title(&self) -> &str { unsafe { - let buf = ll::SDL_GetWindowTitle(self.raw()); + let buf = ll::SDL_GetWindowTitle(self.context.raw); // The window title must be encoded in UTF-8. CStr::from_ptr(buf as *const _).to_str().unwrap() @@ -1053,7 +1047,7 @@ impl WindowRef { pub fn set_icon>(&mut self, icon: S) { unsafe { - ll::SDL_SetWindowIcon(self.raw(), icon.as_ref().raw()) + ll::SDL_SetWindowIcon(self.context.raw, icon.as_ref().raw()) } } @@ -1063,7 +1057,7 @@ impl WindowRef { pub fn set_position(&mut self, x: WindowPos, y: WindowPos) { unsafe { ll::SDL_SetWindowPosition( - self.raw(), to_ll_windowpos(x), to_ll_windowpos(y) + self.context.raw, to_ll_windowpos(x), to_ll_windowpos(y) ) } } @@ -1071,7 +1065,7 @@ impl WindowRef { pub fn position(&self) -> (i32, i32) { let mut x: c_int = 0; let mut y: c_int = 0; - unsafe { ll::SDL_GetWindowPosition(self.raw(), &mut x, &mut y) }; + unsafe { ll::SDL_GetWindowPosition(self.context.raw, &mut x, &mut y) }; (x as i32, y as i32) } @@ -1080,21 +1074,21 @@ impl WindowRef { let w = try!(validate_int(width, "width")); let h = try!(validate_int(height, "height")); Ok(unsafe { - ll::SDL_SetWindowSize(self.raw(), w, h) + ll::SDL_SetWindowSize(self.context.raw, w, h) }) } pub fn size(&self) -> (u32, u32) { let mut w: c_int = 0; let mut h: c_int = 0; - unsafe { ll::SDL_GetWindowSize(self.raw(), &mut w, &mut h) }; + unsafe { ll::SDL_GetWindowSize(self.context.raw, &mut w, &mut h) }; (w as u32, h as u32) } pub fn drawable_size(&self) -> (u32, u32) { let mut w: c_int = 0; let mut h: c_int = 0; - unsafe { ll::SDL_GL_GetDrawableSize(self.raw(), &mut w, &mut h) }; + unsafe { ll::SDL_GL_GetDrawableSize(self.context.raw, &mut w, &mut h) }; (w as u32, h as u32) } @@ -1103,14 +1097,14 @@ impl WindowRef { let w = try!(validate_int(width, "width")); let h = try!(validate_int(height, "height")); Ok(unsafe { - ll::SDL_SetWindowMinimumSize(self.raw(), w, h) + ll::SDL_SetWindowMinimumSize(self.context.raw, w, h) }) } pub fn minimum_size(&self) -> (u32, u32) { let mut w: c_int = 0; let mut h: c_int = 0; - unsafe { ll::SDL_GetWindowMinimumSize(self.raw(), &mut w, &mut h) }; + unsafe { ll::SDL_GetWindowMinimumSize(self.context.raw, &mut w, &mut h) }; (w as u32, h as u32) } @@ -1119,7 +1113,7 @@ impl WindowRef { let w = try!(validate_int(width, "width")); let h = try!(validate_int(height, "height")); Ok(unsafe { - ll::SDL_SetWindowMaximumSize(self.raw(), w, h) + ll::SDL_SetWindowMaximumSize(self.context.raw, w, h) }) } @@ -1127,7 +1121,7 @@ impl WindowRef { let mut w: c_int = 0; let mut h: c_int = 0; unsafe { - ll::SDL_GetWindowMaximumSize(self.raw(), &mut w, &mut h) + ll::SDL_GetWindowMaximumSize(self.context.raw, &mut w, &mut h) }; (w as u32, h as u32) } @@ -1135,34 +1129,34 @@ impl WindowRef { pub fn set_bordered(&mut self, bordered: bool) { unsafe { ll::SDL_SetWindowBordered( - self.raw(), + self.context.raw, if bordered { 1 } else { 0 } ) } } pub fn show(&mut self) { - unsafe { ll::SDL_ShowWindow(self.raw()) } + unsafe { ll::SDL_ShowWindow(self.context.raw) } } pub fn hide(&mut self) { - unsafe { ll::SDL_HideWindow(self.raw()) } + unsafe { ll::SDL_HideWindow(self.context.raw) } } pub fn raise(&mut self) { - unsafe { ll::SDL_RaiseWindow(self.raw()) } + unsafe { ll::SDL_RaiseWindow(self.context.raw) } } pub fn maximize(&mut self) { - unsafe { ll::SDL_MaximizeWindow(self.raw()) } + unsafe { ll::SDL_MaximizeWindow(self.context.raw) } } pub fn minimize(&mut self) { - unsafe { ll::SDL_MinimizeWindow(self.raw()) } + unsafe { ll::SDL_MinimizeWindow(self.context.raw) } } pub fn restore(&mut self) { - unsafe { ll::SDL_RestoreWindow(self.raw()) } + unsafe { ll::SDL_RestoreWindow(self.context.raw) } } pub fn fullscreen_state(&self) -> FullscreenType { @@ -1173,7 +1167,7 @@ impl WindowRef { -> Result<(), String> { unsafe { let result = ll::SDL_SetWindowFullscreen( - self.raw(), fullscreen_type as uint32_t + self.context.raw, fullscreen_type as uint32_t ); if result == 0 { Ok(()) @@ -1184,7 +1178,7 @@ impl WindowRef { } pub fn surface<'a>(&'a self, _e: &'a EventPump) -> Result<&'a SurfaceRef, String> { - let raw = unsafe { ll::SDL_GetWindowSurface(self.raw()) }; + let raw = unsafe { ll::SDL_GetWindowSurface(self.context.raw) }; if raw.is_null() { Err(get_error()) @@ -1194,7 +1188,7 @@ impl WindowRef { } pub fn surface_mut<'a>(&'a mut self, _e: &'a EventPump) -> Result<&'a mut SurfaceRef, String> { - let raw = unsafe { ll::SDL_GetWindowSurface(self.raw()) }; + let raw = unsafe { ll::SDL_GetWindowSurface(self.context.raw) }; if raw.is_null() { Err(get_error()) @@ -1205,7 +1199,7 @@ impl WindowRef { pub fn update_surface(&self) -> Result<(), String> { unsafe { - if ll::SDL_UpdateWindowSurface(self.raw()) == 0 { + if ll::SDL_UpdateWindowSurface(self.context.raw) == 0 { Ok(()) } else { Err(get_error()) @@ -1215,7 +1209,7 @@ impl WindowRef { pub fn update_surface_rects(&self, rects: &[Rect]) -> Result<(), String> { unsafe { - if ll::SDL_UpdateWindowSurfaceRects(self.raw(), Rect::raw_slice(rects), rects.len() as c_int) == 0 { + if ll::SDL_UpdateWindowSurfaceRects(self.context.raw, Rect::raw_slice(rects), rects.len() as c_int) == 0 { Ok(()) } else { Err(get_error()) @@ -1224,16 +1218,16 @@ impl WindowRef { } pub fn set_grab(&mut self, grabbed: bool) { - unsafe { ll::SDL_SetWindowGrab(self.raw(), if grabbed { 1 } else { 0 }) } + unsafe { ll::SDL_SetWindowGrab(self.context.raw, if grabbed { 1 } else { 0 }) } } pub fn grab(&self) -> bool { - unsafe { ll::SDL_GetWindowGrab(self.raw()) == 1 } + unsafe { ll::SDL_GetWindowGrab(self.context.raw) == 1 } } pub fn set_brightness(&mut self, brightness: f64) -> Result<(), String> { unsafe { - if ll::SDL_SetWindowBrightness(self.raw(), brightness as c_float) == 0 { + if ll::SDL_SetWindowBrightness(self.context.raw, brightness as c_float) == 0 { Ok(()) } else { Err(get_error()) @@ -1242,7 +1236,7 @@ impl WindowRef { } pub fn brightness(&self) -> f64 { - unsafe { ll::SDL_GetWindowBrightness(self.raw()) as f64 } + unsafe { ll::SDL_GetWindowBrightness(self.context.raw) as f64 } } pub fn set_gamma_ramp<'a, 'b, 'c, R, G, B>(&mut self, red: R, green: G, blue: B) -> Result<(), String> @@ -1264,7 +1258,7 @@ impl WindowRef { }; let result = unsafe { ll::SDL_SetWindowGammaRamp( - self.raw(), unwrapped_red, unwrapped_green, unwrapped_blue + self.context.raw, unwrapped_red, unwrapped_green, unwrapped_blue ) }; if result != 0 { @@ -1280,7 +1274,7 @@ impl WindowRef { let mut blue: Vec = Vec::with_capacity(256); let result = unsafe { ll::SDL_GetWindowGammaRamp( - self.raw(), red.as_mut_ptr(), green.as_mut_ptr(), + self.context.raw, red.as_mut_ptr(), green.as_mut_ptr(), blue.as_mut_ptr() ) };