Removed the unused ActiveState
[kaka/rust-sdl-test.git] / src / core / app.rs
1 use core::controller::ControllerManager;
2 use core::render::Renderer;
3 use geometry::{Dimension};
4 use sdl2::event::{Event, WindowEvent};
5 use sdl2::keyboard::Keycode;
6 use sdl2::video::SwapInterval;
7 use sdl2::{EventPump, VideoSubsystem};
8 use sprites::SpriteManager;
9 use time::{Duration, Instant};
10
11 #[derive(Default)]
12 pub struct AppBuilder {
13     resolution: Dimension<u16>,
14     state: Option<Box<dyn AppState>>,
15     title: Option<String>,
16 }
17
18 impl AppBuilder {
19     pub fn with_resolution(mut self, width: u16, height: u16) -> Self {
20         self.resolution = Dimension { width, height };
21         self
22     }
23
24     pub fn with_state(mut self, state: Box<dyn AppState>) -> Self {
25         self.state = Some(state);
26         self
27     }
28
29     pub fn with_title(mut self, title: &str) -> Self {
30         self.title = Some(title.to_string());
31         self
32     }
33
34     pub fn build(self) -> Result<App, String> {
35         let context = sdl2::init().unwrap();
36         sdl2::image::init(sdl2::image::InitFlag::PNG)?;
37         let video = context.video()?;
38         //self.print_video_display_modes(&video);
39
40         let window = video
41             .window(
42                 &self.title.unwrap(),
43                 self.resolution.width.into(),
44                 self.resolution.height.into(),
45             )
46             .position_centered()
47             // .fullscreen()
48             // .fullscreen_desktop()
49             .opengl()
50             .build()
51             .unwrap();
52         context.mouse().show_cursor(false);
53
54         let canvas = window.into_canvas().build().unwrap();
55         let sprites = SpriteManager::new(canvas.texture_creator());
56         let renderer = Renderer::new(canvas);
57
58         video.gl_set_swap_interval(SwapInterval::VSync)?;
59
60         let event_pump = context.event_pump()?;
61
62         Ok(App {
63             renderer,
64             event_pump,
65             sprites,
66             states: vec!(self.state.unwrap()),
67             ctrl_man: ControllerManager::new(context.game_controller()?, context.haptic()?),
68         })
69     }
70
71     #[allow(dead_code)]
72     fn print_video_display_modes(&self, video: &VideoSubsystem) {
73         println!("video subsystem: {:?}", video);
74         println!("current_video_driver: {:?}", video.current_video_driver());
75         for display in 0..video.num_video_displays().unwrap() {
76             println!(
77                 "=== display {} - {} ===",
78                 display,
79                 video.display_name(display).unwrap()
80             );
81             println!(
82                 " display_bounds: {:?}",
83                 video.display_bounds(display).unwrap()
84             );
85             println!(
86                 " num_display_modes: {:?}",
87                 video.num_display_modes(display).unwrap()
88             );
89             println!(
90                 " desktop_display_mode: {:?}",
91                 video.desktop_display_mode(display).unwrap()
92             );
93             let current = video.current_display_mode(display).unwrap();
94             println!(
95                 " current_display_mode: {:?}",
96                 current
97             );
98             for idx in 0..video.num_display_modes(display).unwrap() {
99                 let mode = video.display_mode(display, idx).unwrap();
100                 println!(
101                     " {}{:2}: {:?}",
102                     if mode == current { "*" } else { " " },
103                     idx,
104                     mode
105                 );
106             }
107         }
108         println!("swap interval: {:?}", video.gl_get_swap_interval());
109     }
110 }
111
112 pub struct App {
113     renderer: Renderer,
114     event_pump: EventPump,
115     sprites: SpriteManager,
116     states: Vec<Box<dyn AppState>>,
117     pub ctrl_man: ControllerManager,
118 }
119
120 impl App {
121     #[allow(clippy::new_ret_no_self)]
122     pub fn new() -> AppBuilder {
123         Default::default()
124     }
125
126     pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) {
127         for (name, file) in sprites {
128             self.sprites.load(name, file);
129         }
130     }
131
132     pub fn start(&mut self) {
133         let mut last_time = Instant::now();
134
135         self.states[0].enter(&self.ctrl_man);
136
137         loop {
138             if let Some(change) = self.handle_events() {
139                 self.handle_state_change(change);
140             }
141
142             let duration = Instant::now() - last_time;
143             last_time = Instant::now();
144
145             self.ctrl_man.update(duration);
146
147             if let Some(state) = self.states.last_mut() {
148                 if let Some(change) = state.update(duration) {
149                     self.handle_state_change(change);
150                 }
151             } else {
152                 break;
153             }
154
155             self.render();
156         }
157     }
158
159     fn handle_state_change(&mut self, change: StateChange) {
160         match change {
161             StateChange::Push(mut state) => {
162                 // if let Some(s) = self.states.last_mut() {
163                 //     s.pause();
164                 // }
165                 state.enter(&self.ctrl_man);
166                 self.states.push(state);
167             }
168             StateChange::Pop => {
169                 if let Some(mut s) = self.states.pop() {
170                     s.leave();
171                 }
172             }
173             StateChange::Exit => {
174                 while let Some(mut s) = self.states.pop() {
175                     s.leave();
176                 }
177             }
178         }
179     }
180
181     fn handle_events(&mut self) -> Option<StateChange> {
182         for event in self.event_pump.poll_iter() {
183             self.ctrl_man.handle_event(&event);
184             match event {
185                 Event::Quit { .. }
186                 | Event::KeyDown {
187                     keycode: Some(Keycode::Escape),
188                     ..
189                 } => {
190                     return Some(StateChange::Pop)
191                 }
192                 Event::KeyDown {
193                     keycode: Some(Keycode::F11),
194                     ..
195                 } => {
196                     self.renderer.toggle_fullscreen();
197                 }
198                 Event::Window {
199                     win_event: WindowEvent::Resized(x, y),
200                     ..
201                 } => {
202                     println!("window resized({}, {})", x, y)
203                 }
204                 Event::Window {
205                     win_event: WindowEvent::Maximized,
206                     ..
207                 } => {
208                     println!("window maximized")
209                 }
210                 Event::Window {
211                     win_event: WindowEvent::Restored,
212                     ..
213                 } => {
214                     println!("window restored")
215                 }
216                 Event::Window {
217                     win_event: WindowEvent::Enter,
218                     ..
219                 } => {
220                     println!("window enter")
221                 }
222                 Event::Window {
223                     win_event: WindowEvent::Leave,
224                     ..
225                 } => {
226                     println!("window leave")
227                 }
228                 Event::Window {
229                     win_event: WindowEvent::FocusGained,
230                     ..
231                 } => {
232                     println!("window focus gained")
233                 }
234                 Event::Window {
235                     win_event: WindowEvent::FocusLost,
236                     ..
237                 } => {
238                     println!("window focus lost")
239                 }
240                 _ => {
241                     if let Some(state) = self.states.last_mut() {
242                         if let Some(change) = state.handle_event(event) {
243                             return Some(change);
244                         }
245                     } else {
246                         return Some(StateChange::Exit)
247                     }
248                 },
249             }
250         }
251         None
252     }
253
254     fn render(&mut self) {
255         self.renderer.clear();
256         self.states.last_mut().unwrap().render(&mut self.renderer, &self.sprites);
257         self.renderer.present();
258     }
259 }
260
261 pub enum StateChange {
262     Push(Box<dyn AppState>),
263     Pop,
264     Exit,
265 }
266
267 pub trait AppState {
268     fn enter(&mut self, ctrl_man: &ControllerManager);
269     fn leave(&mut self);
270     fn update(&mut self, dt: Duration) -> Option<StateChange>;
271     fn render(&mut self, renderer: &mut Renderer, sprites: &SpriteManager);
272     fn handle_event(&mut self, event: Event) -> Option<StateChange>;
273 }