use flume::Receiver;
use glam::Vec2;
use glfw::{Action, Key, MouseButton};
use ivy_base::{Events, Extent, Position2D};
use ivy_resources::Resources;
use ivy_window::Window;
use crate::events::InputEvent;
pub const MAX_KEYS: usize = glfw::Key::Menu as usize;
pub const MAX_MOUSE_BUTTONS: usize = glfw::MouseButton::Button8 as usize;
pub struct Input {
release_unfocus: bool,
rx: Receiver<InputEvent>,
keys: [bool; MAX_KEYS],
mouse_buttons: [bool; MAX_MOUSE_BUTTONS],
cursor_pos: Position2D,
scroll: Vec2,
old_cursor_pos: Position2D,
window_extent: Extent,
}
impl Input {
pub fn new(resources: &Resources, events: &mut Events) -> ivy_resources::Result<Self> {
let keys = [false; MAX_KEYS];
let mouse_buttons = [false; MAX_MOUSE_BUTTONS];
let rx = events.subscribe();
let window = resources.get_default::<Window>()?;
let window_size = window.extent();
let cursor_pos = window.cursor_pos().into();
Ok(Self {
release_unfocus: true,
rx,
keys,
mouse_buttons,
cursor_pos,
window_extent: window_size,
old_cursor_pos: cursor_pos,
scroll: Vec2::ZERO,
})
}
pub fn handle_events(&mut self) {
self.old_cursor_pos = self.cursor_pos;
self.scroll = Vec2::ZERO;
for e in self.rx.try_iter() {
match e {
InputEvent::MouseButton {
button,
action,
mods: _,
} => {
self.mouse_buttons[button as usize] =
action == Action::Press || action == Action::Repeat
}
InputEvent::CursorPos(val) => self.cursor_pos = val,
InputEvent::Scroll(val) => self.scroll += val,
InputEvent::Key {
key,
scancode: _,
action,
mods: _,
} => {
if (key as usize) < MAX_KEYS {
self.keys[key as usize] =
action == Action::Press || action == Action::Repeat
}
}
InputEvent::Focus(false) => {
if self.release_unfocus {
self.keys = [false; MAX_KEYS];
self.mouse_buttons = [false; MAX_MOUSE_BUTTONS];
}
}
InputEvent::Size(extent) => self.window_extent = extent,
_ => {}
}
}
}
#[inline]
pub fn key(&self, key: Key) -> bool {
self.keys[key as usize]
}
#[inline]
pub fn mouse_button(&self, button: MouseButton) -> bool {
self.mouse_buttons[button as usize]
}
#[inline]
pub fn normalized_cursor_pos(&self) -> Position2D {
let pos = self.cursor_pos;
Position2D::new(
(2.0 * pos.x) / self.window_extent.width as f32 - 1.0,
1.0 - (2.0 * pos.y) / self.window_extent.height as f32,
)
}
#[inline]
pub fn cursor_pos(&self) -> Position2D {
self.cursor_pos
}
#[inline]
pub fn cursor_movement(&self) -> Position2D {
self.old_cursor_pos - self.cursor_pos
}
#[inline]
pub fn normalized_cursor_movement(&self) -> Position2D {
self.cursor_movement() / self.window_extent().as_vec()
}
#[inline]
pub fn scroll(&self) -> Vec2 {
self.scroll
}
#[inline]
pub fn window_extent(&self) -> Extent {
self.window_extent
}
}