use rand::Rng; use sdl2::event::Event; use sdl2::{EventPump, VideoSubsystem}; use sdl2::keyboard::Keycode; use sdl2::pixels::Color; use sdl2::render::BlendMode; use sdl2::render::Canvas; use sdl2::video::{SwapInterval, Window}; use ::{SCREEN_HEIGHT, SCREEN_WIDTH}; use boll::*; use common::{Point2D, Rect}; use sprites::SpriteManager; use NS_PER_FRAME; use point; // defined in common, but loaded from main... pub type Nanoseconds = u64; #[derive(Default)] pub struct AppBuilder { resolution: Rect, state: Option>, title: Option, } impl AppBuilder { pub fn with_resolution(mut self, width: u16, height: u16) -> Self { self.resolution = Rect { width, height }; self } pub fn with_state(mut self, state: Box) -> Self { self.state = Some(state); self } pub fn with_title(mut self, title: &str) -> Self { self.title = Some(title.to_string()); self } pub fn build(self) -> Result { let context = sdl2::init().unwrap(); sdl2::image::init(sdl2::image::InitFlag::PNG)?; let video = context.video()?; self.print_video_display_modes(&video); let window = video .window(&self.title.unwrap(), self.resolution.width.into(), self.resolution.height.into()) .position_centered() // .fullscreen() // .fullscreen_desktop() .opengl() .build().unwrap(); context.mouse().show_cursor(false); let mut canvas = window.into_canvas().build().unwrap(); canvas.set_blend_mode(BlendMode::Add); canvas.set_draw_color(Color::RGB(0, 0, 0)); canvas.clear(); canvas.present(); video.gl_set_swap_interval(SwapInterval::VSync)?; let event_pump = context.event_pump()?; let sprites = SpriteManager::new(canvas.texture_creator()); Ok(App { canvas, event_pump, sprites, state: self.state.unwrap_or(Box::new(ActiveState::new())), }) } fn print_video_display_modes(&self, video: &VideoSubsystem) { println!("video subsystem: {:?}", video); println!("current_video_driver: {:?}", video.current_video_driver()); for display in 0..video.num_video_displays().unwrap() { println!("=== display {} - {} ===", display, video.display_name(display).unwrap()); println!(" display_bounds: {:?}", video.display_bounds(display).unwrap()); println!(" num_display_modes: {:?}", video.num_display_modes(display).unwrap()); println!(" desktop_display_mode: {:?}", video.desktop_display_mode(display).unwrap()); println!(" current_display_mode: {:?}", video.current_display_mode(display).unwrap()); for mode in 0..video.num_display_modes(display).unwrap() { println!(" {:2}: {:?}", mode, video.display_mode(display, mode).unwrap()); } } println!("swap interval: {:?}", video.gl_get_swap_interval()); } } pub struct App { pub canvas: Canvas, pub event_pump: EventPump, pub sprites: SpriteManager, pub state: Box, } impl App { pub fn new() -> AppBuilder { Default::default() } pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) { for (name, file) in sprites { self.sprites.load(name, file); } } } pub trait AppState { fn update(&mut self, dt: Nanoseconds); fn render(&self, canvas: &mut Canvas); fn leave(&self); fn on_event(&mut self, event: Event); } type Bollar = Vec>; pub struct ActiveState { bolls: Bollar, boll_size: u32, } impl ActiveState { pub fn new() -> ActiveState { ActiveState { bolls: Bollar::new(), boll_size: 1, } } fn change_boll_count(&mut self, delta: i32) { if delta > 0 { for _i in 0..delta { self.add_boll(); } } else if delta < 0 { for _i in 0..(-delta) { self.bolls.pop(); } } } fn add_boll(&mut self) { let mut rng = rand::thread_rng(); self.bolls.push(Box::new(SquareBoll { pos: point!(rng.gen_range(0, SCREEN_WIDTH) as f64, rng.gen_range(0, SCREEN_HEIGHT) as f64), vel: point!(rng.gen_range(-2.0, 2.0), rng.gen_range(-2.0, 2.0)), })); } } impl AppState for ActiveState { fn update(&mut self, dt: Nanoseconds) { for b in &mut self.bolls { b.update(); } match dt { ns if ns < (NS_PER_FRAME - 90_0000) as u64 => { self.change_boll_count(100) } ns if ns > (NS_PER_FRAME + 90_0000) as u64 => { self.change_boll_count(-100) } _ => {} } } fn render(&self, canvas: &mut Canvas) { for b in &self.bolls { b.draw(canvas, self.boll_size); } } fn leave(&self) { println!("number of bolls: {}", self.bolls.len()); } fn on_event(&mut self, event: Event) { match event { Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.boll_size = std::cmp::min(self.boll_size + 1, 32) } Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => { self.boll_size = std::cmp::max(self.boll_size - 1, 1) } Event::MouseMotion { x, y, .. } => { self.bolls.push(Box::new(CircleBoll::new( point!(x as f64, y as f64), point!(0.0, 0.0), ))) } _ => {} } } }