using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using Ninject; namespace V3.Input.Internal { /// /// Watches the state of the mouse and keyboard and creates events if a /// change (key pressed or released) is detected. To use this class, call /// Update in every update round. After the update, you may access the /// generated events in KeyEvents and MouseEvents. The input manager only /// watches the mouse buttons and keys listed in sWatchedKeys and /// sWatchedButtons. /// // ReSharper disable once ClassNeverInstantiated.Global internal sealed class InputManager : IInputManager, IInitializable { /// /// The key events that were generated during the last update. Reset /// in the next update. /// public ICollection KeyEvents { get; } = new HashSet(); /// /// The mouse events that were generated during the last update. Reset in /// the next update. /// public ICollection MouseEvents { get; } = new HashSet(); private static readonly ICollection sWatchedKeys = new List { Keys.Enter, Keys.Escape, Keys.E, Keys.L, Keys.Q, Keys.S, Keys.F1, Keys.F2 , Keys.F3, Keys.F4, Keys.F5, Keys.F6, Keys.F7, Keys.F8 }; private static readonly ICollection sWatchedButtons = new List { MouseButton.Left, MouseButton.Right, MouseButton.Middle }; private readonly GraphicsDeviceManager mGraphicsDeviceManager; private readonly IDictionary mKeyStates = new Dictionary(); private readonly IDictionary mButtonStates = new Dictionary(); private readonly IDictionary mButtonPositions = new Dictionary(); /// /// Creates a new input manager. /// public InputManager(GraphicsDeviceManager graphicsDeviceManager) { mGraphicsDeviceManager = graphicsDeviceManager; } public void Initialize() { foreach (var key in sWatchedKeys) mKeyStates.Add(key, KeyState.Up); foreach (var button in sWatchedButtons) { mButtonStates.Add(button, ButtonState.Released); mButtonPositions.Add(button, null); } } /// /// Updates the keyboard and mouse status and generates the key and mouse /// events in KeyEvents and MouseEvents if changes were detected. Should /// be called once every update, before doing something else. /// public void Update() { UpdateKeyboard(); UpdateMouse(); } private void UpdateKeyboard() { KeyEvents.Clear(); var state = Keyboard.GetState(); foreach (var key in sWatchedKeys) { var newState = state[key]; if (newState != mKeyStates[key]) { mKeyStates[key] = newState; KeyEvents.Add(new KeyEvent(key, newState)); } } } private void UpdateMouse() { MouseEvents.Clear(); var state = Mouse.GetState(); foreach (var button in sWatchedButtons) { var newState = GetButtonState(state, button); if (newState != mButtonStates[button]) { var position = new Point(state.X, state.Y); var positionPressed = position; Point? positionReleased = null; if (newState == ButtonState.Released) { if (mButtonPositions[button].HasValue) positionPressed = mButtonPositions[button].Value; positionReleased = position; } mButtonStates[button] = newState; mButtonPositions[button] = position; var releasedOnScreen = false; if (positionReleased.HasValue) releasedOnScreen = IsPointOnScreen(positionReleased.Value); MouseEvents.Add(new MouseEvent(button, newState, positionPressed, positionReleased, releasedOnScreen)); } } } private bool IsPointOnScreen(Point point) { var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport; return point.X >= 0 && point.X <= viewport.Width && point.Y >= 0 && point.Y <= viewport.Height; } private static ButtonState GetButtonState(MouseState state, MouseButton button) { switch (button) { case MouseButton.Left: return state.LeftButton; case MouseButton.Right: return state.RightButton; case MouseButton.Middle: return state.MiddleButton; default: return state.LeftButton; } } } }