use crate::image::Image; use crate::vec2::Vec2; use pixels::{Pixels, SurfaceTexture}; use rand::Rng; use winit::dpi::LogicalSize; use winit::event::VirtualKeyCode::Escape; use winit::event::{ ElementState, Event as WinitEvent, MouseButton as WinitMouseButton, WindowEvent, }; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; pub enum MouseButton { Left, Right, Middle, WheelUp, WheelDown, Forward, Back, } pub enum Event { MouseClick(MouseButton, Vec2), } pub trait Game { fn new(window_state: &mut WindowState) -> Self; fn update(&mut self, window_state: &mut WindowState); fn on_event(&mut self, window_state: &mut WindowState, event: Event); fn draw(&self, target: &mut Image); } pub struct WindowState { palette: [[u8; 4]; 256], mouse_pos: Vec2, } impl WindowState { fn new() -> Self { WindowState { palette: [[0u8; 4]; 256], mouse_pos: Vec2::zero(), } } pub fn set_palette(&mut self, index: u8, r: u8, g: u8, b: u8) { self.palette[index as usize] = [r, g, b, 0xffu8]; } pub fn scramble_palette(&mut self) { let mut rng = rand::thread_rng(); for i in 0..=255 { self.set_palette(i, rng.gen(), rng.gen(), rng.gen()); } } pub fn mouse_pos(&self) -> Vec2 { self.mouse_pos } } pub fn run(width: i32, height: i32) { let event_loop = EventLoop::new(); let window = { let size = LogicalSize::new(width as f64, height as f64); WindowBuilder::new() .with_title("Skunk 2D") .with_inner_size(size) .with_min_inner_size(size) .with_decorations(false) .with_maximized(true) .build(&event_loop) .unwrap() }; //todo: replace Pixels with custom thingie (startup time slow because wgpu?) let mut pixels = { let window_size = window.inner_size(); let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); Pixels::new(width as u32, height as u32, surface_texture).unwrap() }; let mut screen = Image::new(Vec2 { x: width, y: height, }); let mut window_state = WindowState::new(); let mut game = T::new(&mut window_state); event_loop.run(move |event, _, control_flow| { match event { WinitEvent::NewEvents(_) => {} WinitEvent::WindowEvent { event: window_event, .. } => { match window_event { WindowEvent::Resized(size) => { pixels.resize_surface(size.width, size.height).unwrap(); } WindowEvent::Moved(_) => {} WindowEvent::CloseRequested => {} WindowEvent::Destroyed => {} WindowEvent::DroppedFile(_) => {} WindowEvent::HoveredFile(_) => {} WindowEvent::HoveredFileCancelled => {} WindowEvent::ReceivedCharacter(_) => {} WindowEvent::Focused(_) => {} WindowEvent::KeyboardInput { input, .. } => { //todo: actually do input handling if input.virtual_keycode.unwrap() == Escape { *control_flow = ControlFlow::Exit; return; } } WindowEvent::ModifiersChanged(_) => {} WindowEvent::Ime(_) => {} WindowEvent::CursorMoved { position, .. } => { let (x, y) = pixels .window_pos_to_pixel((position.x as f32, position.y as f32)) .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos)); window_state.mouse_pos = Vec2 { x: x as i32, y: y as i32, } } WindowEvent::CursorEntered { .. } => {} WindowEvent::CursorLeft { .. } => {} WindowEvent::MouseWheel { .. } => {} WindowEvent::MouseInput { button, state, .. } => { let b = match button { WinitMouseButton::Left => MouseButton::Left, WinitMouseButton::Right => MouseButton::Right, WinitMouseButton::Middle => MouseButton::Middle, //todo: handle other mousebuttons WinitMouseButton::Other(b) => match b { _ => MouseButton::Back, }, }; match state { ElementState::Pressed => { let e = Event::MouseClick(b, window_state.mouse_pos); game.on_event(&mut window_state, e); } ElementState::Released => {} } } WindowEvent::TouchpadMagnify { .. } => {} WindowEvent::SmartMagnify { .. } => {} WindowEvent::TouchpadRotate { .. } => {} WindowEvent::TouchpadPressure { .. } => {} WindowEvent::AxisMotion { .. } => {} WindowEvent::Touch(_) => {} WindowEvent::ScaleFactorChanged { .. } => {} WindowEvent::ThemeChanged(_) => {} WindowEvent::Occluded(_) => {} } } WinitEvent::DeviceEvent { .. } => {} WinitEvent::UserEvent(_) => {} WinitEvent::Suspended => {} WinitEvent::Resumed => {} WinitEvent::MainEventsCleared => {} WinitEvent::RedrawRequested(_) => { game.draw(&mut screen); for (i, pixel) in pixels.frame_mut().chunks_exact_mut(4).enumerate() { pixel.copy_from_slice(&window_state.palette[screen.data()[i] as usize]); } pixels.render().unwrap(); } WinitEvent::RedrawEventsCleared => {} WinitEvent::LoopDestroyed => {} } game.update(&mut window_state); window.request_redraw(); }); }