Add support for basic input handling without breakout

This commit is contained in:
shivshank 2018-07-28 16:48:23 -04:00
parent 41fcc99608
commit 0d4e6a9822
3 changed files with 168 additions and 5 deletions

View file

@ -1,8 +1,71 @@
use glutin::{GlWindow, EventsLoop}; use glutin::{
GlWindow,
EventsLoop,
VirtualKeyCode,
MouseButton,
ModifiersState,
};
use core::Framebuffer; use core::Framebuffer;
use std::collections::HashMap;
pub struct GlutinBreakout { pub struct GlutinBreakout {
pub events_loop: EventsLoop, pub events_loop: EventsLoop,
pub gl_window: GlWindow, pub gl_window: GlWindow,
pub fb: Framebuffer, pub fb: Framebuffer,
} }
pub struct BasicInput {
/// The mouse position in buffer coordinates
pub mouse_pos: (usize, usize),
/// Stores whether a mouse button was down and is down, in that order.
///
/// If a button has not been pressed yet it will not be in the map.
pub mouse: HashMap<MouseButton, (bool, bool)>,
/// Stores the previous and current "key down" states, in that order.
///
/// If a key has not been pressed yet it will not be in the map.
pub keys: HashMap<VirtualKeyCode, (bool, bool)>,
pub modifiers: ModifiersState,
pub resized: bool,
}
impl BasicInput {
/// If the mouse was pressed this last frame.
pub fn mouse_pressed(&self, button: MouseButton) -> bool {
&(false, true) == self.mouse.get(&button).unwrap_or(&(false, false))
}
/// If the mouse is currently down.
pub fn mouse_is_down(&self, button: MouseButton) -> bool {
if let &(_, true) = self.mouse.get(&button).unwrap_or(&(false, false)) {
true
} else {
false
}
}
/// If the mouse was released this last frame.
pub fn mouse_released(&self, button: MouseButton) -> bool {
&(true, false) == self.mouse.get(&button).unwrap_or(&(false, false))
}
/// If the key was pressed this last frame.
pub fn key_pressed(&self, button: VirtualKeyCode) -> bool {
&(false, true) == self.keys.get(&button).unwrap_or(&(false, false))
}
/// If the key is currently down.
pub fn key_is_down(&self, button: VirtualKeyCode) -> bool {
if let &(_, true) = self.keys.get(&button).unwrap_or(&(false, false)) {
true
} else {
false
}
}
/// If the key was released this last frame.
pub fn key_released(&self, button: VirtualKeyCode) -> bool {
&(true, false) == self.keys.get(&button).unwrap_or(&(false, false))
}
}

View file

