1 extern crate sdl2;
2 
3 #[cfg(feature = "unsafe_textures")]
4 use sdl2::rect::{Point, Rect};
5 #[cfg(feature = "unsafe_textures")]
6 use sdl2::pixels::Color;
7 #[cfg(feature = "unsafe_textures")]
8 use sdl2::event::Event;
9 #[cfg(feature = "unsafe_textures")]
10 use sdl2::mouse::MouseButton;
11 #[cfg(feature = "unsafe_textures")]
12 use sdl2::keyboard::Keycode;
13 #[cfg(feature = "unsafe_textures")]
14 use sdl2::video::{Window};
15 #[cfg(feature = "unsafe_textures")]
16 use sdl2::render::{Canvas, Texture};
17 #[cfg(feature = "unsafe_textures")]
18 use game_of_life::{SQUARE_SIZE, PLAYGROUND_WIDTH, PLAYGROUND_HEIGHT};
19 
20 
21 #[cfg(feature = "unsafe_textures")]
22 mod game_of_life {
23     pub const SQUARE_SIZE: u32 = 16;
24     pub const PLAYGROUND_WIDTH: u32 = 49;
25     pub const PLAYGROUND_HEIGHT: u32 = 40;
26 
27     #[derive(Copy, Clone)]
28     pub enum State {
29         Paused,
30         Playing,
31     }
32 
33     pub struct GameOfLife {
34         playground: [bool; (PLAYGROUND_WIDTH*PLAYGROUND_HEIGHT) as usize],
35         state: State,
36     }
37 
38     impl GameOfLife {
new() -> GameOfLife39         pub fn new() -> GameOfLife {
40             let mut playground = [false; (PLAYGROUND_WIDTH * PLAYGROUND_HEIGHT) as usize];
41 
42             // let's make a nice default pattern !
43             for i in 1..(PLAYGROUND_HEIGHT-1) {
44                 playground[(1 + i* PLAYGROUND_WIDTH) as usize] = true;
45                 playground[((PLAYGROUND_WIDTH-2) + i* PLAYGROUND_WIDTH) as usize] = true;
46             }
47             for j in 2..(PLAYGROUND_WIDTH-2) {
48                 playground[(PLAYGROUND_WIDTH + j) as usize] = true;
49                 playground[((PLAYGROUND_HEIGHT-2)*PLAYGROUND_WIDTH + j) as usize] = true;
50             }
51 
52             GameOfLife {
53                 playground: playground,
54                 state: State::Paused,
55             }
56         }
57 
get(&self, x: i32, y: i32) -> Option<bool>58         pub fn get(&self, x: i32, y: i32) -> Option<bool> {
59             if x >= 0 && y >= 0 &&
60                (x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT {
61                 Some(self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize])
62             } else {
63                 None
64             }
65         }
66 
get_mut(&mut self, x: i32, y: i32) -> Option<&mut bool>67         pub fn get_mut(&mut self, x: i32, y: i32) -> Option<&mut bool> {
68             if x >= 0 && y >= 0 &&
69                (x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT {
70                 Some(&mut self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize])
71             } else {
72                 None
73             }
74         }
75 
toggle_state(&mut self)76         pub fn toggle_state(&mut self) {
77             self.state = match self.state {
78                 State::Paused => State::Playing,
79                 State::Playing => State::Paused,
80             }
81         }
82 
state(&self) -> State83         pub fn state(&self) -> State {
84             self.state
85         }
86 
update(&mut self)87         pub fn update(&mut self) {
88             let mut new_playground = self.playground;
89             for (u, square) in new_playground.iter_mut().enumerate() {
90                 let u = u as u32;
91                 let x = u % PLAYGROUND_WIDTH;
92                 let y = u / PLAYGROUND_WIDTH;
93                 let mut count : u32 = 0;
94                 for i in -1..2 {
95                     for j in -1..2 {
96                         if !(i == 0 && j == 0) {
97                             let peek_x : i32 = (x as i32) + i;
98                             let peek_y : i32 = (y as i32) + j;
99                             if let Some(true) = self.get(peek_x, peek_y) {
100                                 count += 1;
101                             }
102                         }
103                     }
104                 }
105                 if count > 3 || count < 2 {
106                     *square = false;
107                 } else if count == 3 {
108                     *square = true;
109                 } else if count == 2 {
110                     *square = *square;
111                 }
112             }
113             self.playground = new_playground;
114         }
115     }
116 
117 
118 
119     impl<'a> IntoIterator for &'a GameOfLife {
120         type Item = &'a bool;
121         type IntoIter = ::std::slice::Iter<'a, bool>;
into_iter(self) -> ::std::slice::Iter<'a, bool>122         fn into_iter(self) -> ::std::slice::Iter<'a, bool> {
123             self.playground.iter()
124         }
125     }
126 }
127 
128 #[cfg(feature = "unsafe_textures")]
dummy_texture<'a>(canvas: &mut Canvas<Window>) -> Result<(Texture, Texture), String>129 fn dummy_texture<'a>(canvas: &mut Canvas<Window>) -> Result<(Texture, Texture), String> {
130     enum TextureColor {
131         Yellow,
132         White,
133     };
134     let mut square_texture1 = canvas.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).map_err(|e| e.to_string())?;
135     let mut square_texture2 = canvas.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).map_err(|e| e.to_string())?;
136         // let's change the textures we just created
137     {
138         let textures = vec![
139             (&mut square_texture1, TextureColor::Yellow),
140             (&mut square_texture2, TextureColor::White)
141         ];
142         canvas.with_multiple_texture_canvas(textures.iter(), |texture_canvas, user_context| {
143             texture_canvas.set_draw_color(Color::RGB(0, 0, 0));
144             texture_canvas.clear();
145             match *user_context {
146                 TextureColor::Yellow => {
147                     for i in 0..SQUARE_SIZE {
148                         for j in 0..SQUARE_SIZE {
149                             if (i+j) % 4 == 0 {
150                                 texture_canvas.set_draw_color(Color::RGB(255, 255, 0));
151                                 texture_canvas.draw_point(Point::new(i as i32, j as i32))
152                                     .expect("could not draw point");
153                             }
154                             if (i+j*2) % 9 == 0 {
155                                 texture_canvas.set_draw_color(Color::RGB(200, 200, 0));
156                                 texture_canvas.draw_point(Point::new(i as i32, j as i32))
157                                     .expect("could not draw point");
158                             }
159                         }
160                     }
161                 },
162                 TextureColor::White => {
163                     for i in 0..SQUARE_SIZE {
164                         for j in 0..SQUARE_SIZE {
165                             // drawing pixel by pixel isn't very effective, but we only do it once and store
166                             // the texture afterwards so it's still alright!
167                             if (i+j) % 7 == 0 {
168                                 // this doesn't mean anything, there was some trial and error to find
169                                 // something that wasn't too ugly
170                                 texture_canvas.set_draw_color(Color::RGB(192, 192, 192));
171                                 texture_canvas.draw_point(Point::new(i as i32, j as i32))
172                                     .expect("could not draw point");
173                             }
174                             if (i+j*2) % 5 == 0 {
175                                 texture_canvas.set_draw_color(Color::RGB(64, 64, 64));
176                                 texture_canvas.draw_point(Point::new(i as i32, j as i32))
177                                     .expect("could not draw point");
178                             }
179                         }
180                     }
181                 }
182             };
183             for i in 0..SQUARE_SIZE {
184                 for j in 0..SQUARE_SIZE {
185                     // drawing pixel by pixel isn't very effective, but we only do it once and store
186                     // the texture afterwards so it's still alright!
187                     if (i+j) % 7 == 0 {
188                         // this doesn't mean anything, there was some trial and serror to find
189                         // something that wasn't too ugly
190                         texture_canvas.set_draw_color(Color::RGB(192, 192, 192));
191                         texture_canvas.draw_point(Point::new(i as i32, j as i32))
192                             .expect("could not draw point");
193                     }
194                     if (i+j*2) % 5 == 0 {
195                         texture_canvas.set_draw_color(Color::RGB(64, 64, 64));
196                         texture_canvas.draw_point(Point::new(i as i32, j as i32))
197                             .expect("could not draw point");
198                     }
199                 }
200             }
201         }).map_err(|e| e.to_string())?;
202     }
203     Ok((square_texture1, square_texture2))
204 }
205 
206 #[cfg(feature = "unsafe_textures")]
main() -> Result<(), String>207 pub fn main() -> Result<(), String> {
208     let sdl_context = sdl2::init()?;
209     let video_subsystem = sdl_context.video()?;
210 
211     // the window is the representation of a window in your operating system,
212     // however you can only manipulate properties of that window, like its size, whether it's
213     // fullscreen, ... but you cannot change its content without using a Canvas or using the
214     // `surface()` method.
215     let window = video_subsystem
216         .window("rust-sdl2 demo: Game of Life",
217                 SQUARE_SIZE*PLAYGROUND_WIDTH,
218                 SQUARE_SIZE*PLAYGROUND_HEIGHT)
219         .position_centered()
220         .build()
221         .map_err(|e| e.to_string())?;
222 
223     // the canvas allows us to both manipulate the property of the window and to change its content
224     // via hardware or software rendering. See CanvasBuilder for more info.
225     let mut canvas = window.into_canvas()
226         .target_texture()
227         .present_vsync()
228         .build().map_err(|e| e.to_string())?;
229 
230     println!("Using SDL_Renderer \"{}\"", canvas.info().name);
231     canvas.set_draw_color(Color::RGB(0, 0, 0));
232     // clears the canvas with the color we set in `set_draw_color`.
233     canvas.clear();
234     // However the canvas has not been updated to the window yet, everything has been processed to
235     // an internal buffer, but if we want our buffer to be displayed on the window, we need to call
236     // `present`. We need to call this everytime we want to render a new frame on the window.
237     canvas.present();
238 
239     // Create a "target" texture so that we can use our Renderer with it later
240     let (square_texture1, square_texture2) = dummy_texture(&mut canvas)?;
241     let mut game = game_of_life::GameOfLife::new();
242 
243     let mut event_pump = sdl_context.event_pump()?;
244     let mut frame : u32 = 0;
245     'running: loop {
246         // get the inputs here
247         for event in event_pump.poll_iter() {
248             match event {
249                 Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
250                     break 'running
251                 },
252                 Event::KeyDown { keycode: Some(Keycode::Space), repeat: false, .. } => {
253                     game.toggle_state();
254                 },
255                 Event::MouseButtonDown { x, y, mouse_btn: MouseButton::Left, .. } => {
256                     let x = (x as u32) / SQUARE_SIZE;
257                     let y = (y as u32) / SQUARE_SIZE;
258                     match game.get_mut(x as i32, y as i32) {
259                         Some(square) => {*square = !(*square);},
260                         None => unreachable!(),
261                     };
262                 },
263                 _ => {}
264             }
265         }
266 
267         // update the game loop here
268         if frame >= 30 {
269             game.update();
270             frame = 0;
271         }
272 
273         canvas.set_draw_color(Color::RGB(0, 0, 0));
274         canvas.clear();
275         for (i, unit) in (&game).into_iter().enumerate() {
276             let i = i as u32;
277             let square_texture = if frame >= 15 {
278                 &square_texture1
279             } else {
280                 &square_texture2
281             };
282             if *unit {
283                 canvas.copy(&square_texture,
284                             None,
285                             Rect::new(((i % PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32,
286                                       ((i / PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32,
287                                       SQUARE_SIZE,
288                                       SQUARE_SIZE))?;
289             }
290         }
291         canvas.present();
292         if let game_of_life::State::Playing = game.state() {
293             frame += 1;
294         };
295     }
296 
297     Ok(())
298 }
299 
300 #[cfg(not(feature = "unsafe_textures"))]
main()301 pub fn main(){}
302