use crate::image::Image; use crate::vec2::Vec2; use rand::Rng; use std::num::NonZeroU32; 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: [u32; 256], mouse_pos: Vec2, } impl WindowState { fn new() -> Self { WindowState { palette: [0u32; 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 as u32) | ((g as u32) << 8) | ((b as u32) << 16) | 0xff000000; } 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 context = unsafe { softbuffer::Context::new(&window) }.unwrap(); let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.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(_) => {} 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 let Some(k) = input.virtual_keycode { if k == Escape { *control_flow = ControlFlow::Exit; } }; if *control_flow == ControlFlow::Exit { return; } } WindowEvent::ModifiersChanged(_) => {} WindowEvent::Ime(_) => {} WindowEvent::CursorMoved { position, .. } => { window_state.mouse_pos = Vec2 { x: position.x as i32, y: position.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); let (width, height) = { let size = window.inner_size(); (size.width, size.height) }; surface .resize( NonZeroU32::new(width).unwrap(), NonZeroU32::new(height).unwrap(), ) .unwrap(); let mut buf = surface.buffer_mut().unwrap(); for i in 0..(width * height) as usize { buf[i] = window_state.palette[screen.data()[i] as usize]; } buf.present().unwrap(); } WinitEvent::RedrawEventsCleared => {} WinitEvent::LoopDestroyed => {} } game.update(&mut window_state); window.request_redraw(); }); }