diff --git a/Cargo.toml b/Cargo.toml index fbf12ac6..f8475767 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,3 +44,80 @@ mixer = [] use-pkgconfig = [ "sdl2-sys/use-pkgconfig" ] use_mac_framework = ["sdl2-sys/use_mac_framework"] + +[[example]] +name = "animation" + +[[example]] +name = "audio-queue-squarewave" + +[[example]] +name = "audio-wav" + +[[example]] +name = "audio-whitenoise" + +[[example]] +name = "demo" + +[[example]] +name = "game-controller" + +[[example]] +required-features = ["unsafe_textures"] +name = "game-of-life-unsafe-textures" + +[[example]] +name = "game-of-life" + +[[example]] +required-features = ["gfx"] +name = "gfx-demo" + +[[example]] +name = "haptic" + +[[example]] +required-features = ["image"] +name = "image-demo" + +[[example]] +name = "joystick" + +[[example]] +name = "keyboard-state" + +[[example]] +name = "message-box" + +[[example]] +required-features = ["mixer"] +name = "mixer-demo" + +[[example]] +name = "mouse-state" + +[[example]] +name = "no-renderer" + +[[example]] +name = "relative-mouse-state" + +[[example]] +name = "renderer-target" + +[[example]] +name = "renderer-texture" + +[[example]] +name = "renderer-yuv" + +[[example]] +name = "resource-manager" + +[[example]] +required-features = ["ttf"] +name = "ttf-demo" + +[[example]] +name = "window-properties" diff --git a/examples/game-of-life-unsafe-textures.rs b/examples/game-of-life-unsafe-textures.rs new file mode 100644 index 00000000..01beb2e8 --- /dev/null +++ b/examples/game-of-life-unsafe-textures.rs @@ -0,0 +1,295 @@ +extern crate sdl2; + +#[cfg(feature = "unsafe_textures")] +use sdl2::rect::{Point, Rect}; +#[cfg(feature = "unsafe_textures")] +use sdl2::pixels::Color; +#[cfg(feature = "unsafe_textures")] +use sdl2::event::Event; +#[cfg(feature = "unsafe_textures")] +use sdl2::mouse::MouseButton; +#[cfg(feature = "unsafe_textures")] +use sdl2::keyboard::Keycode; +#[cfg(feature = "unsafe_textures")] +use sdl2::video::{Window}; +#[cfg(feature = "unsafe_textures")] +use sdl2::render::{Canvas, Texture}; +#[cfg(feature = "unsafe_textures")] +use game_of_life::{SQUARE_SIZE, PLAYGROUND_WIDTH, PLAYGROUND_HEIGHT}; + + +#[cfg(feature = "unsafe_textures")] +mod game_of_life { + pub const SQUARE_SIZE: u32 = 16; + pub const PLAYGROUND_WIDTH: u32 = 49; + pub const PLAYGROUND_HEIGHT: u32 = 40; + + #[derive(Copy, Clone)] + pub enum State { + Paused, + Playing, + } + + pub struct GameOfLife { + playground: [bool; (PLAYGROUND_WIDTH*PLAYGROUND_HEIGHT) as usize], + state: State, + } + + impl GameOfLife { + pub fn new() -> GameOfLife { + let mut playground = [false; (PLAYGROUND_WIDTH * PLAYGROUND_HEIGHT) as usize]; + + // let's make a nice default pattern ! + for i in 1..(PLAYGROUND_HEIGHT-1) { + playground[(1 + i* PLAYGROUND_WIDTH) as usize] = true; + playground[((PLAYGROUND_WIDTH-2) + i* PLAYGROUND_WIDTH) as usize] = true; + } + for j in 2..(PLAYGROUND_WIDTH-2) { + playground[(PLAYGROUND_WIDTH + j) as usize] = true; + playground[((PLAYGROUND_HEIGHT-2)*PLAYGROUND_WIDTH + j) as usize] = true; + } + + GameOfLife { + playground: playground, + state: State::Paused, + } + } + + pub fn get(&self, x: i32, y: i32) -> Option { + if x >= 0 && y >= 0 && + (x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT { + Some(self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize]) + } else { + None + } + } + + pub fn get_mut(&mut self, x: i32, y: i32) -> Option<&mut bool> { + if x >= 0 && y >= 0 && + (x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT { + Some(&mut self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize]) + } else { + None + } + } + + pub fn toggle_state(&mut self) { + self.state = match self.state { + State::Paused => State::Playing, + State::Playing => State::Paused, + } + } + + pub fn state(&self) -> State { + self.state + } + + pub fn update(&mut self) { + let mut new_playground = self.playground; + for (u, mut square) in new_playground.iter_mut().enumerate() { + let u = u as u32; + let x = u % PLAYGROUND_WIDTH; + let y = u / PLAYGROUND_WIDTH; + let mut count : u32 = 0; + for i in -1..2 { + for j in -1..2 { + if !(i == 0 && j == 0) { + let peek_x : i32 = (x as i32) + i; + let peek_y : i32 = (y as i32) + j; + if let Some(true) = self.get(peek_x, peek_y) { + count += 1; + } + } + } + } + if count > 3 || count < 2 { + *square = false; + } else if count == 3 { + *square = true; + } else if count == 2 { + *square = *square; + } + } + self.playground = new_playground; + } + } + + + + impl<'a> IntoIterator for &'a GameOfLife { + type Item = &'a bool; + type IntoIter = ::std::slice::Iter<'a, bool>; + fn into_iter(self) -> ::std::slice::Iter<'a, bool> { + self.playground.iter() + } + } +} + +#[cfg(feature = "unsafe_textures")] +fn dummy_texture<'a>(canvas: &mut Canvas) -> (Texture, Texture) { + enum TextureColor { + Yellow, + White, + }; + let mut square_texture1 : Texture = + canvas.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).unwrap(); + let mut square_texture2 : Texture = + canvas.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).unwrap(); + // let's change the textures we just created + { + let textures = vec![ + (&mut square_texture1, TextureColor::Yellow), + (&mut square_texture2, TextureColor::White) + ]; + canvas.with_multiple_texture_canvas(textures.iter(), |texture_canvas, user_context| { + texture_canvas.set_draw_color(Color::RGB(0, 0, 0)); + texture_canvas.clear(); + match *user_context { + TextureColor::Yellow => { + for i in 0..SQUARE_SIZE { + for j in 0..SQUARE_SIZE { + if (i+j) % 4 == 0 { + texture_canvas.set_draw_color(Color::RGB(255, 255, 0)); + texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap(); + } + if (i+j*2) % 9 == 0 { + texture_canvas.set_draw_color(Color::RGB(200, 200, 0)); + texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap(); + } + } + } + }, + TextureColor::White => { + for i in 0..SQUARE_SIZE { + for j in 0..SQUARE_SIZE { + // drawing pixel by pixel isn't very effective, but we only do it once and store + // the texture afterwards so it's still alright! + if (i+j) % 7 == 0 { + // this doesn't mean anything, there was some trial and error to find + // something that wasn't too ugly + texture_canvas.set_draw_color(Color::RGB(192, 192, 192)); + texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap(); + } + if (i+j*2) % 5 == 0 { + texture_canvas.set_draw_color(Color::RGB(64, 64, 64)); + texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap(); + } + } + } + } + }; + for i in 0..SQUARE_SIZE { + for j in 0..SQUARE_SIZE { + // drawing pixel by pixel isn't very effective, but we only do it once and store + // the texture afterwards so it's still alright! + if (i+j) % 7 == 0 { + // this doesn't mean anything, there was some trial and serror to find + // something that wasn't too ugly + texture_canvas.set_draw_color(Color::RGB(192, 192, 192)); + texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap(); + } + if (i+j*2) % 5 == 0 { + texture_canvas.set_draw_color(Color::RGB(64, 64, 64)); + texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap(); + } + } + } + }).unwrap(); + } + (square_texture1, square_texture2) +} + +#[cfg(feature = "unsafe_textures")] +pub fn main() { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + // the window is the representation of a window in your operating system, + // however you can only manipulate properties of that window, like its size, whether it's + // fullscreen, ... but you cannot change its content without using a Canvas or using the + // `surface()` method. + let window = video_subsystem + .window("rust-sdl2 demo: Game of Life", + SQUARE_SIZE*PLAYGROUND_WIDTH, + SQUARE_SIZE*PLAYGROUND_HEIGHT) + .position_centered() + .build() + .unwrap(); + + // the canvas allows us to both manipulate the property of the window and to change its content + // via hardware or software rendering. See CanvasBuilder for more info. + let mut canvas = window.into_canvas() + .target_texture() + .present_vsync() + .build().unwrap(); + + println!("Using SDL_Renderer \"{}\"", canvas.info().name); + canvas.set_draw_color(Color::RGB(0, 0, 0)); + // clears the canvas with the color we set in `set_draw_color`. + canvas.clear(); + // However the canvas has not been updated to the window yet, everything has been processed to + // an internal buffer, but if we want our buffer to be displayed on the window, we need to call + // `present`. We need to call this everytime we want to render a new frame on the window. + canvas.present(); + + // Create a "target" texture so that we can use our Renderer with it later + let (square_texture1, square_texture2) = dummy_texture(&mut canvas); + let mut game = game_of_life::GameOfLife::new(); + + let mut event_pump = sdl_context.event_pump().unwrap(); + let mut frame : u32 = 0; + 'running: loop { + // get the inputs here + for event in event_pump.poll_iter() { + match event { + Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { + break 'running + }, + Event::KeyDown { keycode: Some(Keycode::Space), repeat: false, .. } => { + game.toggle_state(); + }, + Event::MouseButtonDown { x, y, mouse_btn: MouseButton::Left, .. } => { + let x = (x as u32) / SQUARE_SIZE; + let y = (y as u32) / SQUARE_SIZE; + match game.get_mut(x as i32, y as i32) { + Some(mut square) => {*square = !(*square);}, + None => {panic!()} + }; + }, + _ => {} + } + } + + // update the game loop here + if frame >= 30 { + game.update(); + frame = 0; + } + + canvas.set_draw_color(Color::RGB(0, 0, 0)); + canvas.clear(); + for (i, unit) in (&game).into_iter().enumerate() { + let i = i as u32; + let square_texture = if frame >= 15 { + &square_texture1 + } else { + &square_texture2 + }; + if *unit { + canvas.copy(&square_texture, + None, + Rect::new(((i % PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32, + ((i / PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32, + SQUARE_SIZE, + SQUARE_SIZE)).unwrap(); + } + } + canvas.present(); + if let game_of_life::State::Playing = game.state() { + frame += 1; + }; + } +} + +#[cfg(not(feature = "unsafe_textures"))] +pub fn main(){}