organicomplex 0.7.0

Interactive complex-valued cellular automaton on 2D and 3D grids in search of that stuff - emergence, open-endedness, organicity etc.
use std::collections::HashSet;

use sdl2::event::Event;

mod buttons;
mod keys;

pub use self::{
    buttons::Button,
    keys::Key
};

use super::config::Config;

pub struct System {
    mouse_sensitivity: i32,
    left_hand: bool,

    event_pump: sdl2::EventPump,

    keys_buffer: HashSet<Key>,
    buttons_buffer: HashSet<Button>,

    mouse_dx: i32,
    mouse_dy: i32,
    mouse_dw: i32 // wheel delta
}

impl System {
    pub fn init(sdl: &sdl2::Sdl, config: &Config) -> Result<System, String> {
        sdl.mouse().show_cursor(false); // after window building
        sdl.mouse().set_relative_mouse_mode(true);

        let event_pump = sdl.event_pump()?;

        let keys_buffer: HashSet<Key> = HashSet::new();
        let buttons_buffer: HashSet<Button> = HashSet::new();

        Ok(System{
            mouse_sensitivity: config.mouse_sensitivity,
            left_hand: config.left_hand,
            event_pump,
            keys_buffer,
            buttons_buffer,
            mouse_dx: 0,
            mouse_dy: 0,
            mouse_dw: 0
        })
    }

    pub fn mouse_dx(&self) -> i32 {
        self.mouse_dx * self.mouse_sensitivity / 100
    }

    pub fn mouse_dy(&self) -> i32 {
        self.mouse_dy * self.mouse_sensitivity / 100
    }

    pub fn mouse_dw(&self) -> i32 {
        self.mouse_dw
    }

    pub fn poll_mouse_dx(&mut self) -> i32 {
        let dx = self.mouse_dx();
        self.mouse_dx = 0;
        dx
    }

    pub fn poll_mouse_dy(&mut self) -> i32 {
        let dy = self.mouse_dy();
        self.mouse_dy = 0;
        dy
    }

    pub fn poll_mouse_dw(&mut self) -> i32 {
        let dw = self.mouse_dw();
        self.mouse_dw = 0;
        dw
    }

    pub fn test_key(&self, key: Key) -> bool {
        match keys::to_sdl(Some(key)) {
            Some(s) => self.event_pump.keyboard_state().is_scancode_pressed(s),
            None => false
        }
    }

    pub fn test_keys(&self, keys: &[Key]) -> bool {
        for k in keys {
            if self.test_key(*k) {
                return true
            }
        }
        false
    }

    pub fn test_modkeys(&self) -> bool {
        self.test_keys(&[Key::LAlt, Key::RAlt, Key::LCtrl, Key::RCtrl, Key::LShift, Key::RShift])
    }

    pub fn poll_key(&mut self, key: Key) -> bool {
        match self.keys_buffer.take(&key) { // removes from buffer
            Some(_) => true,
            None => false
        }
    }

    pub fn poll_keys(&mut self, keys: &[Key]) -> bool {
        keys.iter().fold(false, |any_pressed, this_pressed| any_pressed | self.poll_key(*this_pressed)) // removes from buffer
    }

    pub fn get_poll_char(&mut self) -> Option<char> {
        let mut char_key: Option<Key> = None;
        // Find first non-Shift pressed key
        for key in & self.keys_buffer {
            if ![Key::LShift, Key::RShift].contains(key) {
                char_key = Some(*key);
                break;
            }
        }
        if let Some(ch_key) = char_key {
            let shift = self.test_keys(&[Key::LShift, Key::RShift]);
            let ch = if ((ch_key as usize) >= (Key::A as usize)) && ((ch_key as usize) <= (Key::Z as usize)) {
                let mut ascii = (0x61 + (ch_key as usize) - (Key::A as usize)) as u8;
                if shift { ascii -= 0x20; }
                ascii as char
            } else {
                match ch_key { // how can it be shortened?..
                    Key::Space => ' ',
                    Key::Num0 => match shift { false => '0', true => ')' },
                    Key::Num1 => match shift { false => '1', true => '!' },
                    Key::Num2 => match shift { false => '2', true => '@' },
                    Key::Num3 => match shift { false => '3', true => '#' },
                    Key::Num4 => match shift { false => '4', true => '$' },
                    Key::Num5 => match shift { false => '5', true => '%' },
                    Key::Num6 => match shift { false => '6', true => '^' },
                    Key::Num7 => match shift { false => '7', true => '&' },
                    Key::Num8 => match shift { false => '8', true => '*' },
                    Key::Num9 => match shift { false => '9', true => '(' },
                    Key::Minus => match shift {false => '-', true => '_'},
                    Key::Equals => match shift {false => '=', true => '+'},
                    Key::LeftBracket => match shift {false => '[', true => '{'},
                    Key::RightBracket => match shift {false => ']', true => '}'},
                    Key::Backslash => match shift {false => '\\', true => '|'},
                    Key::Semicolon => match shift {false => ';', true => ':'},
                    Key::Apostrophe => match shift {false => '\'', true => '"'},
                    Key::Grave => match shift {false => '`', true => '~'},
                    Key::Comma => match shift {false => ',', true => '<'},
                    Key::Period => match shift {false => '.', true => '>'},
                    Key::Slash => match shift {false => '/', true => '?'},
                    Key::KpDivide => '/',
                    Key::KpMultiply => '*',
                    Key::KpMinus => '-',
                    Key::KpPlus => '+',

                    _ => 0u8 as char
                }
            };
            if ch as u32 != 0 {
                self.keys_buffer.remove(&ch_key);
                Some(ch)
            } else { // do not "swallow" keys that may be processed not as chars
                None
            }
        } else {
            None
        }
    }

    fn swap_if_lefthand(&self, btn: Button) -> Button {
        if self.left_hand {
            match btn {
                Button::Left => Button::Right,
                Button::Right => Button::Left,
                _ => btn
            }
        } else {
            btn
        }
    }

    pub fn test_btn(&self, btn: Button) -> bool {
        match buttons::to_sdl(Some(self.swap_if_lefthand(btn))) {
            Some(b) => self.event_pump.relative_mouse_state().is_mouse_button_pressed(b),
            None => false
        }
    }

    pub fn poll_btn(&mut self, btn: Button) -> bool {
        match self.buttons_buffer.take(&(self.swap_if_lefthand(btn))) { // removes from buffer
            Some(_) => true,
            None => false
        }
    }

    fn clear(&mut self) {
        self.keys_buffer.clear();
        self.buttons_buffer.clear();
        self.mouse_dx = 0;
        self.mouse_dy = 0;
        self.mouse_dw = 0;
    }

    pub fn sync(&mut self) -> Result<(), String> {
        self.clear();

        for event in self.event_pump.poll_iter() {
            match event {
                Event::KeyDown{scancode, ..} => {
                    match keys::from_sdl(scancode) {
                        Some(k) => {self.keys_buffer.insert(k);},
                        None => {}
                    }
                },
                Event::MouseButtonDown{mouse_btn, ..} => {
                    match buttons::from_sdl(Some(mouse_btn)) {
                        Some(b) => {self.buttons_buffer.insert(b);},
                        None => {}
                    }
                },
                Event::MouseWheel{y, ..} => {
                    self.mouse_dw += y;
                },
                Event::MouseMotion{xrel, yrel, ..} => {
                    self.mouse_dx += xrel;
                    self.mouse_dy += yrel;
                },

                _ => {}
            }
        }

        Ok(())
    }

    pub fn shut(&mut self) -> Result<(), String> {
        Ok(())
    }
}