diff --git a/Hypercube.Client/ClientConstants.cs b/Hypercube.Client/ClientConstants.cs index 5a4c8f6..21a936a 100644 --- a/Hypercube.Client/ClientConstants.cs +++ b/Hypercube.Client/ClientConstants.cs @@ -3,4 +3,5 @@ public static class ClientConstants { public static bool MultiThreadingWindow = false; + } \ No newline at end of file diff --git a/Hypercube.Client/Graphics/Viewports/CameraManager.cs b/Hypercube.Client/Graphics/Viewports/CameraManager.cs index 03e58d2..ac5c37f 100644 --- a/Hypercube.Client/Graphics/Viewports/CameraManager.cs +++ b/Hypercube.Client/Graphics/Viewports/CameraManager.cs @@ -28,28 +28,28 @@ public void UpdateInput(ICamera? camera, float delta) var speed = 60f; - if (_inputHandler.IsKeyDown(Key.W)) + if (_inputHandler.IsKeyHeld(Key.W)) position += Vector3.UnitY * speed * delta; - if (_inputHandler.IsKeyDown(Key.S)) + if (_inputHandler.IsKeyHeld(Key.S)) position -= Vector3.UnitY * speed * delta; - if (_inputHandler.IsKeyDown(Key.A)) + if (_inputHandler.IsKeyHeld(Key.A)) position -= Vector3.UnitX * speed * delta; - if (_inputHandler.IsKeyDown(Key.D)) + if (_inputHandler.IsKeyHeld(Key.D)) position += Vector3.UnitX * speed * delta; - if (_inputHandler.IsKeyDown(Key.Q)) + if (_inputHandler.IsKeyHeld(Key.Q)) rotation -= Vector3.UnitZ * delta; - if (_inputHandler.IsKeyDown(Key.E)) + if (_inputHandler.IsKeyHeld(Key.E)) rotation += Vector3.UnitZ * delta; - if (_inputHandler.IsKeyDown(Key.T)) + if (_inputHandler.IsKeyHeld(Key.T)) scale -= Vector3.One * delta; - if (_inputHandler.IsKeyDown(Key.Y)) + if (_inputHandler.IsKeyHeld(Key.Y)) scale += Vector3.One * delta; camera.SetPosition(position); diff --git a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Callbacks.cs b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Callbacks.cs index 5c72ff0..7dc8144 100644 --- a/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Callbacks.cs +++ b/Hypercube.Client/Graphics/Windows/Realisation/GLFW/GlfwWindowManager.Callbacks.cs @@ -55,12 +55,12 @@ private void OnErrorHandled(ErrorCode error, string description) private void OnWindowKeyHandled(Window* window, Keys glfwKey, int scanCode, InputAction action, GlfwKeyModifiers mods) { - RaiseInput(new WindowingKeyHandledEvent(new KeyStateChangedArgs( + RaiseInput(new WindowingKeyHandledEvent( (Key)glfwKey, Convert(action), (KeyModifiers)mods, scanCode - ))); + )); } private void OnWindowCharHandled(Window* window, uint codepoint) @@ -75,11 +75,11 @@ private void OnWindowScrollHandled(Window* window, double offsetX, double offset private void OnMouseButtonHandled(Window* window, GlfwMouseButton button, InputAction action, GlfwKeyModifiers mods) { - RaiseInput(new WindowingMouseButtonHandledEvent(new MouseButtonChangedArgs( + RaiseInput(new WindowingMouseButtonHandledEvent( (MouseButton)button, Convert(action), (KeyModifiers)mods - ))); + )); } private void RaiseInput(T args) where T : IEventArgs @@ -126,8 +126,8 @@ private static KeyState Convert(InputAction action) return action switch { InputAction.Release => KeyState.Release, - InputAction.Press => KeyState.Press, - InputAction.Repeat => KeyState.Repeat, + InputAction.Press => KeyState.Pressed, + InputAction.Repeat => KeyState.Held, _ => throw new ArgumentOutOfRangeException() }; } diff --git a/Hypercube.Client/Input/Events/KeyHandledEvent.cs b/Hypercube.Client/Input/Events/KeyHandledEvent.cs new file mode 100644 index 0000000..ede0a73 --- /dev/null +++ b/Hypercube.Client/Input/Events/KeyHandledEvent.cs @@ -0,0 +1,11 @@ +using Hypercube.Input; +using Hypercube.Shared.EventBus.Events; + +namespace Hypercube.Client.Input.Events; + +public sealed class KeyHandledEvent : KeyStateChangedArgs, IEventArgs +{ + public KeyHandledEvent(Key key, KeyState state, KeyModifiers modifiers, int scanCode) : base(key, state, modifiers, scanCode) + { + } +} \ No newline at end of file diff --git a/Hypercube.Client/Input/Events/MouseButtonHandledEvent.cs b/Hypercube.Client/Input/Events/MouseButtonHandledEvent.cs new file mode 100644 index 0000000..d94375f --- /dev/null +++ b/Hypercube.Client/Input/Events/MouseButtonHandledEvent.cs @@ -0,0 +1,11 @@ +using Hypercube.Input; +using Hypercube.Shared.EventBus.Events; + +namespace Hypercube.Client.Input.Events; + +public class MouseButtonHandledEvent : MouseButtonChangedArgs, IEventArgs +{ + public MouseButtonHandledEvent(MouseButton button, KeyState state, KeyModifiers modifiers) : base(button, state, modifiers) + { + } +} \ No newline at end of file diff --git a/Hypercube.Client/Input/Events/Windowing/WindowingKeyHandledEvent.cs b/Hypercube.Client/Input/Events/Windowing/WindowingKeyHandledEvent.cs index 68f88fc..223c99b 100644 --- a/Hypercube.Client/Input/Events/Windowing/WindowingKeyHandledEvent.cs +++ b/Hypercube.Client/Input/Events/Windowing/WindowingKeyHandledEvent.cs @@ -3,4 +3,9 @@ namespace Hypercube.Client.Input.Events.Windowing; -public readonly record struct WindowingKeyHandledEvent(KeyStateChangedArgs State) : IEventArgs; \ No newline at end of file +public sealed class WindowingKeyHandledEvent : KeyStateChangedArgs, IEventArgs +{ + public WindowingKeyHandledEvent(Key key, KeyState state, KeyModifiers modifiers, int scanCode) : base(key, state, modifiers, scanCode) + { + } +} \ No newline at end of file diff --git a/Hypercube.Client/Input/Events/Windowing/WindowingMouseButtonHandledEvent.cs b/Hypercube.Client/Input/Events/Windowing/WindowingMouseButtonHandledEvent.cs index 26198e4..dae7373 100644 --- a/Hypercube.Client/Input/Events/Windowing/WindowingMouseButtonHandledEvent.cs +++ b/Hypercube.Client/Input/Events/Windowing/WindowingMouseButtonHandledEvent.cs @@ -3,4 +3,9 @@ namespace Hypercube.Client.Input.Events.Windowing; -public readonly record struct WindowingMouseButtonHandledEvent(MouseButtonChangedArgs State) : IEventArgs; \ No newline at end of file +public class WindowingMouseButtonHandledEvent : MouseButtonChangedArgs, IEventArgs +{ + public WindowingMouseButtonHandledEvent(MouseButton button, KeyState state, KeyModifiers modifiers) : base(button, state, modifiers) + { + } +} \ No newline at end of file diff --git a/Hypercube.Client/Input/Handler/IInputHandler.cs b/Hypercube.Client/Input/Handler/IInputHandler.cs index ea6007f..0a20480 100644 --- a/Hypercube.Client/Input/Handler/IInputHandler.cs +++ b/Hypercube.Client/Input/Handler/IInputHandler.cs @@ -10,5 +10,15 @@ namespace Hypercube.Client.Input.Handler; /// public interface IInputHandler : IEventSubscriber { - bool IsKeyDown(Key key); + bool IsKeyState(Key key, KeyState state); + bool IsKeyHeld(Key key); + bool IsKeyPressed(Key key); + bool IsKeyReleased(Key key); + void KeyClear(); + + bool IsMouseButtonState(MouseButton button, KeyState state); + bool IsMouseButtonHeld(MouseButton button); + bool IsMouseButtonPressed(MouseButton button); + bool IsMouseButtonReleased(MouseButton button); + void MouseButtonClear(); } \ No newline at end of file diff --git a/Hypercube.Client/Input/Handler/InputHandler.cs b/Hypercube.Client/Input/Handler/InputHandler.cs index 38f0573..2a57b4a 100644 --- a/Hypercube.Client/Input/Handler/InputHandler.cs +++ b/Hypercube.Client/Input/Handler/InputHandler.cs @@ -1,32 +1,68 @@ -using Hypercube.Client.Input.Events.Windowing; +using System.Collections.Frozen; +using Hypercube.Client.Input.Events; +using Hypercube.Client.Input.Events.Windowing; using Hypercube.Input; using Hypercube.Shared.Dependency; using Hypercube.Shared.EventBus; using Hypercube.Shared.Logging; +using Hypercube.Shared.Runtimes.Loop.Event; namespace Hypercube.Client.Input.Handler; public sealed class InputHandler : IInputHandler, IPostInject { [Dependency] private readonly IEventBus _eventBus = default!; - - private readonly HashSet _keysRelease = []; - private readonly HashSet _keysPressed = []; - private readonly HashSet _keysDown = []; + + private readonly FrozenDictionary> _keys = new Dictionary> + { + { KeyState.Held, [] }, + { KeyState.Release, [] }, + { KeyState.Pressed, [] }, + }.ToFrozenDictionary(); + + private readonly FrozenDictionary> _mouseButtons = new Dictionary> + { + { KeyState.Held, [] }, + { KeyState.Release, [] }, + { KeyState.Pressed, [] }, + }.ToFrozenDictionary(); + private readonly Logger _logger = LoggingManager.GetLogger("input_handler"); public void PostInject() { + _eventBus.Subscribe(this, OnInputFrameUpdate); + _eventBus.Subscribe(this, OnCharHandled); _eventBus.Subscribe(this, OnKeyHandled); _eventBus.Subscribe(this, OnMouseButtonHandled); _eventBus.Subscribe(this, OnScrollHandled); } + private void OnInputFrameUpdate(ref InputFrameEvent args) + { + _keys[KeyState.Pressed].Clear(); + _keys[KeyState.Release].Clear(); + + foreach (var key in _keys[KeyState.Held]) + { + // Held event don't support modifiers yet, also scanCode + _eventBus.Raise(new KeyHandledEvent(key, KeyState.Held, KeyModifiers.None, 0)); + } + + _mouseButtons[KeyState.Pressed].Clear(); + _mouseButtons[KeyState.Release].Clear(); + + foreach (var mouseButton in _mouseButtons[KeyState.Held]) + { + // Held event don't support modifiers yet + _eventBus.Raise(new MouseButtonHandledEvent(mouseButton, KeyState.Held, KeyModifiers.None)); + } + } + private void OnCharHandled(ref WindowingCharHandledEvent args) { - throw new NotImplementedException(); } private void OnKeyHandled(ref WindowingKeyHandledEvent args) @@ -34,32 +70,128 @@ private void OnKeyHandled(ref WindowingKeyHandledEvent args) #if DEBUG // Use only in Debug build, // as this check can take quite a lot of performance during input processing - if (!Enum.IsDefined(typeof(Key), args.State.Key)) + if (!Enum.IsDefined(typeof(Key), args.Key)) { - _logger.Warning($"Unknown key {args.State.Key} handled"); + _logger.Warning($"Unknown {args.Key} handled"); return; } #endif - if (args.State == KeyState.Press) + switch (args.State) { - _keysDown.Add(args.State.Key); - return; + case KeyState.Held: + return; + + // Legacy shit, maybe will eat many ram and cpu + // We made many shit because fucking Key rollover: https://en.wikipedia.org/wiki/Key_rollover + case KeyState.Pressed: + _keys[KeyState.Held].Add(args.Key); + _keys[KeyState.Pressed].Add(args.Key); + break; + + case KeyState.Release: + _keys[KeyState.Held].Remove(args.Key); + _keys[KeyState.Pressed].Add(args.Key); + break; + + default: + throw new ArgumentOutOfRangeException(); } - - _keysDown.Remove(args.State.Key); + + _eventBus.Raise(new KeyHandledEvent(args.Key, args.State, args.Modifiers, args.ScanCode)); } private void OnMouseButtonHandled(ref WindowingMouseButtonHandledEvent args) { +#if DEBUG + // Use only in Debug build, + // as this check can take quite a lot of performance during input processing + if (!Enum.IsDefined(typeof(MouseButton), args.Button)) + { + _logger.Warning($"Unknown {args.Button} handled"); + return; + } +#endif + + switch (args.State) + { + case KeyState.Held: + return; + + case KeyState.Pressed: + _mouseButtons[KeyState.Held].Add(args.Button); + _mouseButtons[KeyState.Pressed].Add(args.Button); + break; + + case KeyState.Release: + _mouseButtons[KeyState.Held].Remove(args.Button); + _mouseButtons[KeyState.Pressed].Add(args.Button); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + _eventBus.Raise(new MouseButtonHandledEvent(args.Button, args.State, args.Modifiers)); } private void OnScrollHandled(ref WindowingScrollHandledEvent args) { } - public bool IsKeyDown(Key key) + public bool IsKeyState(Key key, KeyState state) { - return _keysDown.Contains(key); + return _keys[state].Contains(key); + } + + public bool IsKeyHeld(Key key) + { + return IsKeyState(key, KeyState.Held); + } + + public bool IsKeyPressed(Key key) + { + return IsKeyState(key, KeyState.Pressed); + } + + public bool IsKeyReleased(Key key) + { + return IsKeyState(key, KeyState.Pressed); + } + + public void KeyClear() + { + foreach (var (_, key) in _keys) + { + key.Clear(); + } + } + + public bool IsMouseButtonState(MouseButton button, KeyState state) + { + return _mouseButtons[state].Contains(button); + } + + public bool IsMouseButtonHeld(MouseButton button) + { + return IsMouseButtonState(button, KeyState.Held); + } + + public bool IsMouseButtonPressed(MouseButton button) + { + return IsMouseButtonState(button, KeyState.Pressed); + } + + public bool IsMouseButtonReleased(MouseButton button) + { + return IsMouseButtonState(button, KeyState.Release); + } + + public void MouseButtonClear() + { + foreach (var (_, mouseButtons) in _mouseButtons) + { + mouseButtons.Clear(); + } } } \ No newline at end of file diff --git a/Hypercube.Example.Client/Controls/ControlsSystem.cs b/Hypercube.Example.Client/Controls/ControlsSystem.cs index db7d64a..adab2e2 100644 --- a/Hypercube.Example.Client/Controls/ControlsSystem.cs +++ b/Hypercube.Example.Client/Controls/ControlsSystem.cs @@ -17,8 +17,8 @@ public override void FrameUpdate(UpdateFrameEvent args) { base.FrameUpdate(args); - var inputX = (_inputHandler.IsKeyDown(Key.D) ? 1 : 0) - (_inputHandler.IsKeyDown(Key.A) ? 1 : 0); - var inputY = (_inputHandler.IsKeyDown(Key.W) ? 1 : 0) - (_inputHandler.IsKeyDown(Key.S) ? 1 : 0); + var inputX = (_inputHandler.IsKeyHeld(Key.D) ? 1 : 0) - (_inputHandler.IsKeyHeld(Key.A) ? 1 : 0); + var inputY = (_inputHandler.IsKeyHeld(Key.W) ? 1 : 0) - (_inputHandler.IsKeyHeld(Key.S) ? 1 : 0); foreach (var entity in GetEntities()) { diff --git a/Hypercube.Input/KeyState.cs b/Hypercube.Input/KeyState.cs index e110596..11d2905 100644 --- a/Hypercube.Input/KeyState.cs +++ b/Hypercube.Input/KeyState.cs @@ -3,6 +3,6 @@ public enum KeyState { Release, - Press, - Repeat + Pressed, + Held } \ No newline at end of file diff --git a/Hypercube.Input/KeyStateChangedArgs.cs b/Hypercube.Input/KeyStateChangedArgs.cs index a390194..ff6ca14 100644 --- a/Hypercube.Input/KeyStateChangedArgs.cs +++ b/Hypercube.Input/KeyStateChangedArgs.cs @@ -1,6 +1,6 @@ namespace Hypercube.Input; -public readonly struct KeyStateChangedArgs +public class KeyStateChangedArgs { public bool Shift => Modifiers.HasFlag(KeyModifiers.Shift); public bool Control => Modifiers.HasFlag(KeyModifiers.Control); diff --git a/Hypercube.Input/MouseButtonChangedArgs.cs b/Hypercube.Input/MouseButtonChangedArgs.cs index d59e2e3..855764d 100644 --- a/Hypercube.Input/MouseButtonChangedArgs.cs +++ b/Hypercube.Input/MouseButtonChangedArgs.cs @@ -1,6 +1,6 @@ namespace Hypercube.Input; -public readonly struct MouseButtonChangedArgs +public class MouseButtonChangedArgs { public bool Shift => Modifiers.HasFlag(KeyModifiers.Shift); public bool Control => Modifiers.HasFlag(KeyModifiers.Control);