Removed the unused ActiveState
[kaka/rust-sdl-test.git] / src / core / app.rs
CommitLineData
3f344b63 1use core::controller::ControllerManager;
6566d7e5 2use core::render::Renderer;
b1002380 3use geometry::{Dimension};
541aee90 4use sdl2::event::{Event, WindowEvent};
95e3e10d 5use sdl2::keyboard::Keycode;
6566d7e5 6use sdl2::video::SwapInterval;
6ba7aef1 7use sdl2::{EventPump, VideoSubsystem};
3bfea951 8use sprites::SpriteManager;
b1002380 9use time::{Duration, Instant};
6ba7aef1 10
6edafdc0
TW
11#[derive(Default)]
12pub struct AppBuilder {
1f42d724 13 resolution: Dimension<u16>,
6edafdc0
TW
14 state: Option<Box<dyn AppState>>,
15 title: Option<String>,
3bfea951
TW
16}
17
6edafdc0
TW
18impl AppBuilder {
19 pub fn with_resolution(mut self, width: u16, height: u16) -> Self {
1f42d724 20 self.resolution = Dimension { width, height };
6ba7aef1 21 self
6edafdc0
TW
22 }
23
6edafdc0 24 pub fn with_state(mut self, state: Box<dyn AppState>) -> Self {
6ba7aef1
TW
25 self.state = Some(state);
26 self
6edafdc0
TW
27 }
28
29 pub fn with_title(mut self, title: &str) -> Self {
6ba7aef1
TW
30 self.title = Some(title.to_string());
31 self
6edafdc0
TW
32 }
33
fea68d56 34 pub fn build(self) -> Result<App, String> {
3bfea951 35 let context = sdl2::init().unwrap();
fea68d56 36 sdl2::image::init(sdl2::image::InitFlag::PNG)?;
6ba7aef1 37 let video = context.video()?;
b0566120 38 //self.print_video_display_modes(&video);
fea68d56
TW
39
40 let window = video
6ba7aef1
TW
41 .window(
42 &self.title.unwrap(),
43 self.resolution.width.into(),
44 self.resolution.height.into(),
45 )
3bfea951 46 .position_centered()
6ba7aef1
TW
47 // .fullscreen()
48 // .fullscreen_desktop()
3bfea951 49 .opengl()
6ba7aef1
TW
50 .build()
51 .unwrap();
3bfea951 52 context.mouse().show_cursor(false);
6edafdc0 53
6566d7e5
TW
54 let canvas = window.into_canvas().build().unwrap();
55 let sprites = SpriteManager::new(canvas.texture_creator());
6566d7e5 56 let renderer = Renderer::new(canvas);
6edafdc0 57
6ba7aef1 58 video.gl_set_swap_interval(SwapInterval::VSync)?;
fea68d56
TW
59
60 let event_pump = context.event_pump()?;
6edafdc0 61
fea68d56 62 Ok(App {
6566d7e5 63 renderer,
3bfea951
TW
64 event_pump,
65 sprites,
b1002380 66 states: vec!(self.state.unwrap()),
09cd68fe 67 ctrl_man: ControllerManager::new(context.game_controller()?, context.haptic()?),
fea68d56
TW
68 })
69 }
70
b0566120 71 #[allow(dead_code)]
fea68d56 72 fn print_video_display_modes(&self, video: &VideoSubsystem) {
6ba7aef1
TW
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 );
1f362f49 93 let current = video.current_display_mode(display).unwrap();
6ba7aef1
TW
94 println!(
95 " current_display_mode: {:?}",
1f362f49 96 current
6ba7aef1 97 );
1f362f49
TW
98 for idx in 0..video.num_display_modes(display).unwrap() {
99 let mode = video.display_mode(display, idx).unwrap();
6ba7aef1 100 println!(
1f362f49
TW
101 " {}{:2}: {:?}",
102 if mode == current { "*" } else { " " },
103 idx,
104 mode
6ba7aef1
TW
105 );
106 }
107 }
108 println!("swap interval: {:?}", video.gl_get_swap_interval());
3bfea951 109 }
6edafdc0
TW
110}
111
112pub struct App {
6566d7e5 113 renderer: Renderer,
fca4e4f0
TW
114 event_pump: EventPump,
115 sprites: SpriteManager,
eb253fcc 116 states: Vec<Box<dyn AppState>>,
b0566120 117 pub ctrl_man: ControllerManager,
6edafdc0
TW
118}
119
120impl App {
98995f2b 121 #[allow(clippy::new_ret_no_self)]
6edafdc0 122 pub fn new() -> AppBuilder {
6ba7aef1 123 Default::default()
6edafdc0 124 }
3bfea951 125
1e322944 126 pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) {
3bfea951
TW
127 for (name, file) in sprites {
128 self.sprites.load(name, file);
129 }
130 }
6ba7aef1
TW
131
132 pub fn start(&mut self) {
902b2b31 133 let mut last_time = Instant::now();
6ba7aef1 134
3af74c40 135 self.states[0].enter(&self.ctrl_man);
b0566120 136
eb253fcc
TW
137 loop {
138 if let Some(change) = self.handle_events() {
139 self.handle_state_change(change);
541aee90 140 }
6ba7aef1 141
902b2b31
TW
142 let duration = Instant::now() - last_time;
143 last_time = Instant::now();
144
bf7b5671 145 self.ctrl_man.update(duration);
eb253fcc
TW
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 }
541aee90
TW
154
155 self.render();
6ba7aef1 156 }
eb253fcc 157 }
6ba7aef1 158
eb253fcc
TW
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 // }
0c56b1f7 165 state.enter(&self.ctrl_man);
eb253fcc
TW
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 }
6ba7aef1 179 }
541aee90 180
eb253fcc 181 fn handle_events(&mut self) -> Option<StateChange> {
541aee90 182 for event in self.event_pump.poll_iter() {
3f344b63 183 self.ctrl_man.handle_event(&event);
541aee90
TW
184 match event {
185 Event::Quit { .. }
186 | Event::KeyDown {
187 keycode: Some(Keycode::Escape),
188 ..
189 } => {
eb253fcc 190 return Some(StateChange::Pop)
541aee90
TW
191 }
192 Event::KeyDown {
193 keycode: Some(Keycode::F11),
194 ..
195 } => {
6566d7e5 196 self.renderer.toggle_fullscreen();
541aee90
TW
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 }
eb253fcc
TW
240 _ => {
241 if let Some(state) = self.states.last_mut() {
40c949e5
TW
242 if let Some(change) = state.handle_event(event) {
243 return Some(change);
244 }
eb253fcc
TW
245 } else {
246 return Some(StateChange::Exit)
247 }
248 },
541aee90
TW
249 }
250 }
eb253fcc 251 None
541aee90
TW
252 }
253
254 fn render(&mut self) {
6566d7e5 255 self.renderer.clear();
0c56b1f7 256 self.states.last_mut().unwrap().render(&mut self.renderer, &self.sprites);
6566d7e5 257 self.renderer.present();
541aee90 258 }
3bfea951 259}
95e3e10d 260
eb253fcc
TW
261pub enum StateChange {
262 Push(Box<dyn AppState>),
263 Pop,
264 Exit,
265}
266
95e3e10d 267pub trait AppState {
a82a4d23 268 fn enter(&mut self, ctrl_man: &ControllerManager);
b0566120 269 fn leave(&mut self);
eb253fcc 270 fn update(&mut self, dt: Duration) -> Option<StateChange>;
6566d7e5 271 fn render(&mut self, renderer: &mut Renderer, sprites: &SpriteManager);
eb253fcc 272 fn handle_event(&mut self, event: Event) -> Option<StateChange>;
95e3e10d 273}