summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Input')
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/ExtendedPlayerIndex.cs32
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadEventArgs.cs62
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListener.cs529
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListenerSettings.cs134
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/IInputService.cs13
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListener.cs13
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs37
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerSettings.cs8
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardEventArgs.cs119
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListener.cs104
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListenerSettings.cs21
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardModifiers.cs13
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseEventArgs.cs35
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListener.cs193
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListenerSettings.cs23
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchEventArgs.cs39
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListener.cs57
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListenerSettings.cs18
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardExtended.cs22
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardStateExtended.cs60
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj12
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseButton.cs15
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseExtended.cs34
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseStateExtended.cs149
24 files changed, 1742 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/ExtendedPlayerIndex.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/ExtendedPlayerIndex.cs
new file mode 100644
index 0000000..a153f02
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/ExtendedPlayerIndex.cs
@@ -0,0 +1,32 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Input
+{
+ /// <summary>Player index enumeration with slots for 8 players</summary>
+ public enum ExtendedPlayerIndex
+ {
+ /// <summary>First player</summary>
+ One = PlayerIndex.One,
+
+ /// <summary>Second player</summary>
+ Two = PlayerIndex.Two,
+
+ /// <summary>Third player</summary>
+ Three = PlayerIndex.Three,
+
+ /// <summary>Fourth player</summary>
+ Four = PlayerIndex.Four,
+
+ /// <summary>Fifth player</summary>
+ Five,
+
+ /// <summary>Sixth player</summary>
+ Six,
+
+ /// <summary>Seventh player</summary>
+ Seven,
+
+ /// <summary>Eigth player</summary>
+ Eight
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadEventArgs.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadEventArgs.cs
new file mode 100644
index 0000000..9b113c6
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadEventArgs.cs
@@ -0,0 +1,62 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ /// <summary>
+ /// This class contains all information resulting from events fired by
+ /// <see cref="GamePadListener" />.
+ /// </summary>
+ public class GamePadEventArgs : EventArgs
+ {
+ public GamePadEventArgs(GamePadState previousState, GamePadState currentState,
+ TimeSpan elapsedTime, PlayerIndex playerIndex, Buttons? button = null,
+ float triggerState = 0, Vector2? thumbStickState = null)
+ {
+ PlayerIndex = playerIndex;
+ PreviousState = previousState;
+ CurrentState = currentState;
+ ElapsedTime = elapsedTime;
+ if (button != null)
+ Button = button.Value;
+ TriggerState = triggerState;
+ ThumbStickState = thumbStickState ?? Vector2.Zero;
+ }
+
+ /// <summary>
+ /// The index of the controller.
+ /// </summary>
+ public PlayerIndex PlayerIndex { get; private set; }
+
+ /// <summary>
+ /// The state of the controller in the previous update.
+ /// </summary>
+ public GamePadState PreviousState { get; private set; }
+
+ /// <summary>
+ /// The state of the controller in this update.
+ /// </summary>
+ public GamePadState CurrentState { get; private set; }
+
+ /// <summary>
+ /// The button that triggered this event, if appliable.
+ /// </summary>
+ public Buttons Button { get; private set; }
+
+ /// <summary>
+ /// The time elapsed since last event.
+ /// </summary>
+ public TimeSpan ElapsedTime { get; private set; }
+
+ /// <summary>
+ /// If a TriggerMoved event, displays the responsible trigger's position.
+ /// </summary>
+ public float TriggerState { get; private set; }
+
+ /// <summary>
+ /// If a ThumbStickMoved event, displays the responsible stick's position.
+ /// </summary>
+ public Vector2 ThumbStickState { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListener.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListener.cs
new file mode 100644
index 0000000..b7ea79b
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListener.cs
@@ -0,0 +1,529 @@
+using System;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ /// <summary>
+ /// This is a listener that exposes several events for easier handling of gamepads.
+ /// </summary>
+ public class GamePadListener : InputListener
+ {
+ private static readonly bool[] _gamePadConnections = new bool[4];
+
+ // These buttons are not to be evaluated normally, but with the debounce filter
+ // in their respective methods.
+ private readonly Buttons[] _excludedButtons =
+ {
+ Buttons.LeftTrigger, Buttons.RightTrigger,
+ Buttons.LeftThumbstickDown, Buttons.LeftThumbstickUp, Buttons.LeftThumbstickRight,
+ Buttons.LeftThumbstickLeft,
+ Buttons.RightThumbstickLeft, Buttons.RightThumbstickRight, Buttons.RightThumbstickUp,
+ Buttons.RightThumbstickDown
+ };
+
+ private GamePadState _currentState;
+ //private int _lastPacketNumber;
+ // Implementation doesn't work, see explanation in CheckAllButtons().
+ private GameTime _gameTime;
+ private Buttons _lastButton;
+ private Buttons _lastLeftStickDirection;
+ private Buttons _lastRightStickDirection;
+ private GamePadState _lastThumbStickState;
+
+ private GamePadState _lastTriggerState;
+
+ private float _leftCurVibrationStrength;
+ private bool _leftStickDown;
+ private bool _leftTriggerDown;
+ private bool _leftVibrating;
+ private GameTime _previousGameTime;
+ private GamePadState _previousState;
+ private int _repeatedButtonTimer;
+ private float _rightCurVibrationStrength;
+ private bool _rightStickDown;
+ private bool _rightTriggerDown;
+ private bool _rightVibrating;
+ private TimeSpan _vibrationDurationLeft;
+ private TimeSpan _vibrationDurationRight;
+ private TimeSpan _vibrationStart;
+
+ private float _vibrationStrengthLeft;
+ private float _vibrationStrengthRight;
+
+ public GamePadListener()
+ : this(new GamePadListenerSettings())
+ {
+ }
+
+ public GamePadListener(GamePadListenerSettings settings)
+ {
+ PlayerIndex = settings.PlayerIndex;
+ VibrationEnabled = settings.VibrationEnabled;
+ VibrationStrengthLeft = settings.VibrationStrengthLeft;
+ VibrationStrengthRight = settings.VibrationStrengthRight;
+ ThumbStickDeltaTreshold = settings.ThumbStickDeltaTreshold;
+ ThumbstickDownTreshold = settings.ThumbstickDownTreshold;
+ TriggerDeltaTreshold = settings.TriggerDeltaTreshold;
+ TriggerDownTreshold = settings.TriggerDownTreshold;
+ RepeatInitialDelay = settings.RepeatInitialDelay;
+ RepeatDelay = settings.RepeatDelay;
+
+ _previousGameTime = new GameTime();
+ _previousState = GamePadState.Default;
+ }
+
+ /// <summary>
+ /// If set to true, the static event <see cref="ControllerConnectionChanged" />
+ /// will fire when any controller changes in connectivity status.
+ /// <para>
+ /// This functionality requires that you have one actively updating
+ /// <see cref="InputListenerManager" />.
+ /// </para>
+ /// </summary>
+ public static bool CheckControllerConnections { get; set; }
+
+ /// <summary>
+ /// The index of the controller.
+ /// </summary>
+ public PlayerIndex PlayerIndex { get; }
+
+ /// <summary>
+ /// When a button is held down, the interval in which
+ /// ButtonRepeated fires. Value in milliseconds.
+ /// </summary>
+ public int RepeatDelay { get; }
+
+ /// <summary>
+ /// The amount of time a button has to be held down
+ /// in order to fire ButtonRepeated the first time.
+ /// Value in milliseconds.
+ /// </summary>
+ public int RepeatInitialDelay { get; }
+
+ /// <summary>
+ /// Whether vibration is enabled for this controller.
+ /// </summary>
+ public bool VibrationEnabled { get; set; }
+
+ /// <summary>
+ /// General setting for the strength of the left motor.
+ /// This motor has a slow, deep, powerful rumble.
+ /// <para>
+ /// This setting will modify all future vibrations
+ /// through this listener.
+ /// </para>
+ /// </summary>
+ public float VibrationStrengthLeft
+ {
+ get { return _vibrationStrengthLeft; }
+ // Clamp the value, just to be sure.
+ set { _vibrationStrengthLeft = MathHelper.Clamp(value, 0, 1); }
+ }
+
+ /// <summary>
+ /// General setting for the strength of the right motor.
+ /// This motor has a snappy, quick, high-pitched rumble.
+ /// <para>
+ /// This setting will modify all future vibrations
+ /// through this listener.
+ /// </para>
+ /// </summary>
+ public float VibrationStrengthRight
+ {
+ get { return _vibrationStrengthRight; }
+ // Clamp the value, just to be sure.
+ set { _vibrationStrengthRight = MathHelper.Clamp(value, 0, 1); }
+ }
+
+ /// <summary>
+ /// The treshold of movement that has to be met in order
+ /// for the listener to fire an event with the trigger's
+ /// updated position.
+ /// <para>
+ /// In essence this defines the event's
+ /// resolution.
+ /// </para>
+ /// At a value of 0 this will fire every time
+ /// the trigger's position is not 0f.
+ /// </summary>
+ public float TriggerDeltaTreshold { get; }
+
+ /// <summary>
+ /// The treshold of movement that has to be met in order
+ /// for the listener to fire an event with the thumbstick's
+ /// updated position.
+ /// <para>
+ /// In essence this defines the event's
+ /// resolution.
+ /// </para>
+ /// At a value of 0 this will fire every time
+ /// the thumbstick's position is not {x:0, y:0}.
+ /// </summary>
+ public float ThumbStickDeltaTreshold { get; }
+
+ /// <summary>
+ /// How deep the triggers have to be depressed in order to
+ /// register as a ButtonDown event.
+ /// </summary>
+ public float TriggerDownTreshold { get; }
+
+ /// <summary>
+ /// How deep the triggers have to be depressed in order to
+ /// register as a ButtonDown event.
+ /// </summary>
+ public float ThumbstickDownTreshold { get; }
+
+ /// <summary>
+ /// This event fires whenever a controller connects or disconnects.
+ /// <para>
+ /// In order
+ /// for it to work, the <see cref="CheckControllerConnections" /> property must
+ /// be set to true.
+ /// </para>
+ /// </summary>
+ public static event EventHandler<GamePadEventArgs> ControllerConnectionChanged;
+
+ /// <summary>
+ /// This event fires whenever a button changes from the Up
+ /// to the Down state.
+ /// </summary>
+ public event EventHandler<GamePadEventArgs> ButtonDown;
+
+ /// <summary>
+ /// This event fires whenever a button changes from the Down
+ /// to the Up state.
+ /// </summary>
+ public event EventHandler<GamePadEventArgs> ButtonUp;
+
+ /// <summary>
+ /// This event fires repeatedly whenever a button is held sufficiently
+ /// long. Use this for things like menu navigation.
+ /// </summary>
+ public event EventHandler<GamePadEventArgs> ButtonRepeated;
+
+ /// <summary>
+ /// This event fires whenever a thumbstick changes position.
+ /// <para>
+ /// The parameter governing the sensitivity of this functionality
+ /// is <see cref="GamePadListenerSettings.ThumbStickDeltaTreshold" />.
+ /// </para>
+ /// </summary>
+ public event EventHandler<GamePadEventArgs> ThumbStickMoved;
+
+ /// <summary>
+ /// This event fires whenever a trigger changes position.
+ /// <para>
+ /// The parameter governing the sensitivity of this functionality
+ /// is <see cref="GamePadListenerSettings.TriggerDeltaTreshold" />.
+ /// </para>
+ /// </summary>
+ public event EventHandler<GamePadEventArgs> TriggerMoved;
+
+
+ /// <summary>
+ /// Send a vibration command to the controller.
+ /// Returns true if the operation succeeded.
+ /// <para>
+ /// Motor values that are unset preserve
+ /// their current vibration strength and duration.
+ /// </para>
+ /// Note: Vibration currently only works on select platforms,
+ /// like Monogame.Windows.
+ /// </summary>
+ /// <param name="durationMs">Duration of the vibration in milliseconds.</param>
+ /// <param name="leftStrength">
+ /// The strength of the left motor.
+ /// This motor has a slow, deep, powerful rumble.
+ /// </param>
+ /// <param name="rightStrength">
+ /// The strength of the right motor.
+ /// This motor has a snappy, quick, high-pitched rumble.
+ /// </param>
+ /// <returns>Returns true if the operation succeeded.</returns>
+ public bool Vibrate(int durationMs, float leftStrength = float.NegativeInfinity,
+ float rightStrength = float.NegativeInfinity)
+ {
+ if (!VibrationEnabled)
+ return false;
+
+ var lstrength = MathHelper.Clamp(leftStrength, 0, 1);
+ var rstrength = MathHelper.Clamp(rightStrength, 0, 1);
+
+ if (float.IsNegativeInfinity(leftStrength))
+ lstrength = _leftCurVibrationStrength;
+ if (float.IsNegativeInfinity(rightStrength))
+ rstrength = _rightCurVibrationStrength;
+
+ var success = GamePad.SetVibration(PlayerIndex, lstrength*VibrationStrengthLeft,
+ rstrength*VibrationStrengthRight);
+ if (success)
+ {
+ _leftVibrating = true;
+ _rightVibrating = true;
+
+ if (leftStrength > 0)
+ _vibrationDurationLeft = new TimeSpan(0, 0, 0, 0, durationMs);
+ else
+ {
+ if (lstrength > 0)
+ _vibrationDurationLeft -= _gameTime.TotalGameTime - _vibrationStart;
+ else
+ _leftVibrating = false;
+ }
+
+ if (rightStrength > 0)
+ _vibrationDurationRight = new TimeSpan(0, 0, 0, 0, durationMs);
+ else
+ {
+ if (rstrength > 0)
+ _vibrationDurationRight -= _gameTime.TotalGameTime - _vibrationStart;
+ else
+ _rightVibrating = false;
+ }
+
+ _vibrationStart = _gameTime.TotalGameTime;
+
+ _leftCurVibrationStrength = lstrength;
+ _rightCurVibrationStrength = rstrength;
+ }
+ return success;
+ }
+
+ private void CheckAllButtons()
+ {
+ // PacketNumber only and always changes if there is a difference between GamePadStates.
+ // ...At least, that's the theory. It doesn't seem to be implemented. Disabled for now.
+ //if (_lastPacketNumber == _currentState.PacketNumber)
+ // return;
+ foreach (Buttons button in Enum.GetValues(typeof(Buttons)))
+ {
+ if (_excludedButtons.Contains(button))
+ break;
+ if (_currentState.IsButtonDown(button) && _previousState.IsButtonUp(button))
+ RaiseButtonDown(button);
+ if (_currentState.IsButtonUp(button) && _previousState.IsButtonDown(button))
+ RaiseButtonUp(button);
+ }
+
+ // Checks triggers as buttons and floats
+ CheckTriggers(s => s.Triggers.Left, Buttons.LeftTrigger);
+ CheckTriggers(s => s.Triggers.Right, Buttons.RightTrigger);
+
+ // Checks thumbsticks as vector2s
+ CheckThumbSticks(s => s.ThumbSticks.Right, Buttons.RightStick);
+ CheckThumbSticks(s => s.ThumbSticks.Left, Buttons.LeftStick);
+ }
+
+ private void CheckTriggers(Func<GamePadState, float> getButtonState, Buttons button)
+ {
+ var debounce = 0.05f; // Value used to qualify a trigger as coming Up from a Down state
+ var curstate = getButtonState(_currentState);
+ var curdown = curstate > TriggerDownTreshold;
+ var prevdown = button == Buttons.RightTrigger ? _rightTriggerDown : _leftTriggerDown;
+
+ if (!prevdown && curdown)
+ {
+ RaiseButtonDown(button);
+ if (button == Buttons.RightTrigger)
+ _rightTriggerDown = true;
+ else
+ _leftTriggerDown = true;
+ }
+ else
+ {
+ if (prevdown && (curstate < debounce))
+ {
+ RaiseButtonUp(button);
+ if (button == Buttons.RightTrigger)
+ _rightTriggerDown = false;
+ else
+ _leftTriggerDown = false;
+ }
+ }
+
+ var prevstate = getButtonState(_lastTriggerState);
+ if (curstate > TriggerDeltaTreshold)
+ {
+ if (Math.Abs(prevstate - curstate) >= TriggerDeltaTreshold)
+ {
+ TriggerMoved?.Invoke(this, MakeArgs(button, curstate));
+ _lastTriggerState = _currentState;
+ }
+ }
+ else
+ {
+ if (prevstate > TriggerDeltaTreshold)
+ {
+ TriggerMoved?.Invoke(this, MakeArgs(button, curstate));
+ _lastTriggerState = _currentState;
+ }
+ }
+ }
+
+ private void CheckThumbSticks(Func<GamePadState, Vector2> getButtonState, Buttons button)
+ {
+ const float debounce = 0.15f;
+ var curVector = getButtonState(_currentState);
+ var curdown = curVector.Length() > ThumbstickDownTreshold;
+ var right = button == Buttons.RightStick;
+ var prevdown = right ? _rightStickDown : _leftStickDown;
+
+ var prevdir = button == Buttons.RightStick ? _lastRightStickDirection : _lastLeftStickDirection;
+ Buttons curdir;
+ if (curVector.Y > curVector.X)
+ {
+ if (curVector.Y > -curVector.X)
+ curdir = right ? Buttons.RightThumbstickUp : Buttons.LeftThumbstickUp;
+ else
+ curdir = right ? Buttons.RightThumbstickLeft : Buttons.LeftThumbstickLeft;
+ }
+ else
+ {
+ if (curVector.Y < -curVector.X)
+ curdir = right ? Buttons.RightThumbstickDown : Buttons.LeftThumbstickDown;
+ else
+ curdir = right ? Buttons.RightThumbstickRight : Buttons.LeftThumbstickRight;
+ }
+
+ if (!prevdown && curdown)
+ {
+ if (right)
+ _lastRightStickDirection = curdir;
+ else
+ _lastLeftStickDirection = curdir;
+
+ RaiseButtonDown(curdir);
+ if (button == Buttons.RightStick)
+ _rightStickDown = true;
+ else
+ _leftStickDown = true;
+ }
+ else
+ {
+ if (prevdown && (curVector.Length() < debounce))
+ {
+ RaiseButtonUp(prevdir);
+ if (button == Buttons.RightStick)
+ _rightStickDown = false;
+ else
+ _leftStickDown = false;
+ }
+ else
+ {
+ if (prevdown && curdown && (curdir != prevdir))
+ {
+ RaiseButtonUp(prevdir);
+ if (right)
+ _lastRightStickDirection = curdir;
+ else
+ _lastLeftStickDirection = curdir;
+ RaiseButtonDown(curdir);
+ }
+ }
+ }
+
+ var prevVector = getButtonState(_lastThumbStickState);
+ if (curVector.Length() > ThumbStickDeltaTreshold)
+ {
+ if (Vector2.Distance(curVector, prevVector) >= ThumbStickDeltaTreshold)
+ {
+ ThumbStickMoved?.Invoke(this, MakeArgs(button, thumbStickState: curVector));
+ _lastThumbStickState = _currentState;
+ }
+ }
+ else
+ {
+ if (prevVector.Length() > ThumbStickDeltaTreshold)
+ {
+ ThumbStickMoved?.Invoke(this, MakeArgs(button, thumbStickState: curVector));
+ _lastThumbStickState = _currentState;
+ }
+ }
+ }
+
+ internal static void CheckConnections()
+ {
+ if (!CheckControllerConnections)
+ return;
+
+ foreach (PlayerIndex index in Enum.GetValues(typeof(PlayerIndex)))
+ {
+ if (GamePad.GetState(index).IsConnected ^ _gamePadConnections[(int) index])
+ // We need more XORs in this world
+ {
+ _gamePadConnections[(int) index] = !_gamePadConnections[(int) index];
+ ControllerConnectionChanged?.Invoke(null,
+ new GamePadEventArgs(GamePadState.Default, GamePad.GetState(index), TimeSpan.Zero, index));
+ }
+ }
+ }
+
+ private void CheckVibrate()
+ {
+ if (_leftVibrating && (_vibrationStart + _vibrationDurationLeft < _gameTime.TotalGameTime))
+ Vibrate(0, 0);
+ if (_rightVibrating && (_vibrationStart + _vibrationDurationRight < _gameTime.TotalGameTime))
+ Vibrate(0, rightStrength: 0);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ _gameTime = gameTime;
+ _currentState = GamePad.GetState(PlayerIndex);
+ CheckVibrate();
+ if (!_currentState.IsConnected)
+ return;
+ CheckAllButtons();
+ CheckRepeatButton();
+ //_lastPacketNumber = _currentState.PacketNumber;
+ _previousGameTime = gameTime;
+ _previousState = _currentState;
+ }
+
+ private GamePadEventArgs MakeArgs(Buttons? button,
+ float triggerstate = 0, Vector2? thumbStickState = null)
+ {
+ var elapsedTime = _gameTime.TotalGameTime - _previousGameTime.TotalGameTime;
+ return new GamePadEventArgs(_previousState, _currentState,
+ elapsedTime, PlayerIndex, button, triggerstate, thumbStickState);
+ }
+
+ private void RaiseButtonDown(Buttons button)
+ {
+ ButtonDown?.Invoke(this, MakeArgs(button));
+ ButtonRepeated?.Invoke(this, MakeArgs(button));
+ _lastButton = button;
+ _repeatedButtonTimer = 0;
+ }
+
+ private void RaiseButtonUp(Buttons button)
+ {
+ ButtonUp?.Invoke(this, MakeArgs(button));
+ _lastButton = 0;
+ }
+
+ private void CheckRepeatButton()
+ {
+ _repeatedButtonTimer += _gameTime.ElapsedGameTime.Milliseconds;
+
+ if ((_repeatedButtonTimer < RepeatInitialDelay) || (_lastButton == 0))
+ return;
+
+ if (_repeatedButtonTimer < RepeatInitialDelay + RepeatDelay)
+ {
+ ButtonRepeated?.Invoke(this, MakeArgs(_lastButton));
+ _repeatedButtonTimer = RepeatDelay + RepeatInitialDelay;
+ }
+ else
+ {
+ if (_repeatedButtonTimer > RepeatInitialDelay + RepeatDelay*2)
+ {
+ ButtonRepeated?.Invoke(this, MakeArgs(_lastButton));
+ _repeatedButtonTimer = RepeatDelay + RepeatInitialDelay;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListenerSettings.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListenerSettings.cs
new file mode 100644
index 0000000..8c36e4c
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/GamePadListenerSettings.cs
@@ -0,0 +1,134 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ /// <summary>
+ /// This is a class that contains settings to be used to initialise a <see cref="GamePadListener" />.
+ /// </summary>
+ /// <seealso cref="InputListenerManager" />
+ public class GamePadListenerSettings : InputListenerSettings<GamePadListener>
+ {
+ public GamePadListenerSettings()
+ : this(PlayerIndex.One)
+ {
+ }
+
+ /// <summary>
+ /// This is a class that contains settings to be used to initialise a <see cref="GamePadListener" />.
+ /// <para>Note: There are a number of extra settings that are settable properties.</para>
+ /// </summary>
+ /// <param name="playerIndex">The index of the controller the listener will be tied to.</param>
+ /// <param name="vibrationEnabled">Whether vibration is enabled on the controller.</param>
+ /// <param name="vibrationStrengthLeft">
+ /// General setting for the strength of the left motor.
+ /// This motor has a slow, deep, powerful rumble.
+ /// This setting will modify all future vibrations
+ /// through this listener.
+ /// </param>
+ /// <param name="vibrationStrengthRight">
+ /// General setting for the strength of the right motor.
+ /// This motor has a snappy, quick, high-pitched rumble.
+ /// This setting will modify all future vibrations
+ /// through this listener.
+ /// </param>
+ public GamePadListenerSettings(PlayerIndex playerIndex, bool vibrationEnabled = true,
+ float vibrationStrengthLeft = 1.0f, float vibrationStrengthRight = 1.0f)
+ {
+ PlayerIndex = playerIndex;
+ VibrationEnabled = vibrationEnabled;
+ VibrationStrengthLeft = vibrationStrengthLeft;
+ VibrationStrengthRight = vibrationStrengthRight;
+ TriggerDownTreshold = 0.15f;
+ ThumbstickDownTreshold = 0.5f;
+ RepeatInitialDelay = 500;
+ RepeatDelay = 50;
+ }
+
+ /// <summary>
+ /// The index of the controller.
+ /// </summary>
+ public PlayerIndex PlayerIndex { get; set; }
+
+ /// <summary>
+ /// When a button is held down, the interval in which
+ /// ButtonRepeated fires. Value in milliseconds.
+ /// </summary>
+ public int RepeatDelay { get; set; }
+
+ /// <summary>
+ /// The amount of time a button has to be held down
+ /// in order to fire ButtonRepeated the first time.
+ /// Value in milliseconds.
+ /// </summary>
+ public int RepeatInitialDelay { get; set; }
+
+
+ /// <summary>
+ /// Whether vibration is enabled for this controller.
+ /// </summary>
+ public bool VibrationEnabled { get; set; }
+
+ /// <summary>
+ /// General setting for the strength of the left motor.
+ /// This motor has a slow, deep, powerful rumble.
+ /// <para>
+ /// This setting will modify all future vibrations
+ /// through this listener.
+ /// </para>
+ /// </summary>
+ public float VibrationStrengthLeft { get; set; }
+
+ /// <summary>
+ /// General setting for the strength of the right motor.
+ /// This motor has a snappy, quick, high-pitched rumble.
+ /// <para>
+ /// This setting will modify all future vibrations
+ /// through this listener.
+ /// </para>
+ /// </summary>
+ public float VibrationStrengthRight { get; set; }
+
+ /// <summary>
+ /// The treshold of movement that has to be met in order
+ /// for the listener to fire an event with the trigger's
+ /// updated position.
+ /// <para>
+ /// In essence this defines the event's
+ /// resolution.
+ /// </para>
+ /// At a value of 0 this will fire every time
+ /// the trigger's position is not 0f.
+ /// </summary>
+ public float TriggerDeltaTreshold { get; set; }
+
+ /// <summary>
+ /// The treshold of movement that has to be met in order
+ /// for the listener to fire an event with the thumbstick's
+ /// updated position.
+ /// <para>
+ /// In essence this defines the event's
+ /// resolution.
+ /// </para>
+ /// At a value of 0 this will fire every time
+ /// the thumbstick's position is not {x:0, y:0}.
+ /// </summary>
+ public float ThumbStickDeltaTreshold { get; set; }
+
+ /// <summary>
+ /// How deep the triggers have to be depressed in order to
+ /// register as a ButtonDown event.
+ /// </summary>
+ public float TriggerDownTreshold { get; set; }
+
+ /// <summary>
+ /// How deep the triggers have to be depressed in order to
+ /// register as a ButtonDown event.
+ /// </summary>
+ public float ThumbstickDownTreshold { get; private set; }
+
+ public override GamePadListener CreateListener()
+ {
+ return new GamePadListener(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/IInputService.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/IInputService.cs
new file mode 100644
index 0000000..46198ec
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/IInputService.cs
@@ -0,0 +1,13 @@
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public interface IInputService
+ {
+ KeyboardListener GuiKeyboardListener { get; }
+
+ MouseListener GuiMouseListener { get; }
+
+ GamePadListener GuiGamePadListener { get; }
+
+ TouchListener GuiTouchListener { get; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListener.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListener.cs
new file mode 100644
index 0000000..6323295
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListener.cs
@@ -0,0 +1,13 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public abstract class InputListener
+ {
+ protected InputListener()
+ {
+ }
+
+ public abstract void Update(GameTime gameTime);
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs
new file mode 100644
index 0000000..302f6b5
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerComponent.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class InputListenerComponent : GameComponent, IUpdate
+ {
+ private readonly List<InputListener> _listeners;
+
+ public InputListenerComponent(Game game)
+ : base(game)
+ {
+ _listeners = new List<InputListener>();
+ }
+
+ public InputListenerComponent(Game game, params InputListener[] listeners)
+ : base(game)
+ {
+ _listeners = new List<InputListener>(listeners);
+ }
+
+ public IList<InputListener> Listeners => _listeners;
+
+ public override void Update(GameTime gameTime)
+ {
+ base.Update(gameTime);
+
+ if (Game.IsActive)
+ {
+ foreach (var listener in _listeners)
+ listener.Update(gameTime);
+ }
+
+ GamePadListener.CheckConnections();
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerSettings.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerSettings.cs
new file mode 100644
index 0000000..468a30b
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/InputListenerSettings.cs
@@ -0,0 +1,8 @@
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public abstract class InputListenerSettings<T>
+ where T : InputListener
+ {
+ public abstract T CreateListener();
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardEventArgs.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardEventArgs.cs
new file mode 100644
index 0000000..d6d01ab
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardEventArgs.cs
@@ -0,0 +1,119 @@
+using System;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class KeyboardEventArgs : EventArgs
+ {
+ public KeyboardEventArgs(Keys key, KeyboardState keyboardState)
+ {
+ Key = key;
+
+ Modifiers = KeyboardModifiers.None;
+
+ if (keyboardState.IsKeyDown(Keys.LeftControl) || keyboardState.IsKeyDown(Keys.RightControl))
+ Modifiers |= KeyboardModifiers.Control;
+
+ if (keyboardState.IsKeyDown(Keys.LeftShift) || keyboardState.IsKeyDown(Keys.RightShift))
+ Modifiers |= KeyboardModifiers.Shift;
+
+ if (keyboardState.IsKeyDown(Keys.LeftAlt) || keyboardState.IsKeyDown(Keys.RightAlt))
+ Modifiers |= KeyboardModifiers.Alt;
+ }
+
+ public Keys Key { get; }
+ public KeyboardModifiers Modifiers { get; }
+
+ public char? Character => ToChar(Key, Modifiers);
+
+ private static char? ToChar(Keys key, KeyboardModifiers modifiers = KeyboardModifiers.None)
+ {
+ var isShiftDown = (modifiers & KeyboardModifiers.Shift) == KeyboardModifiers.Shift;
+
+ if (key == Keys.A) return isShiftDown ? 'A' : 'a';
+ if (key == Keys.B) return isShiftDown ? 'B' : 'b';
+ if (key == Keys.C) return isShiftDown ? 'C' : 'c';
+ if (key == Keys.D) return isShiftDown ? 'D' : 'd';
+ if (key == Keys.E) return isShiftDown ? 'E' : 'e';
+ if (key == Keys.F) return isShiftDown ? 'F' : 'f';
+ if (key == Keys.G) return isShiftDown ? 'G' : 'g';
+ if (key == Keys.H) return isShiftDown ? 'H' : 'h';
+ if (key == Keys.I) return isShiftDown ? 'I' : 'i';
+ if (key == Keys.J) return isShiftDown ? 'J' : 'j';
+ if (key == Keys.K) return isShiftDown ? 'K' : 'k';
+ if (key == Keys.L) return isShiftDown ? 'L' : 'l';
+ if (key == Keys.M) return isShiftDown ? 'M' : 'm';
+ if (key == Keys.N) return isShiftDown ? 'N' : 'n';
+ if (key == Keys.O) return isShiftDown ? 'O' : 'o';
+ if (key == Keys.P) return isShiftDown ? 'P' : 'p';
+ if (key == Keys.Q) return isShiftDown ? 'Q' : 'q';
+ if (key == Keys.R) return isShiftDown ? 'R' : 'r';
+ if (key == Keys.S) return isShiftDown ? 'S' : 's';
+ if (key == Keys.T) return isShiftDown ? 'T' : 't';
+ if (key == Keys.U) return isShiftDown ? 'U' : 'u';
+ if (key == Keys.V) return isShiftDown ? 'V' : 'v';
+ if (key == Keys.W) return isShiftDown ? 'W' : 'w';
+ if (key == Keys.X) return isShiftDown ? 'X' : 'x';
+ if (key == Keys.Y) return isShiftDown ? 'Y' : 'y';
+ if (key == Keys.Z) return isShiftDown ? 'Z' : 'z';
+
+ if (((key == Keys.D0) && !isShiftDown) || (key == Keys.NumPad0)) return '0';
+ if (((key == Keys.D1) && !isShiftDown) || (key == Keys.NumPad1)) return '1';
+ if (((key == Keys.D2) && !isShiftDown) || (key == Keys.NumPad2)) return '2';
+ if (((key == Keys.D3) && !isShiftDown) || (key == Keys.NumPad3)) return '3';
+ if (((key == Keys.D4) && !isShiftDown) || (key == Keys.NumPad4)) return '4';
+ if (((key == Keys.D5) && !isShiftDown) || (key == Keys.NumPad5)) return '5';
+ if (((key == Keys.D6) && !isShiftDown) || (key == Keys.NumPad6)) return '6';
+ if (((key == Keys.D7) && !isShiftDown) || (key == Keys.NumPad7)) return '7';
+ if (((key == Keys.D8) && !isShiftDown) || (key == Keys.NumPad8)) return '8';
+ if (((key == Keys.D9) && !isShiftDown) || (key == Keys.NumPad9)) return '9';
+
+ if ((key == Keys.D0) && isShiftDown) return ')';
+ if ((key == Keys.D1) && isShiftDown) return '!';
+ if ((key == Keys.D2) && isShiftDown) return '@';
+ if ((key == Keys.D3) && isShiftDown) return '#';
+ if ((key == Keys.D4) && isShiftDown) return '$';
+ if ((key == Keys.D5) && isShiftDown) return '%';
+ if ((key == Keys.D6) && isShiftDown) return '^';
+ if ((key == Keys.D7) && isShiftDown) return '&';
+ if ((key == Keys.D8) && isShiftDown) return '*';
+ if ((key == Keys.D9) && isShiftDown) return '(';
+
+ if (key == Keys.Space) return ' ';
+ if (key == Keys.Tab) return '\t';
+ if (key == Keys.Enter) return (char) 13;
+ if (key == Keys.Back) return (char) 8;
+
+ if (key == Keys.Add) return '+';
+ if (key == Keys.Decimal) return '.';
+ if (key == Keys.Divide) return '/';
+ if (key == Keys.Multiply) return '*';
+ if (key == Keys.OemBackslash) return '\\';
+ if ((key == Keys.OemComma) && !isShiftDown) return ',';
+ if ((key == Keys.OemComma) && isShiftDown) return '<';
+ if ((key == Keys.OemOpenBrackets) && !isShiftDown) return '[';
+ if ((key == Keys.OemOpenBrackets) && isShiftDown) return '{';
+ if ((key == Keys.OemCloseBrackets) && !isShiftDown) return ']';
+ if ((key == Keys.OemCloseBrackets) && isShiftDown) return '}';
+ if ((key == Keys.OemPeriod) && !isShiftDown) return '.';
+ if ((key == Keys.OemPeriod) && isShiftDown) return '>';
+ if ((key == Keys.OemPipe) && !isShiftDown) return '\\';
+ if ((key == Keys.OemPipe) && isShiftDown) return '|';
+ if ((key == Keys.OemPlus) && !isShiftDown) return '=';
+ if ((key == Keys.OemPlus) && isShiftDown) return '+';
+ if ((key == Keys.OemMinus) && !isShiftDown) return '-';
+ if ((key == Keys.OemMinus) && isShiftDown) return '_';
+ if ((key == Keys.OemQuestion) && !isShiftDown) return '/';
+ if ((key == Keys.OemQuestion) && isShiftDown) return '?';
+ if ((key == Keys.OemQuotes) && !isShiftDown) return '\'';
+ if ((key == Keys.OemQuotes) && isShiftDown) return '"';
+ if ((key == Keys.OemSemicolon) && !isShiftDown) return ';';
+ if ((key == Keys.OemSemicolon) && isShiftDown) return ':';
+ if ((key == Keys.OemTilde) && !isShiftDown) return '`';
+ if ((key == Keys.OemTilde) && isShiftDown) return '~';
+ if (key == Keys.Subtract) return '-';
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListener.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListener.cs
new file mode 100644
index 0000000..9fc85a0
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListener.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class KeyboardListener : InputListener
+ {
+ private Array _keysValues = Enum.GetValues(typeof(Keys));
+
+ private bool _isInitial;
+ private TimeSpan _lastPressTime;
+
+ private Keys _previousKey;
+ private KeyboardState _previousState;
+
+ public KeyboardListener()
+ : this(new KeyboardListenerSettings())
+ {
+ }
+
+ public KeyboardListener(KeyboardListenerSettings settings)
+ {
+ RepeatPress = settings.RepeatPress;
+ InitialDelay = settings.InitialDelayMilliseconds;
+ RepeatDelay = settings.RepeatDelayMilliseconds;
+ }
+
+ public bool RepeatPress { get; }
+ public int InitialDelay { get; }
+ public int RepeatDelay { get; }
+
+ public event EventHandler<KeyboardEventArgs> KeyTyped;
+ public event EventHandler<KeyboardEventArgs> KeyPressed;
+ public event EventHandler<KeyboardEventArgs> KeyReleased;
+
+ public override void Update(GameTime gameTime)
+ {
+ var currentState = Keyboard.GetState();
+
+ RaisePressedEvents(gameTime, currentState);
+ RaiseReleasedEvents(currentState);
+
+ if (RepeatPress)
+ RaiseRepeatEvents(gameTime, currentState);
+
+ _previousState = currentState;
+ }
+
+ private void RaisePressedEvents(GameTime gameTime, KeyboardState currentState)
+ {
+ if (!currentState.IsKeyDown(Keys.LeftAlt) && !currentState.IsKeyDown(Keys.RightAlt))
+ {
+ var pressedKeys = _keysValues
+ .Cast<Keys>()
+ .Where(key => currentState.IsKeyDown(key) && _previousState.IsKeyUp(key));
+
+ foreach (var key in pressedKeys)
+ {
+ var args = new KeyboardEventArgs(key, currentState);
+
+ KeyPressed?.Invoke(this, args);
+
+ if (args.Character.HasValue)
+ KeyTyped?.Invoke(this, args);
+
+ _previousKey = key;
+ _lastPressTime = gameTime.TotalGameTime;
+ _isInitial = true;
+ }
+ }
+ }
+
+ private void RaiseReleasedEvents(KeyboardState currentState)
+ {
+ var releasedKeys = _keysValues
+ .Cast<Keys>()
+ .Where(key => currentState.IsKeyUp(key) && _previousState.IsKeyDown(key));
+
+ foreach (var key in releasedKeys)
+ KeyReleased?.Invoke(this, new KeyboardEventArgs(key, currentState));
+ }
+
+ private void RaiseRepeatEvents(GameTime gameTime, KeyboardState currentState)
+ {
+ var elapsedTime = (gameTime.TotalGameTime - _lastPressTime).TotalMilliseconds;
+
+ if (currentState.IsKeyDown(_previousKey) &&
+ (_isInitial && elapsedTime > InitialDelay || !_isInitial && elapsedTime > RepeatDelay))
+ {
+ var args = new KeyboardEventArgs(_previousKey, currentState);
+
+ KeyPressed?.Invoke(this, args);
+
+ if (args.Character.HasValue)
+ KeyTyped?.Invoke(this, args);
+
+ _lastPressTime = gameTime.TotalGameTime;
+ _isInitial = false;
+ }
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListenerSettings.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListenerSettings.cs
new file mode 100644
index 0000000..86481ed
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardListenerSettings.cs
@@ -0,0 +1,21 @@
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class KeyboardListenerSettings : InputListenerSettings<KeyboardListener>
+ {
+ public KeyboardListenerSettings()
+ {
+ RepeatPress = true;
+ InitialDelayMilliseconds = 800;
+ RepeatDelayMilliseconds = 50;
+ }
+
+ public bool RepeatPress { get; set; }
+ public int InitialDelayMilliseconds { get; set; }
+ public int RepeatDelayMilliseconds { get; set; }
+
+ public override KeyboardListener CreateListener()
+ {
+ return new KeyboardListener(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardModifiers.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardModifiers.cs
new file mode 100644
index 0000000..de59905
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/KeyboardModifiers.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ [Flags]
+ public enum KeyboardModifiers
+ {
+ Control = 1,
+ Shift = 2,
+ Alt = 4,
+ None = 0
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseEventArgs.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseEventArgs.cs
new file mode 100644
index 0000000..2717325
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseEventArgs.cs
@@ -0,0 +1,35 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using MonoGame.Extended.ViewportAdapters;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class MouseEventArgs : EventArgs
+ {
+ public MouseEventArgs(ViewportAdapter viewportAdapter, TimeSpan time, MouseState previousState,
+ MouseState currentState,
+ MouseButton button = MouseButton.None)
+ {
+ PreviousState = previousState;
+ CurrentState = currentState;
+ Position = viewportAdapter?.PointToScreen(currentState.X, currentState.Y)
+ ?? new Point(currentState.X, currentState.Y);
+ Button = button;
+ ScrollWheelValue = currentState.ScrollWheelValue;
+ ScrollWheelDelta = currentState.ScrollWheelValue - previousState.ScrollWheelValue;
+ Time = time;
+ }
+
+ public TimeSpan Time { get; }
+
+ public MouseState PreviousState { get; }
+ public MouseState CurrentState { get; }
+ public Point Position { get; }
+ public MouseButton Button { get; }
+ public int ScrollWheelValue { get; }
+ public int ScrollWheelDelta { get; }
+
+ public Vector2 DistanceMoved => CurrentState.Position.ToVector2() - PreviousState.Position.ToVector2();
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListener.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListener.cs
new file mode 100644
index 0000000..e71a67f
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListener.cs
@@ -0,0 +1,193 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using MonoGame.Extended.ViewportAdapters;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ /// <summary>
+ /// Handles mouse input.
+ /// </summary>
+ /// <remarks>
+ /// Due to nature of the listener, even when game is not in focus, listener will continue to be updated.
+ /// To avoid that, manual pause of Update() method is required whenever game loses focus.
+ /// To avoid having to do it manually, register listener to <see cref="InputListenerComponent" />
+ /// </remarks>
+ public class MouseListener : InputListener
+ {
+ private MouseState _currentState;
+ private bool _dragging;
+ private GameTime _gameTime;
+ private bool _hasDoubleClicked;
+ private MouseEventArgs _mouseDownArgs;
+ private MouseEventArgs _previousClickArgs;
+ private MouseState _previousState;
+
+ public MouseListener()
+ : this(new MouseListenerSettings())
+ {
+ }
+
+ public MouseListener(ViewportAdapter viewportAdapter)
+ : this(new MouseListenerSettings())
+ {
+ ViewportAdapter = viewportAdapter;
+ }
+
+ public MouseListener(MouseListenerSettings settings)
+ {
+ ViewportAdapter = settings.ViewportAdapter;
+ DoubleClickMilliseconds = settings.DoubleClickMilliseconds;
+ DragThreshold = settings.DragThreshold;
+ }
+
+ public ViewportAdapter ViewportAdapter { get; }
+
+ public int DoubleClickMilliseconds { get; }
+ public int DragThreshold { get; }
+
+ /// <summary>
+ /// Returns true if the mouse has moved between the current and previous frames.
+ /// </summary>
+ /// <value><c>true</c> if the mouse has moved; otherwise, <c>false</c>.</value>
+ public bool HasMouseMoved => (_previousState.X != _currentState.X) || (_previousState.Y != _currentState.Y);
+
+ public event EventHandler<MouseEventArgs> MouseDown;
+ public event EventHandler<MouseEventArgs> MouseUp;
+ public event EventHandler<MouseEventArgs> MouseClicked;
+ public event EventHandler<MouseEventArgs> MouseDoubleClicked;
+ public event EventHandler<MouseEventArgs> MouseMoved;
+ public event EventHandler<MouseEventArgs> MouseWheelMoved;
+ public event EventHandler<MouseEventArgs> MouseDragStart;
+ public event EventHandler<MouseEventArgs> MouseDrag;
+ public event EventHandler<MouseEventArgs> MouseDragEnd;
+
+ private void CheckButtonPressed(Func<MouseState, ButtonState> getButtonState, MouseButton button)
+ {
+ if ((getButtonState(_currentState) == ButtonState.Pressed) &&
+ (getButtonState(_previousState) == ButtonState.Released))
+ {
+ var args = new MouseEventArgs(ViewportAdapter, _gameTime.TotalGameTime, _previousState, _currentState, button);
+
+ MouseDown?.Invoke(this, args);
+ _mouseDownArgs = args;
+
+ if (_previousClickArgs != null)
+ {
+ // If the last click was recent
+ var clickMilliseconds = (args.Time - _previousClickArgs.Time).TotalMilliseconds;
+
+ if (clickMilliseconds <= DoubleClickMilliseconds)
+ {
+ MouseDoubleClicked?.Invoke(this, args);
+ _hasDoubleClicked = true;
+ }
+
+ _previousClickArgs = null;
+ }
+ }
+ }
+
+ private void CheckButtonReleased(Func<MouseState, ButtonState> getButtonState, MouseButton button)
+ {
+ if ((getButtonState(_currentState) == ButtonState.Released) &&
+ (getButtonState(_previousState) == ButtonState.Pressed))
+ {
+ var args = new MouseEventArgs(ViewportAdapter, _gameTime.TotalGameTime, _previousState, _currentState, button);
+
+ if (_mouseDownArgs.Button == args.Button)
+ {
+ var clickMovement = DistanceBetween(args.Position, _mouseDownArgs.Position);
+
+ // If the mouse hasn't moved much between mouse down and mouse up
+ if (clickMovement < DragThreshold)
+ {
+ if (!_hasDoubleClicked)
+ MouseClicked?.Invoke(this, args);
+ }
+ else // If the mouse has moved between mouse down and mouse up
+ {
+ MouseDragEnd?.Invoke(this, args);
+ _dragging = false;
+ }
+ }
+
+ MouseUp?.Invoke(this, args);
+
+ _hasDoubleClicked = false;
+ _previousClickArgs = args;
+ }
+ }
+
+ private void CheckMouseDragged(Func<MouseState, ButtonState> getButtonState, MouseButton button)
+ {
+ if ((getButtonState(_currentState) == ButtonState.Pressed) &&
+ (getButtonState(_previousState) == ButtonState.Pressed))
+ {
+ var args = new MouseEventArgs(ViewportAdapter, _gameTime.TotalGameTime, _previousState, _currentState, button);
+
+ if (_mouseDownArgs.Button == args.Button)
+ {
+ if (_dragging)
+ MouseDrag?.Invoke(this, args);
+ else
+ {
+ // Only start to drag based on DragThreshold
+ var clickMovement = DistanceBetween(args.Position, _mouseDownArgs.Position);
+
+ if (clickMovement > DragThreshold)
+ {
+ _dragging = true;
+ MouseDragStart?.Invoke(this, args);
+ }
+ }
+ }
+ }
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ _gameTime = gameTime;
+ _currentState = Mouse.GetState();
+
+ CheckButtonPressed(s => s.LeftButton, MouseButton.Left);
+ CheckButtonPressed(s => s.MiddleButton, MouseButton.Middle);
+ CheckButtonPressed(s => s.RightButton, MouseButton.Right);
+ CheckButtonPressed(s => s.XButton1, MouseButton.XButton1);
+ CheckButtonPressed(s => s.XButton2, MouseButton.XButton2);
+
+ CheckButtonReleased(s => s.LeftButton, MouseButton.Left);
+ CheckButtonReleased(s => s.MiddleButton, MouseButton.Middle);
+ CheckButtonReleased(s => s.RightButton, MouseButton.Right);
+ CheckButtonReleased(s => s.XButton1, MouseButton.XButton1);
+ CheckButtonReleased(s => s.XButton2, MouseButton.XButton2);
+
+ // Check for any sort of mouse movement.
+ if (HasMouseMoved)
+ {
+ MouseMoved?.Invoke(this,
+ new MouseEventArgs(ViewportAdapter, gameTime.TotalGameTime, _previousState, _currentState));
+
+ CheckMouseDragged(s => s.LeftButton, MouseButton.Left);
+ CheckMouseDragged(s => s.MiddleButton, MouseButton.Middle);
+ CheckMouseDragged(s => s.RightButton, MouseButton.Right);
+ CheckMouseDragged(s => s.XButton1, MouseButton.XButton1);
+ CheckMouseDragged(s => s.XButton2, MouseButton.XButton2);
+ }
+
+ // Handle mouse wheel events.
+ if (_previousState.ScrollWheelValue != _currentState.ScrollWheelValue)
+ {
+ MouseWheelMoved?.Invoke(this,
+ new MouseEventArgs(ViewportAdapter, gameTime.TotalGameTime, _previousState, _currentState));
+ }
+
+ _previousState = _currentState;
+ }
+
+ private static int DistanceBetween(Point a, Point b)
+ {
+ return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListenerSettings.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListenerSettings.cs
new file mode 100644
index 0000000..1c0ca3d
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/MouseListenerSettings.cs
@@ -0,0 +1,23 @@
+using MonoGame.Extended.ViewportAdapters;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class MouseListenerSettings : InputListenerSettings<MouseListener>
+ {
+ public MouseListenerSettings()
+ {
+ // initial values are windows defaults
+ DoubleClickMilliseconds = 500;
+ DragThreshold = 2;
+ }
+
+ public int DragThreshold { get; set; }
+ public int DoubleClickMilliseconds { get; set; }
+ public ViewportAdapter ViewportAdapter { get; set; }
+
+ public override MouseListener CreateListener()
+ {
+ return new MouseListener(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchEventArgs.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchEventArgs.cs
new file mode 100644
index 0000000..8172885
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchEventArgs.cs
@@ -0,0 +1,39 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input.Touch;
+using MonoGame.Extended.ViewportAdapters;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class TouchEventArgs : EventArgs
+ {
+ public TouchEventArgs(ViewportAdapter viewportAdapter, TimeSpan time, TouchLocation location)
+ {
+ ViewportAdapter = viewportAdapter;
+ RawTouchLocation = location;
+ Time = time;
+ Position = viewportAdapter?.PointToScreen((int)location.Position.X, (int)location.Position.Y)
+ ?? location.Position.ToPoint();
+ }
+
+ public ViewportAdapter ViewportAdapter { get; }
+ public TouchLocation RawTouchLocation { get; }
+ public TimeSpan Time { get; }
+ public Point Position { get; }
+
+ public override bool Equals(object other)
+ {
+ var args = other as TouchEventArgs;
+
+ if (args == null)
+ return false;
+
+ return ReferenceEquals(this, args) || RawTouchLocation.Id.Equals(args.RawTouchLocation.Id);
+ }
+
+ public override int GetHashCode()
+ {
+ return RawTouchLocation.Id.GetHashCode();
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListener.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListener.cs
new file mode 100644
index 0000000..2a89cc9
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListener.cs
@@ -0,0 +1,57 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input.Touch;
+using MonoGame.Extended.ViewportAdapters;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class TouchListener : InputListener
+ {
+ public TouchListener()
+ : this(new TouchListenerSettings())
+ {
+ }
+
+ public TouchListener(ViewportAdapter viewportAdapter)
+ : this(new TouchListenerSettings())
+ {
+ ViewportAdapter = viewportAdapter;
+ }
+
+ public TouchListener(TouchListenerSettings settings)
+ {
+ ViewportAdapter = settings.ViewportAdapter;
+ }
+
+ public ViewportAdapter ViewportAdapter { get; set; }
+
+ public event EventHandler<TouchEventArgs> TouchStarted;
+ public event EventHandler<TouchEventArgs> TouchEnded;
+ public event EventHandler<TouchEventArgs> TouchMoved;
+ public event EventHandler<TouchEventArgs> TouchCancelled;
+
+ public override void Update(GameTime gameTime)
+ {
+ var touchCollection = TouchPanel.GetState();
+
+ foreach (var touchLocation in touchCollection)
+ {
+ switch (touchLocation.State)
+ {
+ case TouchLocationState.Pressed:
+ TouchStarted?.Invoke(this, new TouchEventArgs(ViewportAdapter, gameTime.TotalGameTime, touchLocation));
+ break;
+ case TouchLocationState.Moved:
+ TouchMoved?.Invoke(this, new TouchEventArgs(ViewportAdapter, gameTime.TotalGameTime, touchLocation));
+ break;
+ case TouchLocationState.Released:
+ TouchEnded?.Invoke(this, new TouchEventArgs(ViewportAdapter, gameTime.TotalGameTime, touchLocation));
+ break;
+ case TouchLocationState.Invalid:
+ TouchCancelled?.Invoke(this, new TouchEventArgs(ViewportAdapter, gameTime.TotalGameTime, touchLocation));
+ break;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListenerSettings.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListenerSettings.cs
new file mode 100644
index 0000000..6d42b42
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/InputListeners/TouchListenerSettings.cs
@@ -0,0 +1,18 @@
+using MonoGame.Extended.ViewportAdapters;
+
+namespace MonoGame.Extended.Input.InputListeners
+{
+ public class TouchListenerSettings : InputListenerSettings<TouchListener>
+ {
+ public TouchListenerSettings()
+ {
+ }
+
+ public ViewportAdapter ViewportAdapter { get; set; }
+
+ public override TouchListener CreateListener()
+ {
+ return new TouchListener(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardExtended.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardExtended.cs
new file mode 100644
index 0000000..0ed7c76
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardExtended.cs
@@ -0,0 +1,22 @@
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input
+{
+ public static class KeyboardExtended
+ {
+ // TODO: This global static state was a horrible idea.
+ private static KeyboardState _currentKeyboardState;
+ private static KeyboardState _previousKeyboardState;
+
+ public static KeyboardStateExtended GetState()
+ {
+ return new KeyboardStateExtended(_currentKeyboardState, _previousKeyboardState);
+ }
+
+ public static void Refresh()
+ {
+ _previousKeyboardState = _currentKeyboardState;
+ _currentKeyboardState = Keyboard.GetState();
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardStateExtended.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardStateExtended.cs
new file mode 100644
index 0000000..ee18677
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/KeyboardStateExtended.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Linq;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input
+{
+ public struct KeyboardStateExtended
+ {
+ private KeyboardState _currentKeyboardState;
+ private KeyboardState _previousKeyboardState;
+
+ public KeyboardStateExtended(KeyboardState currentKeyboardState, KeyboardState previousKeyboardState)
+ {
+ _currentKeyboardState = currentKeyboardState;
+ _previousKeyboardState = previousKeyboardState;
+ }
+
+ public bool CapsLock => _currentKeyboardState.CapsLock;
+ public bool NumLock => _currentKeyboardState.NumLock;
+ public bool IsShiftDown() => _currentKeyboardState.IsKeyDown(Keys.LeftShift) || _currentKeyboardState.IsKeyDown(Keys.RightShift);
+ public bool IsControlDown() => _currentKeyboardState.IsKeyDown(Keys.LeftControl) || _currentKeyboardState.IsKeyDown(Keys.RightControl);
+ public bool IsAltDown() => _currentKeyboardState.IsKeyDown(Keys.LeftAlt) || _currentKeyboardState.IsKeyDown(Keys.RightAlt);
+ public bool IsKeyDown(Keys key) => _currentKeyboardState.IsKeyDown(key);
+ public bool IsKeyUp(Keys key) => _currentKeyboardState.IsKeyUp(key);
+ public Keys[] GetPressedKeys() => _currentKeyboardState.GetPressedKeys();
+ public void GetPressedKeys(Keys[] keys) => _currentKeyboardState.GetPressedKeys(keys);
+
+ /// <summary>
+ /// Gets whether the given key was down on the previous state, but is now up.
+ /// </summary>
+ /// <param name="key">The key to check.</param>
+ /// <returns>true if the key was released this state-change, otherwise false.</returns>
+ [Obsolete($"Deprecated in favor of {nameof(IsKeyReleased)}")]
+ public bool WasKeyJustDown(Keys key) => _previousKeyboardState.IsKeyDown(key) && _currentKeyboardState.IsKeyUp(key);
+
+ /// <summary>
+ /// Gets whether the given key was up on the previous state, but is now down.
+ /// </summary>
+ /// <param name="key">The key to check.</param>
+ /// <returns>true if the key was pressed this state-change, otherwise false.</returns>
+ [Obsolete($"Deprecated in favor of {nameof(IsKeyPressed)}")]
+ public bool WasKeyJustUp(Keys key) => _previousKeyboardState.IsKeyUp(key) && _currentKeyboardState.IsKeyDown(key);
+
+ /// <summary>
+ /// Gets whether the given key was down on the previous state, but is now up.
+ /// </summary>
+ /// <param name="key">The key to check.</param>
+ /// <returns>true if the key was released this state-change, otherwise false.</returns>
+ public readonly bool IsKeyReleased(Keys key) => _previousKeyboardState.IsKeyDown(key) && _currentKeyboardState.IsKeyUp(key);
+
+ /// <summary>
+ /// Gets whether the given key was up on the previous state, but is now down.
+ /// </summary>
+ /// <param name="key">The key to check.</param>
+ /// <returns>true if the key was pressed this state-change, otherwise false.</returns>
+ public readonly bool IsKeyPressed(Keys key) => _previousKeyboardState.IsKeyUp(key) && _currentKeyboardState.IsKeyDown(key);
+
+ public bool WasAnyKeyJustDown() => _previousKeyboardState.GetPressedKeyCount() > 0;
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj
new file mode 100644
index 0000000..4b3bd82
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>An event based input system to MonoGame more awesome.</Description>
+ <PackageTags>monogame input event based listeners</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\MonoGame.Extended\MonoGame.Extended.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseButton.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseButton.cs
new file mode 100644
index 0000000..e4a00f8
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseButton.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace MonoGame.Extended.Input
+{
+ [Flags]
+ public enum MouseButton
+ {
+ None = 0,
+ Left = 1,
+ Middle = 2,
+ Right = 4,
+ XButton1 = 8,
+ XButton2 = 16
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseExtended.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseExtended.cs
new file mode 100644
index 0000000..61d6d18
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseExtended.cs
@@ -0,0 +1,34 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input
+{
+ public static class MouseExtended
+ {
+ // TODO: This global static state was a horrible idea.
+ private static MouseState _currentMouseState;
+ private static MouseState _previousMouseState;
+
+ public static MouseStateExtended GetState()
+ {
+ return new MouseStateExtended(_currentMouseState, _previousMouseState);
+ }
+
+ public static void Refresh()
+ {
+ _previousMouseState = _currentMouseState;
+ _currentMouseState = Mouse.GetState();
+ }
+
+ public static void SetPosition(int x, int y) => Mouse.SetPosition(x, y);
+ public static void SetPosition(Point point) => Mouse.SetPosition(point.X, point.Y);
+ public static void SetCursor(MouseCursor cursor) => Mouse.SetCursor(cursor);
+
+ public static IntPtr WindowHandle
+ {
+ get => Mouse.WindowHandle;
+ set => Mouse.WindowHandle = value;
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseStateExtended.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseStateExtended.cs
new file mode 100644
index 0000000..0ec6943
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Input/MouseStateExtended.cs
@@ -0,0 +1,149 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace MonoGame.Extended.Input
+{
+ public struct MouseStateExtended
+ {
+ private readonly MouseState _currentMouseState;
+ private readonly MouseState _previousMouseState;
+
+ public MouseStateExtended(MouseState currentMouseState, MouseState previousMouseState)
+ {
+ _currentMouseState = currentMouseState;
+ _previousMouseState = previousMouseState;
+ }
+
+ public int X => _currentMouseState.X;
+ public int Y => _currentMouseState.Y;
+ public Point Position => _currentMouseState.Position;
+ public bool PositionChanged => _currentMouseState.Position != _previousMouseState.Position;
+
+ public int DeltaX => _previousMouseState.X - _currentMouseState.X;
+ public int DeltaY => _previousMouseState.Y - _currentMouseState.Y;
+ public Point DeltaPosition => new Point(DeltaX, DeltaY);
+
+ public int ScrollWheelValue => _currentMouseState.ScrollWheelValue;
+ public int DeltaScrollWheelValue => _previousMouseState.ScrollWheelValue - _currentMouseState.ScrollWheelValue;
+
+ public ButtonState LeftButton => _currentMouseState.LeftButton;
+ public ButtonState MiddleButton => _currentMouseState.MiddleButton;
+ public ButtonState RightButton => _currentMouseState.RightButton;
+ public ButtonState XButton1 => _currentMouseState.XButton1;
+ public ButtonState XButton2 => _currentMouseState.XButton2;
+
+ public bool IsButtonDown(MouseButton button)
+ {
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (button)
+ {
+ case MouseButton.Left: return IsPressed(m => m.LeftButton);
+ case MouseButton.Middle: return IsPressed(m => m.MiddleButton);
+ case MouseButton.Right: return IsPressed(m => m.RightButton);
+ case MouseButton.XButton1: return IsPressed(m => m.XButton1);
+ case MouseButton.XButton2: return IsPressed(m => m.XButton2);
+ }
+
+ return false;
+ }
+
+ public bool IsButtonUp(MouseButton button)
+ {
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (button)
+ {
+ case MouseButton.Left: return IsReleased(m => m.LeftButton);
+ case MouseButton.Middle: return IsReleased(m => m.MiddleButton);
+ case MouseButton.Right: return IsReleased(m => m.RightButton);
+ case MouseButton.XButton1: return IsReleased(m => m.XButton1);
+ case MouseButton.XButton2: return IsReleased(m => m.XButton2);
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Get the just-down state for the mouse on this state-change: true if the mouse button has just been pressed.
+ /// </summary>
+ /// <param name="button"></param>
+ /// <remarks>Deprecated because of inconsistency with <see cref="KeyboardStateExtended"/></remarks>
+ /// <returns>The just-down state for the mouse on this state-change.</returns>
+ [Obsolete($"Deprecated in favor of {nameof(IsButtonPressed)}")]
+ public bool WasButtonJustDown(MouseButton button)
+ {
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (button)
+ {
+ case MouseButton.Left: return WasJustPressed(m => m.LeftButton);
+ case MouseButton.Middle: return WasJustPressed(m => m.MiddleButton);
+ case MouseButton.Right: return WasJustPressed(m => m.RightButton);
+ case MouseButton.XButton1: return WasJustPressed(m => m.XButton1);
+ case MouseButton.XButton2: return WasJustPressed(m => m.XButton2);
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Get the just-up state for the mouse on this state-change: true if the mouse button has just been released.
+ /// </summary>
+ /// <param name="button"></param>
+ /// <remarks>Deprecated because of inconsistency with <see cref="KeyboardStateExtended"/></remarks>
+ /// <returns>The just-up state for the mouse on this state-change.</returns>
+ [Obsolete($"Deprecated in favor of {nameof(IsButtonReleased)}")]
+ public bool WasButtonJustUp(MouseButton button)
+ {
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (button)
+ {
+ case MouseButton.Left: return WasJustReleased(m => m.LeftButton);
+ case MouseButton.Middle: return WasJustReleased(m => m.MiddleButton);
+ case MouseButton.Right: return WasJustReleased(m => m.RightButton);
+ case MouseButton.XButton1: return WasJustReleased(m => m.XButton1);
+ case MouseButton.XButton2: return WasJustReleased(m => m.XButton2);
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Get the pressed state of a mouse button, for this state-change.
+ /// </summary>
+ /// <param name="button">The button to check.</param>
+ /// <returns>true if the given mouse button was pressed this state-change, otherwise false</returns>
+ public readonly bool IsButtonPressed(MouseButton button) => button switch
+ {
+ MouseButton.Left => WasJustPressed(m => m.LeftButton),
+ MouseButton.Middle => WasJustPressed(m => m.MiddleButton),
+ MouseButton.Right => WasJustPressed(m => m.RightButton),
+ MouseButton.XButton1 => WasJustPressed(m => m.XButton1),
+ MouseButton.XButton2 => WasJustPressed(m => m.XButton2),
+ _ => false,
+ };
+
+ /// <summary>
+ /// Get the released state of a mouse button, for this state-change.
+ /// </summary>
+ /// <param name="button">The button to check.</param>
+ /// <returns>true if the given mouse button was released this state-change, otherwise false</returns>
+ public readonly bool IsButtonReleased(MouseButton button) => button switch
+ {
+ MouseButton.Left => WasJustReleased(m => m.LeftButton),
+ MouseButton.Middle => WasJustReleased(m => m.MiddleButton),
+ MouseButton.Right => WasJustReleased(m => m.RightButton),
+ MouseButton.XButton1 => WasJustReleased(m => m.XButton1),
+ MouseButton.XButton2 => WasJustReleased(m => m.XButton2),
+ _ => false,
+ };
+
+ private readonly bool IsPressed(Func<MouseState, ButtonState> button)
+ => button(_currentMouseState) == ButtonState.Pressed;
+ private readonly bool IsReleased(Func<MouseState, ButtonState> button)
+ => button(_currentMouseState) == ButtonState.Released;
+ private readonly bool WasJustPressed(Func<MouseState, ButtonState> button)
+ => button(_previousMouseState) == ButtonState.Released && button(_currentMouseState) == ButtonState.Pressed;
+ private readonly bool WasJustReleased(Func<MouseState, ButtonState> button)
+ => button(_previousMouseState) == ButtonState.Pressed && button(_currentMouseState) == ButtonState.Released;
+ }
+}