@ -1,4 +1,4 @@
use breakout::GlutinBreakout; use breakout::{GlutinBreakout, BasicInput};
use rustic_gl; use rustic_gl;
@ -13,12 +13,13 @@ use glutin::{
VirtualKeyCode, VirtualKeyCode,
ElementState, ElementState,
}; };
use glutin::dpi::LogicalSize; use glutin::dpi::{LogicalSize, LogicalPosition};
use gl; use gl;
use gl::types::*; use gl::types::*;
use std::mem::size_of_val; use std::mem::size_of_val;
use std::collections::HashMap;
/// Create a context using glutin given a configuration. /// Create a context using glutin given a configuration.
pub fn init_glutin_context<S: ToString>( pub fn init_glutin_context<S: ToString>(
@ -132,6 +133,7 @@ pub fn init_framebuffer(
vao, vao,
vbo, vbo,
texture_format, texture_format,
did_draw: false,
} }
} }
@ -242,6 +244,83 @@ impl Internal {
} }
} }
pub fn glutin_handle_basic_input<F: FnMut(&mut Framebuffer, &BasicInput) -> bool>(
&mut self, mut handler: F
) {
let mut running = true;
let mut input = BasicInput {
// Not sure how to set mouse pos at start
mouse_pos: (0, 0),
mouse: HashMap::new(),
keys: HashMap::new(),
modifiers: Default::default(),
resized: false,
};
while running {
let mut new_size = None;
let mut new_mouse_pos: Option<LogicalPosition> = None;
self.events_loop.poll_events(|event| {
// Copy the current states into the previous state for input
for (_, val) in &mut input.keys {
val.0 = val.1;
}
for (_, val) in &mut input.mouse {
val.0 = val.1;
}
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => running = false,
WindowEvent::KeyboardInput { input: event_input, .. } => {
if let Some(vk) = event_input.virtual_keycode {
let key = input.keys.entry(vk)
.or_insert((false, false));
key.1 = event_input.state == ElementState::Pressed;
}
input.modifiers = event_input.modifiers;
}
WindowEvent::CursorMoved { position, modifiers, ..} => {
new_mouse_pos = Some(position);
input.modifiers = modifiers;
}
WindowEvent::MouseInput { state, button, modifiers, .. } => {
let button = input.mouse.entry(button)
.or_insert((false, false));
button.1 = state == ElementState::Pressed;
input.modifiers = modifiers;
}
WindowEvent::Resized(logical_size) => {
new_size = Some(logical_size);
}
_ => {},
},
_ => {},
}
});
if let Some(size) = new_size {
let dpi_factor = self.gl_window.get_hidpi_factor();
let (x, y) = size.to_physical(dpi_factor).into();
self.resize_viewport(x, y);
input.resized = false;
}
if let Some(pos) = new_mouse_pos {
let dpi_factor = self.gl_window.get_hidpi_factor();
let (x, y): (f64, f64) = pos.to_physical(dpi_factor).into();
let x_scale = self.fb.buffer_width as f64 / (self.fb.vp_width as f64);
let y_scale = self.fb.buffer_height as f64 / (self.fb.vp_height as f64);
let mouse_pos = ((x * x_scale).floor() as usize, (y * y_scale).floor() as usize);
input.mouse_pos = mouse_pos;
}
if running {
running = handler(&mut self.fb, &input);
if self.fb.did_draw {
self.gl_window.swap_buffers().unwrap();
self.fb.did_draw = false;
}
}
}
}
pub fn glutin_breakout(self) -> GlutinBreakout { pub fn glutin_breakout(self) -> GlutinBreakout {
GlutinBreakout { GlutinBreakout {
events_loop: self.events_loop, events_loop: self.events_loop,
@ -278,6 +357,7 @@ pub struct Framebuffer {
pub vao: GLuint, pub vao: GLuint,
pub vbo: GLuint, pub vbo: GLuint,
pub texture_format: (BufferFormat, GLenum), pub texture_format: (BufferFormat, GLenum),
pub did_draw: bool,
} }
impl Framebuffer { impl Framebuffer {
@ -375,6 +455,7 @@ impl Framebuffer {
gl::BindVertexArray(0); gl::BindVertexArray(0);
gl::UseProgram(0); gl::UseProgram(0);
} }
self.did_draw = true;
} }
pub fn relink_program(&mut self) { pub fn relink_program(&mut self) {

View file

@ -72,9 +72,9 @@ pub mod config;
pub mod core; pub mod core;
pub mod breakout; pub mod breakout;
pub use breakout::GlutinBreakout; pub use breakout::{GlutinBreakout, BasicInput};
pub use config::Config; pub use config::Config;
pub use core::{Internal, BufferFormat}; pub use core::{Internal, BufferFormat, Framebuffer};
use core::ToGlType; use core::ToGlType;
@ -304,6 +304,25 @@ impl MiniGlFb {
self.internal.persist_and_redraw(redraw); self.internal.persist_and_redraw(redraw);
} }
/// Provides an easy interface for rudimentary input handling.
///
/// Automatically handles close events and partially handles resizes (the caller chooses if
/// a redraw is necessary).
///
/// Polls for window events and summarizes the input events for you each frame. See
/// `BasicInput` for the information that is provided to you. You will need to use some
/// glutin types (which just wraps the crate winit's input types), so glutin is re-expoted
/// by this library. You can access it via `use mini_gl_fb::glutin`.
///
/// You can cause the handler to exit by returning false from it. This does not kill the
/// window, so as long as you still have it in scope, you can actually keep using it and,
/// for example, resume handling input but with a different handler callback.
pub fn glutin_handle_basic_input<F: FnMut(&mut Framebuffer, &BasicInput) -> bool>(
&mut self, handler: F
) {
self.internal.glutin_handle_basic_input(handler);
}
/// Need full access to Glutin's event handling? No problem! /// Need full access to Glutin's event handling? No problem!
/// ///
/// Hands you the event loop and the window we created, so you can handle events however you /// Hands you the event loop and the window we created, so you can handle events however you