diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6303919 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,121 @@ +use std::{ + hint, + sync::Arc, + thread, + time::{Duration, Instant}, +}; +use winit::{ + application::ApplicationHandler, + dpi::LogicalSize, + event::{ElementState, WindowEvent}, + event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, + window::{Window, WindowId}, +}; + +struct App { + window: Option<Arc<Window>>, +} + +impl App { + fn new() -> Self { + Self { window: None } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + // ウィンドウメッセージをpollで捌く + // キューが空になる度に`about_to_wait`メソッドが呼ばれる + event_loop.set_control_flow(ControlFlow::Poll); + + let attributes = Window::default_attributes() + .with_inner_size(LogicalSize::new(1280, 720)) + .with_fullscreen(None); + + let window = match event_loop.create_window(attributes) { + Ok(v) => Arc::new(v), + Err(e) => panic!("error: {e}"), + }; + + self.window = Some(window); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::MouseInput { state, button, .. } => match state { + ElementState::Pressed => { + println!("pressed {:?}", button); + } + ElementState::Released => { + println!("released {:?}", button); + } + }, + WindowEvent::CursorMoved { position, .. } => { + let Some(window) = self.window.as_ref() else { + return; + }; + println!( + "moved {:?}", + position.to_logical::<f32>(window.scale_factor()) + ); + } + WindowEvent::CursorLeft { .. } => { + println!("left"); + } + WindowEvent::Focused(false) | WindowEvent::Occluded(true) => { + println!("focused or occluded"); + } + _ => {} + } + } + + fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + let start = Instant::now(); + + let Some(_) = &self.window else { + // NOTE: `window`が`Some`になる`resumed`メソッドで`ControlFlow::Poll`を指定しているため、 + // ここで`ControlFlow::WaitUntil`を指定しても問題ない。 + event_loop.set_control_flow(ControlFlow::WaitUntil(start + Duration::from_millis(16))); + return; + }; + + sync_60fps(start); + } +} + +/// 60fpsになるようにスレッドを停止する関数 +/// +/// * start - フレーム開始時刻 +/// +/// 15.66...msをsleepし、不足時間をスピンウェイトする。 +/// 正直アニメーションの強いゲームではないので厳密さは要らないのだけれど、 +/// 一応アニメーションのあるゲームだからね多少はね。 +fn sync_60fps(start: Instant) { + const TARGET_FRAME_TIME: Duration = Duration::from_nanos(1_000_000_000 / 60); // 60FPS + const SPIN_THRESHOLD: Duration = Duration::from_millis(1); + + let elapsed = start.elapsed(); + + if elapsed < TARGET_FRAME_TIME { + let remaining = TARGET_FRAME_TIME - elapsed; + if remaining > SPIN_THRESHOLD { + thread::sleep(remaining - SPIN_THRESHOLD); + } + while start.elapsed() < TARGET_FRAME_TIME { + hint::spin_loop(); + } + } +} + +fn main() { + let event_loop = match EventLoop::new() { + Ok(v) => v, + Err(e) => panic!("error: {e}"), + }; + + match event_loop.run_app(&mut App::new()) { + Ok(()) => {} + Err(e) => panic!("error: {e}"), + } +} |
