From 182ebfc31c37c0759b5a41c1921273f1ba55b759 Mon Sep 17 00:00:00 2001 From: Perry Date: Thu, 15 Jan 2026 19:49:49 +0100 Subject: [PATCH] Added support for mouse input. Removed redundant keyboard state paramater for KeypressHandler, replaced with static field updated every frame. --- MonoGameLibrary/Input/InputManager.cs | 162 ++++++++++++++++++++++---- 1 file changed, 137 insertions(+), 25 deletions(-) diff --git a/MonoGameLibrary/Input/InputManager.cs b/MonoGameLibrary/Input/InputManager.cs index 533fa06..d69c0a4 100644 --- a/MonoGameLibrary/Input/InputManager.cs +++ b/MonoGameLibrary/Input/InputManager.cs @@ -1,23 +1,37 @@ using System; using System.Collections.Generic; +using System.Transactions; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace MonoGameLibrary.Input; public static class InputManager { - private static Dictionary> keyPressListeners = new(); - private static Dictionary> keyHoldListeners = new(); // The bool is for enabling/disabling that method - private static Dictionary> keyReleaseListeners = new(); + private static Dictionary> keyPressListeners = new(); + private static Dictionary> keyHoldListeners = new(); // The bool is for enabling/disabling that method + private static Dictionary> keyReleaseListeners = new(); private static Dictionary labelDict = new(); - private static KeyboardState oldState = Keyboard.GetState(); - private static KeyboardState newState; + private static KeyboardState oldKBState = Keyboard.GetState(); + private static KeyboardState newKBState; + public static KeyboardState KeyboardState => newKBState; + + + private static MouseState oldMouseState = Mouse.GetState(); + private static MouseState newMouseState; + public static MouseState MouseState => newMouseState; + + private static Dictionary> mousePressListeners = new(); + private static Dictionary> mouseHoldListeners = new(); + private static Dictionary> mouseReleaseListeners = new(); // public static ConcurrentQueue inputEventQueue = new(); - public delegate void KeypressHandler(KeyboardState state); + public delegate void KeypressHandler(); + + public delegate void MouseHandler(); private static int nextUnnamedId = 0; @@ -32,14 +46,14 @@ public static class InputManager { public static void NextInputCycle() { Queue inputEventQueue = new(); - // Read + // Read Keyboard - newState = Keyboard.GetState(); - Keys[] pressed = newState.GetPressedKeys(); - Keys[] pressedLastFrame = oldState.GetPressedKeys(); + newKBState = Keyboard.GetState(); + Keys[] pressed = newKBState.GetPressedKeys(); + Keys[] pressedLastFrame = oldKBState.GetPressedKeys(); Array.ForEach(pressed, key => { - if (oldState.IsKeyUp(key)){ + if (oldKBState.IsKeyUp(key)){ if (keyPressListeners.TryGetValue(key, out var pressHandlers)){ pressHandlers.ForEach(t => { if (t.Enabled){ @@ -69,7 +83,7 @@ public static class InputManager { return; } - if (newState.IsKeyUp(key)){ + if (newKBState.IsKeyUp(key)){ if (keyReleaseListeners.TryGetValue(key, out var releaseHandlers)) releaseHandlers.ForEach(t => { if (t.Enabled){ @@ -81,12 +95,47 @@ public static class InputManager { }); } }); - oldState = newState; + oldKBState = newKBState; + + // Read Mouse + + newMouseState = Mouse.GetState(); + + List mouseHandlers = new List(); + + foreach (var button in ((MouseButton type, ButtonState current, ButtonState old)[])[ + (MouseButton.LEFT, newMouseState.LeftButton, oldMouseState.LeftButton), + (MouseButton.RIGHT, newMouseState.RightButton, oldMouseState.RightButton), + (MouseButton.MIDDLE, newMouseState.MiddleButton, oldMouseState.MiddleButton) + ]) + { + if (button.current == ButtonState.Pressed){ + if (button.old == ButtonState.Released){ + mousePressListeners.TryGetValue(button.type, out var pressHandlers); + mouseHandlers.AddRange(pressHandlers ?? new List()); + } + mouseHoldListeners.TryGetValue(button.type, out var holdHandlers); + mouseHandlers.AddRange(holdHandlers ?? new List()); + } + else if (button.old == ButtonState.Pressed){ + if (button.current == ButtonState.Released){ + mouseReleaseListeners.TryGetValue(button.type, out var releaseHandlers); + mouseHandlers.AddRange(releaseHandlers ?? new List()); + } + } + } + + mouseHandlers.ForEach(entry => inputEventQueue.Enqueue(entry.Action)); + oldMouseState = newMouseState; + + + + // Execute foreach (KeypressHandler handler in inputEventQueue){ - handler(newState); + handler(); } } @@ -98,17 +147,31 @@ public static class InputManager { if (label.StartsWith("!")){ throw new ArgumentException("Label cannot start with !"); } - InputEntry entry = new (label, key, action, timing, hook); - _addListener(entry); + KeyboardInputEntry entry = new (label, key, action, timing, hook); + _addListenerKB(entry); } public static void AddListener(Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) { - InputEntry entry = new ($"!{Enum.GetName(typeof(Keys), key)}Handler{nextUnnamedId++}", key, action, timing, hook); - _addListener(entry); + KeyboardInputEntry entry = new ($"!{Enum.GetName(typeof(Keys), key)}Handler{nextUnnamedId++}", key, action, timing, hook); + _addListenerKB(entry); } - private static void _addListener(InputEntry entry) { - Dictionary> workingDict; + public static void AddListener(string label, MouseButton button, KeypressHandler action, InputTiming timing, InputListenerHook hook) { + if (label.StartsWith("!")){ + throw new ArgumentException("Label cannot start with !"); + } + MouseInputEntry entry = new (label, button, action, timing, hook); + _addListenerMouse(entry); + } + + public static void AddListener(MouseButton button, KeypressHandler action, InputTiming timing, + InputListenerHook hook) { + MouseInputEntry entry = new ($"!{Enum.GetName(typeof(MouseButton), button)}Handler{nextUnnamedId++}", button, action, timing, hook); + _addListenerMouse(entry); + } + + private static void _addListenerKB(KeyboardInputEntry entry) { + Dictionary> workingDict; switch (entry.Timing){ case InputTiming.PRESS: @@ -133,27 +196,76 @@ public static class InputManager { labelDict.Add(entry.Label, entry); } + private static void _addListenerMouse(MouseInputEntry entry) { + Dictionary> workingDict; + + switch (entry.Timing){ + case InputTiming.PRESS: + workingDict = mousePressListeners; + break; + case InputTiming.HOLD: + workingDict = mouseHoldListeners; + break; + case InputTiming.RELEASE: + workingDict = mouseReleaseListeners; + break; + default: + throw new ArgumentOutOfRangeException(nameof(entry.Timing), entry.Timing, null); + } + + if (!workingDict.ContainsKey(entry.Button)){ + workingDict.Add(entry.Button, new()); + } + + workingDict[entry.Button].Add(entry); + + labelDict.Add(entry.Label, entry); + } + + public static InputEntry GetEntry(string label) { return labelDict[label]; } - public class InputEntry { - + public abstract class InputEntry { public string Label; - public Keys Key; + public KeypressHandler Action; public InputTiming Timing; public InputListenerHook Hook; public bool Enabled => Hook.Enabled; - public InputEntry(string label, Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) { - Key = key; + public InputEntry(string label, KeypressHandler action, InputTiming timing, InputListenerHook hook) { Action = action; Timing = timing; Hook = hook; Label = label; } } + + public class KeyboardInputEntry : InputEntry { + public Keys Key; + + public KeyboardInputEntry(string label, Keys key, KeypressHandler action, InputTiming timing, InputListenerHook hook) : base(label, action, timing, hook) { + Key = key; + } + } + + public class MouseInputEntry : InputEntry { + public MouseButton Button; + + public MouseInputEntry(string label, MouseButton button, KeypressHandler action, InputTiming timing, InputListenerHook hook) : base(label, action, timing, hook) { + Button = button; + } + } + + public enum MouseButton { + RIGHT, + LEFT, + MIDDLE + } + + // private static List inputQueue = new(); } \ No newline at end of file