Removed the unused ActiveState
[kaka/rust-sdl-test.git] / src / core / app.rs
... / ...
CommitLineData
1use core::controller::ControllerManager;
2use core::render::Renderer;
3use geometry::{Dimension};
4use sdl2::event::{Event, WindowEvent};
5use sdl2::keyboard::Keycode;
6use sdl2::video::SwapInterval;
7use sdl2::{EventPump, VideoSubsystem};
8use sprites::SpriteManager;
9use time::{Duration, Instant};
10
11#[derive(Default)]
12pub struct AppBuilder {
13 resolution: Dimension<u16>,
14 state: Option<Box<dyn AppState>>,
15 title: Option<String>,
16}
17
18impl 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
112pub 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
120impl 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
261pub enum StateChange {
262 Push(Box<dyn AppState>),
263 Pop,
264 Exit,
265}
266
267pub 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}