diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index cdb5afa2..aa9d27d2 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -25,9 +25,8 @@ namespace Examples.Tests int texture; bool mouse_in_window = false; bool viewport_changed = true; - bool refresh_text = true; - MouseState mouse, mouse_old; - KeyboardState keyboard, keyboard_old; + MouseState mouse; + KeyboardState keyboard; public GameWindowStates() : base(800, 600, GraphicsMode.Default) @@ -40,20 +39,9 @@ namespace Examples.Tests MouseEnter += delegate { mouse_in_window = true; }; MouseLeave += delegate { mouse_in_window = false; }; - Move += delegate { refresh_text = true; }; - Resize += delegate { refresh_text = true; }; - WindowBorderChanged += delegate { refresh_text = true; }; - WindowStateChanged += delegate { refresh_text = true; }; - FocusedChanged += delegate { refresh_text = true; }; Mouse.Move += MouseMoveHandler; Mouse.ButtonDown += MouseButtonHandler; Mouse.ButtonUp += MouseButtonHandler; - foreach (var joystick in Joysticks) - { - joystick.Move += delegate { refresh_text = true; }; - joystick.ButtonDown += delegate { refresh_text = true; }; - joystick.ButtonUp += delegate { refresh_text = true; }; - } } private void KeyPressHandler(object sender, KeyPressEventArgs e) @@ -99,13 +87,10 @@ namespace Examples.Tests void MouseMoveHandler(object sender, MouseMoveEventArgs e) { - refresh_text = true; } void MouseButtonHandler(object sender, MouseButtonEventArgs e) { - refresh_text = true; - if (e.Button == MouseButton.Left && e.IsPressed) { CursorVisible = false; @@ -163,7 +148,7 @@ namespace Examples.Tests } } - static void DrawJoysticks(Graphics gfx, IList joysticks, int line) + static int DrawJoysticks(Graphics gfx, IList joysticks, int line) { float space = gfx.MeasureString(" ", TextFont).Width; @@ -192,54 +177,45 @@ namespace Examples.Tests line++; } + + return line; } protected override void OnUpdateFrame(FrameEventArgs e) - {; + { InputDriver.Poll(); mouse = OpenTK.Input.Mouse.GetState(); - if (mouse != mouse_old) - refresh_text = true; - mouse_old = mouse; - keyboard = OpenTK.Input.Keyboard.GetState(); - if (keyboard != keyboard_old) - refresh_text = true; - keyboard_old = keyboard; - if (refresh_text) + using (Graphics gfx = Graphics.FromImage(TextBitmap)) { - refresh_text = false; - - using (Graphics gfx = Graphics.FromImage(TextBitmap)) - { - int line = 0; + int line = 0; - gfx.Clear(Color.Black); - gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + gfx.Clear(Color.Black); + gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; - DrawString(gfx, GL.GetString(StringName.Vendor), line++); - DrawString(gfx, GL.GetString(StringName.Version), line++); - DrawString(gfx, GL.GetString(StringName.Renderer), line++); - DrawString(gfx, Context.GraphicsMode.ToString(), line++); + DrawString(gfx, GL.GetString(StringName.Vendor), line++); + DrawString(gfx, GL.GetString(StringName.Version), line++); + DrawString(gfx, GL.GetString(StringName.Renderer), line++); + DrawString(gfx, Context.GraphicsMode.ToString(), line++); - DrawString(gfx, String.Format("[1 - 4]: change WindowState (current: {0}).", this.WindowState), line++); - DrawString(gfx, String.Format("[5 - 7]: change WindowBorder (current: {0}).", this.WindowBorder), line++); - DrawString(gfx, String.Format("Focused: {0}.", this.Focused), line++); - DrawString(gfx, String.Format("Mouse {0} window.", mouse_in_window ? "inside" : "outside of"), line++); - DrawString(gfx, String.Format("Mouse visible: {0}", CursorVisible), line++); - DrawString(gfx, String.Format("Mouse position (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)), line++); - DrawString(gfx, String.Format("Mouse position (relative): {0}", new Vector3(mouse.X, mouse.Y, mouse.WheelPrecise)), line++); - DrawString(gfx, String.Format("Window.Bounds: {0}", Bounds), line++); - DrawString(gfx, String.Format("Window.Location: {0}, Size: {1}", Location, Size), line++); - DrawString(gfx, String.Format("Window: {{X={0},Y={1},Width={2},Height={3}}}", X, Y, Width, Height), line++); - DrawString(gfx, String.Format("Window.ClientRectangle: {0}", ClientRectangle), line++); - DrawString(gfx, TypedText.ToString(), line++); - DrawKeyboard(gfx, keyboard, line++); - DrawMouse(gfx, mouse, line++); - DrawJoysticks(gfx, Joysticks, line++); - } + DrawString(gfx, String.Format("[1 - 4]: change WindowState (current: {0}).", this.WindowState), line++); + DrawString(gfx, String.Format("[5 - 7]: change WindowBorder (current: {0}).", this.WindowBorder), line++); + DrawString(gfx, String.Format("Focused: {0}.", this.Focused), line++); + DrawString(gfx, String.Format("Mouse {0} window.", mouse_in_window ? "inside" : "outside of"), line++); + DrawString(gfx, String.Format("Mouse visible: {0}", CursorVisible), line++); + DrawString(gfx, String.Format("Mouse position (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.Wheel)), line++); + DrawString(gfx, String.Format("Mouse position (relative): {0}", new Vector3(mouse.X, mouse.Y, mouse.WheelPrecise)), line++); + DrawString(gfx, String.Format("Window.Bounds: {0}", Bounds), line++); + DrawString(gfx, String.Format("Window.Location: {0}, Size: {1}", Location, Size), line++); + DrawString(gfx, String.Format("Window: {{X={0},Y={1},Width={2},Height={3}}}", X, Y, Width, Height), line++); + DrawString(gfx, String.Format("Window.ClientRectangle: {0}", ClientRectangle), line++); + DrawString(gfx, TypedText.ToString(), line++); + DrawKeyboard(gfx, keyboard, line++); + DrawMouse(gfx, mouse, line++); + line = DrawJoysticks(gfx, Joysticks, line++); + line = DrawGamePads(gfx, line++); } System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits( @@ -249,7 +225,36 @@ namespace Examples.Tests PixelType.UnsignedByte, data.Scan0); TextBitmap.UnlockBits(data); } - + + int DrawGamePads(Graphics gfx, int line) + { + line++; + DrawString(gfx, "GamePads:", line++); + for (int i = 0; i < 4; i++) + { + GamePadCapabilities caps = GamePad.GetCapabilities(i); + GamePadState state = GamePad.GetState(i); + if (state.IsConnected) + { + DrawString(gfx, caps.ToString(), line++); + DrawString(gfx, state.ToString(), line++); + } + } + line++; + DrawString(gfx, "Joysticks:", line++); + for (int i = 0; i < 4; i++) + { + JoystickCapabilities caps = Joystick.GetCapabilities(i); + JoystickState state = Joystick.GetState(i); + if (state.IsConnected) + { + DrawString(gfx, caps.ToString(), line++); + DrawString(gfx, state.ToString(), line++); + } + } + + return line; + } protected override void OnLoad(EventArgs e) { diff --git a/Source/OpenTK/Configuration.cs b/Source/OpenTK/Configuration.cs index 98aad59d..fcae8212 100644 --- a/Source/OpenTK/Configuration.cs +++ b/Source/OpenTK/Configuration.cs @@ -206,8 +206,8 @@ namespace OpenTK { if (!OpenTK.Platform.SDL2.SDL.WasInit(0)) { - var flags = OpenTK.Platform.SDL2.SystemFlags.EVERYTHING; - flags &= ~OpenTK.Platform.SDL2.SystemFlags.AUDIO; + var flags = + OpenTK.Platform.SDL2.SystemFlags.VIDEO | Platform.SDL2.SystemFlags.TIMER; if (OpenTK.Platform.SDL2.SDL.Init(flags) == 0) { supported = true; diff --git a/Source/OpenTK/Input/Buttons.cs b/Source/OpenTK/Input/Buttons.cs new file mode 100644 index 00000000..319e2183 --- /dev/null +++ b/Source/OpenTK/Input/Buttons.cs @@ -0,0 +1,166 @@ +// +// GamePadButton.cs +// +// Author: +// robert <${AuthorEmail}> +// +// Copyright (c) 2012 robert +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace OpenTK.Input +{ + /// + /// Enumerates available buttons for a GamePad device. + /// + [Flags] + public enum Buttons + { + /// + /// DPad up direction button + /// + DPadUp = 1 << 0, + + /// + /// DPad down direction button + /// + DPadDown = 1 << 1, + + /// + /// DPad left direction button + /// + DPadLeft = 1 << 2, + + /// + /// DPad right direction button + /// + DPadRight = 1 << 3, + + /// + /// Start button + /// + Start = 1 << 4, + + /// + /// Back button + /// + Back = 1 << 5, + + /// + /// Left stick button + /// + LeftStick = 1 << 6, + + /// + /// Right stick button + /// + RightStick = 1 << 7, + + /// + /// Left shoulder button + /// + LeftShoulder = 1 << 8, + + /// + /// Right shoulder button + /// + RightShoulder = 1 << 9, + + /// + /// Home button + /// + Home = 1 << 11, + + /// + /// Home button + /// + BigButton = Home, + + /// + /// A button + /// + A = 1 << 12, + + /// + /// B button + /// + B = 1 << 13, + + /// + /// X button + /// + X = 1 << 14, + + /// + /// Y button + /// + Y = 1 << 15, + + /// + /// Left thumbstick left direction button + /// + LeftThumbstickLeft = 1 << 21, + + /// + /// Right trigger button + /// + RightTrigger = 1 << 22, + + /// + /// Left trigger button + /// + LeftTrigger = 1 << 23, + + /// + /// Right thumbstick up direction button + /// + RightThumbstickUp = 1 << 24, + + /// + /// Right thumbstick down direction button + /// + RightThumbstickDown = 1 << 25, + + /// + /// Right stick right direction button + /// + RightThumbstickRight = 1 << 26, + + /// + /// Right stick left direction button + /// + RightThumbstickLeft = 1 << 27, + + /// + /// Left stick up direction button + /// + LeftThumbstickUp = 1 << 28, + + /// + /// Left stick down direction button + /// + LeftThumbstickDown = 1 << 29, + + /// + /// Left stick right direction button + /// + LeftThumbstickRight = 1 << 30, + } +} diff --git a/Source/OpenTK/Input/ConfigurationType.cs b/Source/OpenTK/Input/ConfigurationType.cs new file mode 100644 index 00000000..0cbb649a --- /dev/null +++ b/Source/OpenTK/Input/ConfigurationType.cs @@ -0,0 +1,41 @@ +#region License +// +// ConfigurationType.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + enum ConfigurationType + { + Unmapped = 0, + Axis, + Button + } +} + diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index 303f4516..7578eb23 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -1,46 +1,96 @@ - #region License - // - // The Open Toolkit Library License - // - // Copyright (c) 2006 - 2009 the Open Toolkit library. - // - // Permission is hereby granted, free of charge, to any person obtaining a copy - // of this software and associated documentation files (the "Software"), to deal - // in the Software without restriction, including without limitation the rights to - // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - // the Software, and to permit persons to whom the Software is furnished to do - // so, subject to the following conditions: - // - // The above copyright notice and this permission notice shall be included in all - // copies or substantial portions of the Software. - // - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - // OTHER DEALINGS IN THE SOFTWARE. - // - #endregion +#region License +// +// GamePad.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion using System; namespace OpenTK.Input { /// - /// Provides access to GamePad devices. Note: this API is not implemented yet. + /// Provides access to GamePad devices. + /// A GamePad device offers a well-defined layout with + /// one direction-pad, two thumbsticks, two triggers, + /// four main buttons (A, B, X, Y) and up to seven + /// auxilliary buttons. + /// Use GetCapabilities to retrieve the exact + /// capabilities of a given device. + /// Use GetState to retrieve the current state + /// of a given device. /// - public class GamePad + public sealed class GamePad { - #region Constructors + internal const int MaxAxisCount = 10; + internal const int MaxDPadCount = 2; - static GamePad() + static readonly IGamePadDriver driver = + Platform.Factory.Default.CreateGamePadDriver(); + + private GamePad() { } + + /// + /// Retrieves a GamePadCapabilities structure describing the + /// capabilities of a gamepad device. + /// + /// The zero-based index of a gamepad device. + /// A GamePadCapabilities structure describing the capabilities of the gamepad device. + public static GamePadCapabilities GetCapabilities(int index) { - throw new NotImplementedException(); + if (index < 0) + throw new IndexOutOfRangeException(); + + return driver.GetCapabilities(index); } - #endregion + /// + /// Retrieves the GamePadState for the specified gamepad device. + /// + /// The zero-based index of a gamepad device. + /// A GamePadState structure describing the state of the gamepad device. + public static GamePadState GetState(int index) + { + return driver.GetState(index); + } + + /// + /// Sets the vibration intensity for the left and right motors of this + /// + /// + /// true, if vibration was set, false otherwise. This method can return false + /// if the GamePad hardware does not support vibration or if it cannot respond to + /// the command for any reason. Do not loop until this becomes true, but rather ignore + /// a return value of false. + /// + /// A zero-based device index for the GamePad device to affect + /// The vibration intensity for the left motor, between 0.0 and 1.0. + /// The vibration intensity for the right motor, between 0.0 and 1.0. + public static bool SetVibration(int index, float left, float right) + { + return driver.SetVibration(index, left, right); + } } } diff --git a/Source/OpenTK/Input/GamePadAxis.cs b/Source/OpenTK/Input/GamePadAxes.cs similarity index 57% rename from Source/OpenTK/Input/GamePadAxis.cs rename to Source/OpenTK/Input/GamePadAxes.cs index c1c96c53..3ddea306 100644 --- a/Source/OpenTK/Input/GamePadAxis.cs +++ b/Source/OpenTK/Input/GamePadAxes.cs @@ -25,32 +25,17 @@ // THE SOFTWARE. using System; -namespace OpenTK +namespace OpenTK.Input { - public enum GamePadAxis - { - /// The first axis of the gamepad. - Axis0 = 0, - /// The second axis of the gamepad. - Axis1, - /// The third axis of the gamepad. - Axis2, - /// The fourth axis of the gamepad. - Axis3, - /// The fifth axis of the gamepad. - Axis4, - /// The sixth axis of the gamepad. - Axis5, - /// The seventh axis of the gamepad. - Axis6, - /// The eighth axis of the gamepad. - Axis7, - /// The ninth axis of the gamepad. - Axis8, - /// The tenth axis of the gamepad. - Axis9, - /// The last axis of the gamepad. - LastAxis - } + [Flags] + internal enum GamePadAxes : byte + { + LeftX = 1 << 0, + LeftY = 1 << 1, + LeftTrigger = 1 << 2, + RightX = 1 << 3, + RightY = 1 << 4, + RightTrigger = 1 << 5, + } } diff --git a/Source/OpenTK/Input/GamePadButton.cs b/Source/OpenTK/Input/GamePadButton.cs deleted file mode 100644 index f4f543ad..00000000 --- a/Source/OpenTK/Input/GamePadButton.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// GamePadButton.cs -// -// Author: -// robert <${AuthorEmail}> -// -// Copyright (c) 2012 robert -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; - -namespace OpenTK -{ - public enum GamePadButton - { - /// The first button of the gamepad. - Button0 = 0, - /// The second button of the gamepad. - Button1, - /// The third button of the gamepad. - Button2, - /// The fourth button of the gamepad. - Button3, - /// The fifth button of the gamepad. - Button4, - /// The sixth button of the gamepad. - Button5, - /// The seventh button of the gamepad. - Button6, - /// The eighth button of the gamepad. - Button7, - /// The ninth button of the gamepad. - Button8, - /// The tenth button of the gamepad. - Button9, - /// The eleventh button of the gamepad. - Button10, - /// The twelfth button of the gamepad. - Button11, - /// The thirteenth button of the gamepad. - Button12, - /// The fourteenth button of the gamepad. - Button13, - /// The fifteenth button of the gamepad. - Button14, - /// The sixteenth button of the gamepad. - Button15, - /// The last button of the gamepad. - LastButton - } -} - diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs new file mode 100644 index 00000000..676fb1f9 --- /dev/null +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -0,0 +1,216 @@ +#region License +// +// GamePadButtons.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + /// + /// Describes the of . + /// + public struct GamePadButtons : IEquatable + { + Buttons buttons; + + /// + /// Initializes a new instance of the structure. + /// + /// A bitmask containing the button state. + public GamePadButtons(Buttons state) + { + buttons = state; + } + + #region Public Members + + /// + /// Gets the for the A button. + /// + public ButtonState A + { + get { return GetButton(Buttons.A); } + } + + /// + /// Gets the for the B button. + /// + public ButtonState B + { + get { return GetButton(Buttons.B); } + } + + /// + /// Gets the for the X button. + /// + public ButtonState X + { + get { return GetButton(Buttons.X); } + } + + /// + /// Gets the for the Y button. + /// + public ButtonState Y + { + get { return GetButton(Buttons.Y); } + } + + /// + /// Gets the for the Back button. + /// + public ButtonState Back + { + get { return GetButton(Buttons.Back); } + } + + /// + /// Gets the for the big button. + /// This button is also known as Home or Guide. + /// + public ButtonState BigButton + { + get { return GetButton(Buttons.BigButton); } + } + + /// + /// Gets the for the left shoulder button. + /// + public ButtonState LeftShoulder + { + get { return GetButton(Buttons.LeftShoulder); } + } + + /// + /// Gets the for the left stick button. + /// This button represents a left stick that is pressed in. + /// + public ButtonState LeftStick + { + get { return GetButton(Buttons.LeftStick); } + } + + /// + /// Gets the for the right shoulder button. + /// + public ButtonState RightShoulder + { + get { return GetButton(Buttons.RightShoulder); } + } + + /// + /// Gets the for the right stick button. + /// This button represents a right stick that is pressed in. + /// + public ButtonState RightStick + { + get { return GetButton(Buttons.RightStick); } + } + + /// + /// Gets the for the starth button. + /// + public ButtonState Start + { + get { return GetButton(Buttons.Start); } + } + + /// A instance to test for equality. + /// A instance to test for equality. + public static bool operator ==(GamePadButtons left, GamePadButtons right) + { + return left.Equals(right); + } + + /// A instance to test for inequality. + /// A instance to test for inequality. + public static bool operator !=(GamePadButtons left, GamePadButtons right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return Convert.ToString((int)buttons, 2).PadLeft(10, '0'); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return buttons.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is GamePadButtons && + Equals((GamePadButtons)obj); + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(GamePadButtons other) + { + return buttons == other.buttons; + } + + #endregion + + #region Private Members + + ButtonState GetButton(Buttons b) + { + return (buttons & b) != 0 ? ButtonState.Pressed : ButtonState.Released; + } + + #endregion + } +} + diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs new file mode 100644 index 00000000..269081e8 --- /dev/null +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -0,0 +1,395 @@ +#region License +// +// GamePadCapabilities.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + /// + /// Describes the capabilities of a GamePad input device. + /// + public struct GamePadCapabilities : IEquatable + { + Buttons buttons; + GamePadAxes axes; + byte gamepad_type; + bool is_connected; + + #region Constructors + + internal GamePadCapabilities(GamePadType type, GamePadAxes axes, Buttons buttons, bool is_connected) + : this() + { + gamepad_type = (byte)type; + this.axes = axes; + this.buttons = buttons; + this.is_connected = is_connected; + } + + #endregion + + #region Public Members + + /// + /// Gets a value describing the type of a input device. + /// This value depends on the connected device and the drivers in use. If IsConnected + /// is false, then this value will be GamePadType.Unknown. + /// + /// The GamePadType of the connected input device. + public GamePadType GamePadType + { + get { return (GamePadType)gamepad_type; } + } + + /// + /// Gets a value describing whether this GamePad has + /// an up digital pad button. + /// + /// true if this instance has an up digital pad button; otherwise, false. + public bool HasDPadUpButton + { + get { return (buttons & Buttons.DPadUp) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a down digital pad button. + /// + /// true if this instance has a down digital pad button; otherwise, false. + public bool HasDPadDownButton + { + get { return (buttons & Buttons.DPadDown) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a left digital pad button. + /// + /// true if this instance has a left digital pad button; otherwise, false. + public bool HasDPadLeftButton + { + get { return (buttons & Buttons.DPadLeft) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right digital pad button. + /// + /// true if this instance has a right digital pad button; otherwise, false. + public bool HasDPadRightButton + { + get { return (buttons & Buttons.DPadRight) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// an A button. + /// + /// true if this instance has an A button; otherwise, false. + public bool HasAButton + { + get { return (buttons & Buttons.A) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a B button. + /// + /// true if this instance has a B button; otherwise, false. + public bool HasBButton + { + get { return (buttons & Buttons.B) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a X button. + /// + /// true if this instance has a X button; otherwise, false. + public bool HasXButton + { + get { return (buttons & Buttons.X) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a Y button. + /// + /// true if this instance has a Y button; otherwise, false. + public bool HasYButton + { + get { return (buttons & Buttons.Y) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a left stick button. + /// + /// true if this instance has a left stick button; otherwise, false. + public bool HasLeftStickButton + { + get { return (buttons & Buttons.LeftStick) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right stick button. + /// + /// true if this instance has a right stick button; otherwise, false. + public bool HasRightStickButton + { + get { return (buttons & Buttons.RightStick) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a left shoulder button. + /// + /// true if this instance has a left shoulder button; otherwise, false. + public bool HasLeftShoulderButton + { + get { return (buttons & Buttons.LeftShoulder) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right shoulder button. + /// + /// true if this instance has a right shoulder button; otherwise, false. + public bool HasRightShoulderButton + { + get { return (buttons & Buttons.RightShoulder) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a back button. + /// + /// true if this instance has a back button; otherwise, false. + public bool HasBackButton + { + get { return (buttons & Buttons.Back) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a big button. (also known as "guide" or "home" button). + /// + /// true if this instance has a big button; otherwise, false. + public bool HasBigButton + { + get { return (buttons & Buttons.BigButton) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a start button. + /// + /// true if this instance has a start button; otherwise, false. + public bool HasStartButton + { + get { return (buttons & Buttons.Start) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a left thumbstick with a x-axis. + /// + /// true if this instance has a left thumbstick with a x-axis; otherwise, false. + public bool HasLeftXThumbStick + { + get { return (axes & GamePadAxes.LeftX) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a left thumbstick with a y-axis. + /// + /// true if this instance has a left thumbstick with a y-axis; otherwise, false. + public bool HasLeftYThumbStick + { + get { return (axes & GamePadAxes.LeftY) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right thumbstick with a x-axis. + /// + /// true if this instance has a right thumbstick with a x-axis; otherwise, false. + public bool HasRightXThumbStick + { + get { return (axes & GamePadAxes.RightX) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right thumbstick with a y-axis. + /// + /// true if this instance has a right thumbstick with a y-axis; otherwise, false. + public bool HasRightYThumbStick + { + get { return (axes & GamePadAxes.RightY) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a left trigger. + /// + /// true if this instance has a left trigger; otherwise, false. + public bool HasLeftTrigger + { + get { return (axes & GamePadAxes.LeftTrigger) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right trigger. + /// + /// true if this instance has a right trigger; otherwise, false. + public bool HasRightTrigger + { + get { return (axes & GamePadAxes.RightTrigger) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a low-frequency vibration motor. + /// + /// true if this instance has a low-frequency vibration motor; otherwise, false. + public bool HasLeftVibrationMotor + { + get { return false; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a high-frequency vibration motor. + /// + /// true if this instance has a high frequency vibration motor; otherwise, false. + public bool HasRightVibrationMotor + { + get { return false; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a microphone input. + /// + /// true if this instance has a microphone input; otherwise, false. + public bool HasVoiceSupport + { + get { return false; } + } + + /// + /// Gets a value describing whether this GamePad is + /// currently connected. + /// + /// true if this instance is currently connected; otherwise, false. + public bool IsConnected + { + get { return is_connected; } + } + + /// A structure to test for equality. + /// A structure to test for equality. + public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right) + { + return left.Equals(right); + } + + /// A structure to test for inequality. + /// A structure to test for inequality. + public static bool operator !=(GamePadCapabilities left, GamePadCapabilities right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return String.Format( + "{{Type: {0}; Axes: {1}; Buttons: {2}; Connected: {3}}}", + GamePadType, + Convert.ToString((int)axes, 2), + Convert.ToString((int)buttons, 2), + IsConnected); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return + buttons.GetHashCode() ^ + is_connected.GetHashCode() ^ + gamepad_type.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is GamePadCapabilities && + Equals((GamePadCapabilities)obj); + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(GamePadCapabilities other) + { + return + buttons == other.buttons && + is_connected == other.is_connected && + gamepad_type == other.gamepad_type; + } + + #endregion + } +} + diff --git a/Source/OpenTK/Input/GamePadConfiguration.cs b/Source/OpenTK/Input/GamePadConfiguration.cs new file mode 100644 index 00000000..54600e79 --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfiguration.cs @@ -0,0 +1,204 @@ +#region License +// +// GamePadMapping.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace OpenTK.Input +{ + sealed class GamePadConfiguration + { + static readonly char[] ConfigurationSeparator = new char[] { ',' }; + + Guid guid; + string name; + readonly List configuration_items = + new List(); + + public Guid Guid + { + get { return guid; } + private set { guid = value; } + } + + public string Name + { + get { return name; } + private set { name = value; } + } + + public GamePadConfiguration(string configuration) + { + ParseConfiguration(configuration); + } + + public List.Enumerator GetEnumerator() + { + return configuration_items.GetEnumerator(); + } + + #region Private Members + + // Parses a GamePad configuration string. The string + // follows the rules for SDL2 GameController, outlined here: + // http://wiki.libsdl.org/SDL_GameControllerAddMapping + void ParseConfiguration(string configuration) + { + if (String.IsNullOrEmpty(configuration)) + { + throw new ArgumentNullException(); + } + + // The mapping string has the format "GUID,name,config" + // - GUID is a unigue identifier returned by Joystick.GetGuid() + // - name is a human-readable name for the controller + // - config is a comma-separated list of configurations as follows: + // - [gamepad axis or button name]:[joystick axis, button or hat number] + string[] items = configuration.Split(ConfigurationSeparator, StringSplitOptions.RemoveEmptyEntries); + if (items.Length < 3) + { + throw new ArgumentException(); + } + + GamePadConfiguration map = this; + map.Guid = new Guid(items[0]); + map.Name = items[1]; + for (int i = 2; i < items.Length; i++) + { + string[] config = items[i].Split(':'); + GamePadConfigurationTarget target = ParseTarget(config[0]); + GamePadConfigurationSource source = ParseSource(config[1]); + configuration_items.Add(new GamePadConfigurationItem(source, target)); + } + } + + static GamePadConfigurationTarget ParseTarget(string target) + { + switch (target) + { + // Buttons + case "a": + return new GamePadConfigurationTarget(Buttons.A); + case "b": + return new GamePadConfigurationTarget(Buttons.B); + case "x": + return new GamePadConfigurationTarget(Buttons.X); + case "y": + return new GamePadConfigurationTarget(Buttons.Y); + case "start": + return new GamePadConfigurationTarget(Buttons.Start); + case "back": + return new GamePadConfigurationTarget(Buttons.Back); + case "guide": + return new GamePadConfigurationTarget(Buttons.BigButton); + case "leftshoulder": + return new GamePadConfigurationTarget(Buttons.LeftShoulder); + case "rightshoulder": + return new GamePadConfigurationTarget(Buttons.RightShoulder); + case "leftstick": + return new GamePadConfigurationTarget(Buttons.LeftStick); + case "rightstick": + return new GamePadConfigurationTarget(Buttons.RightStick); + case "dpup": + return new GamePadConfigurationTarget(Buttons.DPadUp); + case "dpdown": + return new GamePadConfigurationTarget(Buttons.DPadDown); + case "dpleft": + return new GamePadConfigurationTarget(Buttons.DPadLeft); + case "dpright": + return new GamePadConfigurationTarget(Buttons.DPadRight); + + // Axes + case "leftx": + return new GamePadConfigurationTarget(GamePadAxes.LeftX); + case "lefty": + return new GamePadConfigurationTarget(GamePadAxes.LeftY); + case "rightx": + return new GamePadConfigurationTarget(GamePadAxes.RightX); + case "righty": + return new GamePadConfigurationTarget(GamePadAxes.RightY); + + // Triggers + case "lefttrigger": + return new GamePadConfigurationTarget(GamePadAxes.LeftTrigger); + case "righttrigger": + return new GamePadConfigurationTarget(GamePadAxes.RightTrigger); + + + // Unmapped + default: + return new GamePadConfigurationTarget(); + } + } + + static GamePadConfigurationSource ParseSource(string item) + { + if (String.IsNullOrEmpty(item)) + { + return new GamePadConfigurationSource(); + } + + switch (item[0]) + { + case 'a': + return new GamePadConfigurationSource(ParseAxis(item)); + + case 'b': + return new GamePadConfigurationSource(ParseButton(item)); + + case 'h': + throw new NotImplementedException(); + //return new MapItem(ParseHat(item)); + + default: + throw new InvalidOperationException("[Input] Invalid GamePad configuration value"); + } + } + + static JoystickAxis ParseAxis(string item) + { + // item is in the format "a#" where # a zero-based integer number + JoystickAxis axis = JoystickAxis.Axis0; + int id = Int32.Parse(item.Substring(1)); + return axis + id; + } + + static JoystickButton ParseButton(string item) + { + // item is in the format "b#" where # a zero-based integer nubmer + JoystickButton button = JoystickButton.Button0; + int id = Int32.Parse(item.Substring(1)); + return button + id; + } + + #endregion + } +} diff --git a/Source/OpenTK/Input/GamePadConfigurationDatabase.cs b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs new file mode 100644 index 00000000..06d3b12e --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs @@ -0,0 +1,132 @@ +#region License +// +// GamePadConfigurationDatabase.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; + +namespace OpenTK.Input +{ + class GamePadConfigurationDatabase + { + readonly Dictionary Configurations = new Dictionary(); + + internal GamePadConfigurationDatabase() + { + // Configuration database copied from SDL + + #region License + // Simple DirectMedia Layer + // Copyright (C) 1997-2013 Sam Lantinga + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + #endregion + + // Default configuration, used when no suitable configuration is available. + // Add("00000000000000000000000000000000,Unknown Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + // Valid for all xinput devices on Windows: + Add("00000000000000000000000000000000,X360 Controller,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,"); + + // Windows + Add("341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); + Add("6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); // Guide button doesn't seem to be sent in DInput mode. + Add("88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,"); + Add("4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); + Add("25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,"); + + // Mac OS X + Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); + Add("6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* Guide button doesn't seem to be sent in DInput mode. */ + Add("6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,"); + Add("6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* This includes F710 in DInput mode and the "Logitech Cordless RumblePad 2"); at the very least. */ + Add("4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); + Add("5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,"); + + // Linux + Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); + Add("03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,"); + Add("030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* Guide button doesn't seem to be sent in DInput mode. */ + Add("030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); + Add("03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + } + + internal void Add(string config) + { + Guid guid = new Guid(config.Substring(0, 32)); + if (!Configurations.ContainsKey(guid)) + { + Configurations.Add(guid, config); + } + else + { + Configurations[guid] = config; + } + } + + internal string this[Guid guid] + { + get + { + if (Configurations.ContainsKey(guid)) + { + return Configurations[guid]; + } + else + { + return Configurations[new Guid()]; // default configuration + } + } + } + } +} + diff --git a/Source/OpenTK/Input/GamePadConfigurationItem.cs b/Source/OpenTK/Input/GamePadConfigurationItem.cs new file mode 100644 index 00000000..c2dba9f5 --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationItem.cs @@ -0,0 +1,58 @@ +#region License +// +// GamePadConfigurationItem.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + class GamePadConfigurationItem + { + GamePadConfigurationSource source; + GamePadConfigurationTarget target; + + public GamePadConfigurationItem(GamePadConfigurationSource source, GamePadConfigurationTarget target) + { + Source = source; + Target = target; + } + + public GamePadConfigurationSource Source + { + get { return source; } + private set { source = value; } + } + + public GamePadConfigurationTarget Target + { + get { return target; } + private set { target = value; } + } + } +} + diff --git a/Source/OpenTK/Input/GamePadConfigurationSource.cs b/Source/OpenTK/Input/GamePadConfigurationSource.cs new file mode 100644 index 00000000..c1b25b82 --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationSource.cs @@ -0,0 +1,72 @@ +#region License +// +// GamePadConfigurationItem.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + struct GamePadConfigurationSource + { + ConfigurationType map_type; + Nullable map_button; + Nullable map_axis; + + public GamePadConfigurationSource(JoystickAxis axis) + : this() + { + Type = ConfigurationType.Axis; + Axis = axis; + } + + public GamePadConfigurationSource(JoystickButton button) + : this() + { + Type = ConfigurationType.Button; + Button = button; + } + + public ConfigurationType Type + { + get { return map_type; } + private set { map_type = value; } + } + + public JoystickAxis Axis + { + get { return map_axis.Value; } + private set { map_axis = value; } + } + + public JoystickButton Button + { + get { return map_button.Value; } + private set { map_button = value; } + } + } +} diff --git a/Source/OpenTK/Input/GamePadConfigurationTarget.cs b/Source/OpenTK/Input/GamePadConfigurationTarget.cs new file mode 100644 index 00000000..75199c9f --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationTarget.cs @@ -0,0 +1,72 @@ +#region License +// +// GamePadConfigurationTarget.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + struct GamePadConfigurationTarget + { + ConfigurationType map_type; + Nullable map_button; + Nullable map_axis; + + public GamePadConfigurationTarget(Buttons button) + : this() + { + Type = ConfigurationType.Button; + map_button = button; + } + + public GamePadConfigurationTarget(GamePadAxes axis) + : this() + { + Type = ConfigurationType.Axis; + map_axis = axis; + } + + public ConfigurationType Type + { + get { return map_type; } + private set { map_type = value; } + } + + public GamePadAxes Axis + { + get { return map_axis.Value; } + private set { map_axis = value; } + } + + public Buttons Button + { + get { return map_button.Value; } + private set { map_button = value; } + } + } +} diff --git a/Source/OpenTK/Input/GamePadDPad.cs b/Source/OpenTK/Input/GamePadDPad.cs new file mode 100644 index 00000000..d05300ef --- /dev/null +++ b/Source/OpenTK/Input/GamePadDPad.cs @@ -0,0 +1,223 @@ +// #region License +// +// GamePadDPad.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// #endregion + +using System; + +namespace OpenTK.Input +{ + /// + /// Describes the state of a directional pad. + /// + public struct GamePadDPad : IEquatable + { + [Flags] + enum DPadButtons : byte + { + Up = Buttons.DPadUp, + Down = Buttons.DPadDown, + Left = Buttons.DPadLeft, + Right = Buttons.DPadRight + } + + DPadButtons buttons; + + #region Internal Members + + internal GamePadDPad(Buttons state) + { + // DPad butons are stored in the lower 4bits + // of the Buttons enumeration. + buttons = (DPadButtons)((int)state & 0x0f); + } + + #endregion + + #region Public Members + + /// + /// Gets the for the up button. + /// + /// ButtonState.Pressed if the up button is pressed; otherwise, ButtonState.Released. + public ButtonState Up + { + get { return IsUp ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the for the down button. + /// + /// ButtonState.Pressed if the down button is pressed; otherwise, ButtonState.Released. + public ButtonState Down + { + get { return IsDown ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the for the left button. + /// + /// ButtonState.Pressed if the left button is pressed; otherwise, ButtonState.Released. + public ButtonState Left + { + get { return IsLeft ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the for the right button. + /// + /// ButtonState.Pressed if the right button is pressed; otherwise, ButtonState.Released. + public ButtonState Right + { + get { return IsRight ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets a value indicating whether the up button is pressed. + /// + /// true if the up button is pressed; otherwise, false. + public bool IsUp + { + get { return (buttons & DPadButtons.Up) != 0; } + internal set { SetButton(DPadButtons.Up, value); } + } + + /// + /// Gets a value indicating whether the down button is pressed. + /// + /// true if the down button is pressed; otherwise, false. + public bool IsDown + { + get { return (buttons & DPadButtons.Down) != 0; } + internal set { SetButton(DPadButtons.Down, value); } + } + + /// + /// Gets a value indicating whether the left button is pressed. + /// + /// true if the left button is pressed; otherwise, false. + public bool IsLeft + { + get { return (buttons & DPadButtons.Left) != 0; } + internal set { SetButton(DPadButtons.Left, value); } + } + + /// + /// Gets a value indicating whether the right button is pressed. + /// + /// true if the right button is pressed; otherwise, false. + public bool IsRight + { + get { return (buttons & DPadButtons.Right) != 0; } + internal set { SetButton(DPadButtons.Right, value); } + } + + /// A instance to test for equality. + /// A instance to test for equality. + public static bool operator ==(GamePadDPad left, GamePadDPad right) + { + return left.Equals(right); + } + + /// A instance to test for inequality. + /// A instance to test for inequality. + public static bool operator !=(GamePadDPad left, GamePadDPad right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return String.Format( + "{{{0}{1}{2}{3}}}", + IsUp ? "U" : String.Empty, + IsLeft ? "L" : String.Empty, + IsDown ? "D" : String.Empty, + IsRight ? "R" : String.Empty); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return buttons.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is GamePadDPad && + Equals((GamePadDPad)obj); + } + + #endregion + + #region Private Members + + void SetButton(DPadButtons button, bool value) + { + if (value) + { + buttons |= button; + } + else + { + buttons &= ~button; + } + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(GamePadDPad other) + { + return buttons == other.buttons; + } + + #endregion + } +} diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index 21e3aa91..7d0f0630 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -30,10 +30,216 @@ using System; namespace OpenTK.Input { /// - /// Encapsulates the state of a GamePad device. + /// Describes the current state of a device. /// - public struct GamePadState /*: IEquatable*/ + public struct GamePadState : IEquatable { + const float RangeMultiplier = 1.0f / (short.MaxValue + 1); + Buttons buttons; + int packet_number; + short left_stick_x; + short left_stick_y; + short right_stick_x; + short right_stick_y; + byte left_trigger; + byte right_trigger; + bool is_connected; + + #region Public Members + + /// + /// Gets a structure describing the + /// state of the GamePad thumb sticks. + /// + public GamePadThumbSticks ThumbSticks + { + get { return new GamePadThumbSticks(left_stick_x, left_stick_y, right_stick_x, right_stick_y); } + } + + /// + /// Gets a structure describing the + /// state of the GamePad buttons. + /// + public GamePadButtons Buttons + { + get { return new GamePadButtons(buttons); } + } + + /// + /// Gets a structure describing the + /// state of the GamePad directional pad. + /// + public GamePadDPad DPad + { + get { return new GamePadDPad(buttons); } + } + + /// + /// Gets a structure describing the + /// state of the GamePad triggers. + /// + public GamePadTriggers Triggers + { + get { return new GamePadTriggers(left_trigger, right_trigger); } + } + + /// + /// Gets a value indicating whether this GamePad instance is connected. + /// + /// true if this instance is connected; otherwise, false. + public bool IsConnected + { + get { return is_connected; } + } + + /// + /// Gets the packet number for this GamePadState instance. + /// Use the packet number to determine whether the state of a + /// GamePad device has changed. + /// + public int PacketNumber + { + get { return packet_number; } + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return String.Format( + "{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}}}", + ThumbSticks, Buttons, DPad, IsConnected); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return + ThumbSticks.GetHashCode() ^ Buttons.GetHashCode() ^ + DPad.GetHashCode() ^ IsConnected.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is GamePadState && + Equals((GamePadState)obj); + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(GamePadState other) + { + return + ThumbSticks == other.ThumbSticks && + Buttons == other.Buttons && + DPad == other.DPad && + IsConnected == other.IsConnected; + } + + #endregion + + #region Internal Members + + internal void SetAxis(GamePadAxes axis, short value) + { + if ((axis & GamePadAxes.LeftX) != 0) + { + left_stick_x = value; + } + + if ((axis & GamePadAxes.LeftY) != 0) + { + left_stick_y = value; + } + + if ((axis & GamePadAxes.RightX) != 0) + { + right_stick_x = value; + } + + if ((axis & GamePadAxes.RightY) != 0) + { + right_stick_y = value; + } + + if ((axis & GamePadAxes.LeftTrigger) != 0) + { + // Adjust from [-32768, 32767] to [0, 255] + left_trigger = (byte)((value - short.MinValue) >> 8); + } + + if ((axis & GamePadAxes.RightTrigger) != 0) + { + // Adjust from [-32768, 32767] to [0, 255] + right_trigger = (byte)((value - short.MinValue) >> 8); + } + } + + internal void SetButton(Buttons button, bool pressed) + { + if (pressed) + { + buttons |= button; + } + else + { + buttons &= ~button; + } + } + + internal void SetConnected(bool connected) + { + is_connected = connected; + } + + internal void SetTriggers(byte left, byte right) + { + left_trigger = left; + right_trigger = right; + } + + internal void SetPacketNumber(int number) + { + packet_number = number; + } + + #endregion + + #region Private Members + + bool IsAxisValid(GamePadAxes axis) + { + int index = (int)axis; + return index >= 0 && index < GamePad.MaxAxisCount; + } + + bool IsDPadValid(int index) + { + return index >= 0 && index < GamePad.MaxDPadCount; + } + + #endregion } } diff --git a/Source/OpenTK/Input/GamePadThumbSticks.cs b/Source/OpenTK/Input/GamePadThumbSticks.cs new file mode 100644 index 00000000..42fd2c0f --- /dev/null +++ b/Source/OpenTK/Input/GamePadThumbSticks.cs @@ -0,0 +1,142 @@ +#region License +// +// GamePadThumbSticks.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + /// + /// Describes the current thumb stick state of a device + /// + public struct GamePadThumbSticks : IEquatable + { + const float ConversionFactor = 1.0f / short.MaxValue; + short left_x, left_y; + short right_x, right_y; + + internal GamePadThumbSticks( + short left_x, short left_y, + short right_x, short right_y) + { + this.left_x = left_x; + this.left_y = left_y; + this.right_x = right_x; + this.right_y = right_y; + } + + #region Public Members + + /// + /// Gets a describing the state of the left thumb stick. + /// + public Vector2 Left + { + get { return new Vector2(left_x * ConversionFactor, left_y * ConversionFactor); } + } + + /// + /// Gets a describing the state of the right thumb stick. + /// + public Vector2 Right + { + get { return new Vector2(right_x * ConversionFactor, right_y * ConversionFactor); } + } + + /// A instance to test for equality. + /// A instance to test for equality. + public static bool operator ==(GamePadThumbSticks left, GamePadThumbSticks right) + { + return left.Equals(right); + } + + /// A instance to test for inequality. + /// A instance to test for inequality. + public static bool operator !=(GamePadThumbSticks left, GamePadThumbSticks right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return String.Format( + "{{Left: ({0:f4}; {1:f4}); Right: ({2:f4}; {3:f4})}}", + Left.X, Left.Y, Right.X, Right.Y); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return + left_x.GetHashCode() ^ left_y.GetHashCode() ^ + right_x.GetHashCode() ^ right_y.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is GamePadThumbSticks && + Equals((GamePadThumbSticks)obj); + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(GamePadThumbSticks other) + { + return + left_x == other.left_x && + left_y == other.left_y && + right_x == other.right_x && + right_y == other.right_y; + } + + #endregion + } +} diff --git a/Source/OpenTK/Input/GamePadTriggers.cs b/Source/OpenTK/Input/GamePadTriggers.cs new file mode 100644 index 00000000..df9df1a7 --- /dev/null +++ b/Source/OpenTK/Input/GamePadTriggers.cs @@ -0,0 +1,136 @@ +// #region License +// +// GamePadTriggers.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// #endregion + + +using System; + +namespace OpenTK.Input +{ + /// + /// Describes the state of a trigger buttons. + /// + public struct GamePadTriggers : IEquatable + { + const float ConversionFactor = 1.0f / short.MaxValue; + short left; + short right; + + internal GamePadTriggers(short left, short right) + { + this.left = left; + this.right = right; + } + + #region Public Members + + /// + /// Gets the offset of the left trigger button, between 0.0 and 1.0. + /// + public float Left + { + get { return left * ConversionFactor; } + } + + /// + /// Gets the offset of the left trigger button, between 0.0 and 1.0. + /// + public float Right + { + get { return right * ConversionFactor; } + } + + /// A instance to test for equality. + /// A instance to test for equality. + public static bool operator ==(GamePadTriggers left, GamePadTriggers right) + { + return left.Equals(right); + } + + /// A instance to test for equality. + /// A instance to test for equality. + public static bool operator !=(GamePadTriggers left, GamePadTriggers right) + { + return !left.Equals(right); + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return String.Format( + "{{Left: {0:f2}; Right: {1:f2}}}", + Left, Right); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return + left.GetHashCode() ^ right.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is GamePadTriggers && + Equals((GamePadTriggers)obj); + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(GamePadTriggers other) + { + return + left == other.left && + right == other.right; + } + + #endregion + } +} diff --git a/Source/OpenTK/Input/GamePadType.cs b/Source/OpenTK/Input/GamePadType.cs new file mode 100644 index 00000000..1464117f --- /dev/null +++ b/Source/OpenTK/Input/GamePadType.cs @@ -0,0 +1,97 @@ +#region License +// +// GamePadType.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +namespace OpenTK.Input +{ + /// + /// Enumerates available types. + /// + public enum GamePadType + { + /// + /// The GamePad is of an unknown type. + /// + Unknown = 0, + + /// + /// The GamePad is an arcade stick. + /// + ArcadeStick, + + /// + /// The GamePad is a dance pad. + /// + DancePad, + + /// + /// The GamePad is a flight stick. + /// + FlightStick, + + /// + /// The GamePad is a guitar. + /// + Guitar, + + /// + /// The GamePad is a driving wheel. + /// + Wheel, + + /// + /// The GamePad is an alternate guitar. + /// + AlternateGuitar, + + /// + /// The GamePad is a big button pad. + /// + BigButtonPad, + + /// + /// The GamePad is a drum kit. + /// + DrumKit, + + /// + /// The GamePad is a game pad. + /// + GamePad, + + /// + /// The GamePad is an arcade pad. + /// + ArcadePad, + + /// + /// The GamePad is a bass guitar. + /// + BassGuitar, + } +} diff --git a/Source/OpenTK/Input/IGamePadDriver.cs b/Source/OpenTK/Input/IGamePadDriver.cs index 718d1bc1..6852dedf 100644 --- a/Source/OpenTK/Input/IGamePadDriver.cs +++ b/Source/OpenTK/Input/IGamePadDriver.cs @@ -6,19 +6,11 @@ namespace OpenTK.Input { interface IGamePadDriver { - /// - /// Retrieves the combined for all gamepad devices. - /// - /// A structure containing the combined state for all gamepad devices. - GamePadState GetState(); - /// - /// Retrieves the for the specified gamepad device. - /// - /// The index of the keyboard device. - /// A structure containing the state of the gamepad device. GamePadState GetState(int index); + GamePadCapabilities GetCapabilities(int index); + /// /// Retrieves the device name for the gamepad device. /// @@ -26,6 +18,8 @@ namespace OpenTK.Input /// A with the name of the specified device or . /// /// If no device exists at the specified index, the return value is . - string GetDeviceName(int index); + string GetName(int index); + + bool SetVibration(int index, float left, float right); } } diff --git a/Source/OpenTK/Input/IInputDriver2.cs b/Source/OpenTK/Input/IInputDriver2.cs index 7cd788aa..0e4c5132 100644 --- a/Source/OpenTK/Input/IInputDriver2.cs +++ b/Source/OpenTK/Input/IInputDriver2.cs @@ -37,5 +37,6 @@ namespace OpenTK.Input IMouseDriver2 MouseDriver { get; } IKeyboardDriver2 KeyboardDriver { get; } IGamePadDriver GamePadDriver { get; } + IJoystickDriver2 JoystickDriver { get; } } } diff --git a/Source/OpenTK/Input/IJoystickDriver2.cs b/Source/OpenTK/Input/IJoystickDriver2.cs new file mode 100644 index 00000000..8e536aaf --- /dev/null +++ b/Source/OpenTK/Input/IJoystickDriver2.cs @@ -0,0 +1,42 @@ +#region License +// +// IJoystickDriver2.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + interface IJoystickDriver2 + { + JoystickState GetState(int index); + JoystickCapabilities GetCapabilities(int index); + Guid GetGuid(int index); + } +} diff --git a/Source/OpenTK/Input/Joystick.cs b/Source/OpenTK/Input/Joystick.cs new file mode 100644 index 00000000..d280815c --- /dev/null +++ b/Source/OpenTK/Input/Joystick.cs @@ -0,0 +1,92 @@ +#region License +// +// Joystick.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + /// + /// Provides access to Joystick devices. + /// Joystick devices provide a varying number of axes and buttons. + /// Use GetCapabilities to retrieve the number of supported + /// axes and buttons on a given device. + /// Use GetState to retrieve the current state of a given device. + /// + /// + public sealed class Joystick + { + static readonly IJoystickDriver2 implementation = + Platform.Factory.Default.CreateJoystickDriver(); + + private Joystick() { } + + /// + /// Retrieves the of the device connected + /// at the specified index. + /// + /// + /// A structure describing + /// the capabilities of the device at the specified index. + /// If no device is connected at the specified index, the IsConnected + /// property of the returned structure will be false. + /// + /// The zero-based index of the device to poll. + public static JoystickCapabilities GetCapabilities(int index) + { + return implementation.GetCapabilities(index); + } + + /// + /// Retrieves the of the device connected + /// at the specified index. + /// + /// A structure describing + /// the current state of the device at the specified index. + /// If no device is connected at this index, the IsConnected + /// property of the returned structure will be false. + /// + /// The zero-based index of the device to poll. + public static JoystickState GetState(int index) + { + return implementation.GetState(index); + } + + internal static Guid GetGuid(int index) + { + return implementation.GetGuid(index); + } + + //public string GetName(int index) + //{ + // return implementation.GetName(index); + //} + } +} diff --git a/Source/OpenTK/Input/JoystickAxis.cs b/Source/OpenTK/Input/JoystickAxis.cs new file mode 100644 index 00000000..1e166e66 --- /dev/null +++ b/Source/OpenTK/Input/JoystickAxis.cs @@ -0,0 +1,62 @@ +#region License +// +// JoystickAxis.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + /// + /// Defines available JoystickDevice axes. + /// + public enum JoystickAxis + { + /// The first axis of the JoystickDevice. + Axis0 = 0, + /// The second axis of the JoystickDevice. + Axis1, + /// The third axis of the JoystickDevice. + Axis2, + /// The fourth axis of the JoystickDevice. + Axis3, + /// The fifth axis of the JoystickDevice. + Axis4, + /// The sixth axis of the JoystickDevice. + Axis5, + /// The seventh axis of the JoystickDevice. + Axis6, + /// The eighth axis of the JoystickDevice. + Axis7, + /// The ninth axis of the JoystickDevice. + Axis8, + /// The tenth axis of the JoystickDevice. + Axis9, + } +} diff --git a/Source/OpenTK/Input/JoystickButton.cs b/Source/OpenTK/Input/JoystickButton.cs new file mode 100644 index 00000000..e8daf6ed --- /dev/null +++ b/Source/OpenTK/Input/JoystickButton.cs @@ -0,0 +1,74 @@ +#region License +// +// JoystickButton.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + /// + /// Defines available JoystickDevice buttons. + /// + public enum JoystickButton + { + /// The first button of the JoystickDevice. + Button0 = 0, + /// The second button of the JoystickDevice. + Button1, + /// The third button of the JoystickDevice. + Button2, + /// The fourth button of the JoystickDevice. + Button3, + /// The fifth button of the JoystickDevice. + Button4, + /// The sixth button of the JoystickDevice. + Button5, + /// The seventh button of the JoystickDevice. + Button6, + /// The eighth button of the JoystickDevice. + Button7, + /// The ninth button of the JoystickDevice. + Button8, + /// The tenth button of the JoystickDevice. + Button9, + /// The eleventh button of the JoystickDevice. + Button10, + /// The twelfth button of the JoystickDevice. + Button11, + /// The thirteenth button of the JoystickDevice. + Button12, + /// The fourteenth button of the JoystickDevice. + Button13, + /// The fifteenth button of the JoystickDevice. + Button14, + /// The sixteenth button of the JoystickDevice. + Button15, + } +} diff --git a/Source/OpenTK/Input/JoystickCapabilities.cs b/Source/OpenTK/Input/JoystickCapabilities.cs new file mode 100644 index 00000000..3bfab7a1 --- /dev/null +++ b/Source/OpenTK/Input/JoystickCapabilities.cs @@ -0,0 +1,156 @@ +#region License +// +// JoystickCapabilities.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + /// + /// Describes the JoystickCapabilities of a . + /// + public struct JoystickCapabilities : IEquatable + { + byte axis_count; + byte button_count; + byte dpad_count; + bool is_connected; + + #region Constructors + + internal JoystickCapabilities(int axis_count, int button_count, bool is_connected) + { + if (axis_count < 0 || axis_count >= JoystickState.MaxAxes) + throw new ArgumentOutOfRangeException("axis_count"); + if (button_count < 0 || button_count >= JoystickState.MaxButtons) + throw new ArgumentOutOfRangeException("axis_count"); + + this.axis_count = (byte)axis_count; + this.button_count = (byte)button_count; + this.dpad_count = 0; // Todo: either remove dpad_count or add it as a parameter + this.is_connected = is_connected; + } + + #endregion + + #region Public Members + + /// + /// Gets the number of axes supported by this . + /// + public int AxisCount + { + get { return axis_count; } + } + + /// + /// Gets the number of buttons supported by this . + /// + public int ButtonCount + { + get { return button_count; } + } + + /// + /// Gets a value indicating whether this is connected. + /// + /// true if this instance is connected; otherwise, false. + public bool IsConnected + { + get { return is_connected; } + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return String.Format( + "{{Axes: {0}; Buttons: {1}; IsConnected: {2}}}", + AxisCount, ButtonCount, IsConnected); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + return + AxisCount.GetHashCode() ^ + ButtonCount.GetHashCode() ^ + IsConnected.GetHashCode(); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is JoystickCapabilities && + Equals((JoystickCapabilities)obj); + } + + #endregion + + #region Private Members + + int DPadCount + { + get { return dpad_count; } + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(JoystickCapabilities other) + { + return + AxisCount == other.AxisCount && + ButtonCount == other.ButtonCount && + IsConnected == other.IsConnected; + } + + #endregion + } +} diff --git a/Source/OpenTK/Input/JoystickDevice.cs b/Source/OpenTK/Input/JoystickDevice.cs index 6f99cdac..55a92bef 100644 --- a/Source/OpenTK/Input/JoystickDevice.cs +++ b/Source/OpenTK/Input/JoystickDevice.cs @@ -156,12 +156,13 @@ namespace OpenTK.Input // Provides platform-specific information about the relevant JoystickDevice. internal sealed class JoystickDevice : JoystickDevice + where TDetail : new() { internal JoystickDevice(int id, int axes, int buttons) : base(id, axes, buttons) { } - internal TDetail Details; + internal TDetail Details = new TDetail(); } #endregion @@ -271,49 +272,6 @@ namespace OpenTK.Input #endregion - #region JoystickButton - - /// - /// Defines available JoystickDevice buttons. - /// - public enum JoystickButton - { - /// The first button of the JoystickDevice. - Button0 = 0, - /// The second button of the JoystickDevice. - Button1, - /// The third button of the JoystickDevice. - Button2, - /// The fourth button of the JoystickDevice. - Button3, - /// The fifth button of the JoystickDevice. - Button4, - /// The sixth button of the JoystickDevice. - Button5, - /// The seventh button of the JoystickDevice. - Button6, - /// The eighth button of the JoystickDevice. - Button7, - /// The ninth button of the JoystickDevice. - Button8, - /// The tenth button of the JoystickDevice. - Button9, - /// The eleventh button of the JoystickDevice. - Button10, - /// The twelfth button of the JoystickDevice. - Button11, - /// The thirteenth button of the JoystickDevice. - Button12, - /// The fourteenth button of the JoystickDevice. - Button13, - /// The fifteenth button of the JoystickDevice. - Button14, - /// The sixteenth button of the JoystickDevice. - Button15, - } - - #endregion - #region JoystickButtonCollection /// @@ -376,37 +334,6 @@ namespace OpenTK.Input #endregion - #region JoystickAxis - - /// - /// Defines available JoystickDevice axes. - /// - public enum JoystickAxis - { - /// The first axis of the JoystickDevice. - Axis0 = 0, - /// The second axis of the JoystickDevice. - Axis1, - /// The third axis of the JoystickDevice. - Axis2, - /// The fourth axis of the JoystickDevice. - Axis3, - /// The fifth axis of the JoystickDevice. - Axis4, - /// The sixth axis of the JoystickDevice. - Axis5, - /// The seventh axis of the JoystickDevice. - Axis6, - /// The eighth axis of the JoystickDevice. - Axis7, - /// The ninth axis of the JoystickDevice. - Axis8, - /// The tenth axis of the JoystickDevice. - Axis9, - } - - #endregion - #region JoystickAxisCollection /// diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs new file mode 100644 index 00000000..f5f533a2 --- /dev/null +++ b/Source/OpenTK/Input/JoystickState.cs @@ -0,0 +1,260 @@ +#region License +// +// JoystickState.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Diagnostics; +using System.Text; + +namespace OpenTK.Input +{ + /// + /// Describes the current state of a . + /// + public struct JoystickState : IEquatable + { + // If we ever add more values to JoystickAxis or JoystickButton + // then we'll need to increase these limits. + internal const int MaxAxes = 10; + internal const int MaxButtons = 32; + + const float ConversionFactor = 1.0f / (short.MaxValue + 0.5f); + + unsafe fixed short axes[MaxAxes]; + int buttons; + int packet_number; + bool is_connected; + + #region Public Members + + /// + /// Gets a value between -1.0 and 1.0 representing the current offset of the specified . + /// + /// + /// A value between -1.0 and 1.0 representing offset of the specified . + /// If the specified axis does not exist, then the return value is 0.0. Use + /// to query the number of available axes. + /// + /// The to query. + public float GetAxis(JoystickAxis axis) + { + return GetAxisRaw(axis) * ConversionFactor; + } + + /// + /// Gets the current of the specified . + /// + /// if the specified button is pressed; otherwise, . + /// The to query. + public ButtonState GetButton(JoystickButton button) + { + return (buttons & (1 << (int)button)) != 0 ? ButtonState.Pressed : ButtonState.Released; + } + + /// + /// Gets a value indicating whether the specified is currently pressed. + /// + /// true if the specified button is pressed; otherwise, false. + /// The to query. + public bool IsButtonDown(JoystickButton button) + { + return (buttons & (1 << (int)button)) != 0; + } + + /// + /// Gets a value indicating whether the specified is currently released. + /// + /// true if the specified button is released; otherwise, false. + /// The to query. + public bool IsButtonUp(JoystickButton button) + { + return (buttons & (1 << (int)button)) == 0; + } + + /// + /// Gets a value indicating whether this instance is connected. + /// + /// true if this instance is connected; otherwise, false. + public bool IsConnected + { + get { return is_connected; } + } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < MaxAxes; i++) + { + sb.Append(" "); + sb.Append(String.Format("{0:f4}", GetAxis(JoystickAxis.Axis0 + i))); + } + return String.Format( + "{{Axes:{0}; Buttons: {1}; IsConnected: {2}}}", + sb.ToString(), + Convert.ToString((int)buttons, 2).PadLeft(16, '0'), + IsConnected); + } + + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. + public override int GetHashCode() + { + int hash = buttons.GetHashCode() ^ IsConnected.GetHashCode(); + for (int i = 0; i < MaxAxes; i++) + { + hash ^= GetAxisUnsafe(i).GetHashCode(); + } + return hash; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public override bool Equals(object obj) + { + return + obj is JoystickState && + Equals((JoystickState)obj); + } + + #endregion + + #region Internal Members + + internal int PacketNumber + { + get { return packet_number; } + } + + internal short GetAxisRaw(JoystickAxis axis) + { + return GetAxisRaw((int)axis); + } + + internal short GetAxisRaw(int axis) + { + short value = 0; + if (axis >= 0 && axis < MaxAxes) + { + value = GetAxisUnsafe(axis); + } + else + { + Debug.Print("[Joystick] Invalid axis {0}", axis); + } + return value; + } + + internal void SetAxis(JoystickAxis axis, short value) + { + int index = (int)axis; + if (index < 0 || index >= MaxAxes) + throw new ArgumentOutOfRangeException("axis"); + + unsafe + { + fixed (short* paxes = axes) + { + *(paxes + index) = value; + } + } + } + + internal void SetButton(JoystickButton button, bool value) + { + int index = 1 << (int)button; + if (value) + { + buttons |= index; + } + else + { + buttons &= ~index; + } + } + + internal void SetIsConnected(bool value) + { + is_connected = value; + } + + internal void SetPacketNumber(int number) + { + packet_number = number; + } + + #endregion + + #region Private Members + + short GetAxisUnsafe(int index) + { + unsafe + { + fixed (short* paxis = axes) + { + return *(paxis + index); + } + } + } + + #endregion + + #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. + public bool Equals(JoystickState other) + { + bool equals = + buttons == other.buttons && + IsConnected == other.IsConnected; + for (int i = 0; equals && i < MaxAxes; i++) + { + equals &= GetAxisUnsafe(i) == other.GetAxisUnsafe(i); + } + return equals; + } + + #endregion + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 92404b7f..d9b969c9 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -133,10 +133,19 @@ + + + + + + + + + @@ -154,7 +163,9 @@ + + Code @@ -738,8 +749,7 @@ - - + @@ -778,6 +788,16 @@ + + + + + + + + + + diff --git a/Source/OpenTK/Platform/Factory.cs b/Source/OpenTK/Platform/Factory.cs index a3ffa951..e49eb36b 100644 --- a/Source/OpenTK/Platform/Factory.cs +++ b/Source/OpenTK/Platform/Factory.cs @@ -149,6 +149,11 @@ namespace OpenTK.Platform return default_implementation.CreateGamePadDriver(); } + public Input.IJoystickDriver2 CreateJoystickDriver() + { + return default_implementation.CreateJoystickDriver(); + } + class UnsupportedPlatform : IPlatformFactory { #region Fields @@ -210,6 +215,11 @@ namespace OpenTK.Platform throw new PlatformNotSupportedException(error_string); } + public Input.IJoystickDriver2 CreateJoystickDriver() + { + throw new PlatformNotSupportedException(error_string); + } + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/IPlatformFactory.cs b/Source/OpenTK/Platform/IPlatformFactory.cs index 541c8710..c59654f3 100644 --- a/Source/OpenTK/Platform/IPlatformFactory.cs +++ b/Source/OpenTK/Platform/IPlatformFactory.cs @@ -52,5 +52,7 @@ namespace OpenTK.Platform OpenTK.Input.IMouseDriver2 CreateMouseDriver(); OpenTK.Input.IGamePadDriver CreateGamePadDriver(); + + Input.IJoystickDriver2 CreateJoystickDriver(); } } diff --git a/Source/OpenTK/Platform/MacOS/CarbonInput.cs b/Source/OpenTK/Platform/MacOS/CarbonInput.cs index 1fd65a2c..640b99b8 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonInput.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonInput.cs @@ -113,5 +113,10 @@ namespace OpenTK.Platform.MacOS throw new NotImplementedException(); } } + + public IJoystickDriver2 JoystickDriver + { + get { throw new NotImplementedException(); } + } } } diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 8e3c6011..9090e703 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -50,7 +50,7 @@ namespace OpenTK.Platform.MacOS // Requires Mac OS X 10.5 or higher. // Todo: create a driver for older installations. Maybe use CGGetLastMouseDelta for that? - class HIDInput : IInputDriver2, IMouseDriver2, IKeyboardDriver2/*, IGamePadDriver*/ + class HIDInput : IInputDriver2, IMouseDriver2, IKeyboardDriver2, IJoystickDriver2 { #region Fields @@ -69,6 +69,8 @@ namespace OpenTK.Platform.MacOS readonly CFString InputLoopMode = CF.RunLoopModeDefault; readonly CFDictionary DeviceTypes = new CFDictionary(); + readonly MappedGamePadDriver mapped_gamepad = new MappedGamePadDriver(); + NativeMethods.IOHIDDeviceCallback HandleDeviceAdded; NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved; NativeMethods.IOHIDValueCallback HandleDeviceValueReceived; @@ -93,7 +95,7 @@ namespace OpenTK.Platform.MacOS #endregion - #region Private Members + #region Private Members IOHIDManagerRef CreateHIDManager() { @@ -291,7 +293,8 @@ namespace OpenTK.Platform.MacOS public IMouseDriver2 MouseDriver { get { return this; } } public IKeyboardDriver2 KeyboardDriver { get { return this; } } - public IGamePadDriver GamePadDriver { get { throw new NotImplementedException(); } } + public IGamePadDriver GamePadDriver { get { return mapped_gamepad; } } + public IJoystickDriver2 JoystickDriver { get { return this; } } #endregion @@ -366,6 +369,25 @@ namespace OpenTK.Platform.MacOS #endregion + #region IJoystickDriver2 Members + + JoystickState IJoystickDriver2.GetState(int index) + { + return new JoystickState(); + } + + JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) + { + return new JoystickCapabilities(); + } + + Guid IJoystickDriver2.GetGuid(int index) + { + return new Guid(); + } + + #endregion + #region NativeMethods class NativeMethods diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs index 97473c23..697f2352 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs @@ -93,6 +93,11 @@ namespace OpenTK.Platform.MacOS { return InputDriver.GamePadDriver; } + + public IJoystickDriver2 CreateJoystickDriver() + { + return InputDriver.JoystickDriver; + } #endregion diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs new file mode 100644 index 00000000..12f918c9 --- /dev/null +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -0,0 +1,196 @@ +#region License +// +// MappedGamePadDriver.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using OpenTK.Input; + +namespace OpenTK.Platform +{ + /// \internal + /// + /// Implements IGamePadDriver using OpenTK.Input.Joystick + /// and a gamepad-specific axis/button mapping. + /// + /// + /// + /// This class supports OpenTK and is not meant to be accessed by user code. + /// + /// + /// To support gamepads on platforms that do not offer a gamepad-optimized API, + /// we need to use the generic OpenTK.Input.Joystick and implement a custom + /// mapping scheme to provide a stable mapping to OpenTK.Input.GamePad. This + /// class implements this mapping scheme. + /// + /// + class MappedGamePadDriver : IGamePadDriver + { + readonly GamePadConfigurationDatabase database = + new GamePadConfigurationDatabase(); + readonly Dictionary configurations = + new Dictionary(); + + public GamePadState GetState(int index) + { + JoystickState joy = Joystick.GetState(index); + GamePadState pad = new GamePadState(); + + if (joy.IsConnected) + { + pad.SetConnected(true); + pad.SetPacketNumber(joy.PacketNumber); + + GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index)); + + foreach (GamePadConfigurationItem map in configuration) + { + switch (map.Source.Type) + { + case ConfigurationType.Axis: + { + // JoystickAxis -> Buttons/GamePadAxes mapping + JoystickAxis source_axis = map.Source.Axis; + GamePadAxes target_axis = map.Target.Axis; + short value = joy.GetAxisRaw(source_axis); + + switch (map.Target.Type) + { + case ConfigurationType.Axis: + pad.SetAxis(target_axis, value); + break; + + case ConfigurationType.Button: + throw new NotImplementedException(); + break; + } + } + break; + + case ConfigurationType.Button: + { + // JoystickButton -> Buttons/GamePadAxes mapping + JoystickButton source_button = map.Source.Button; + Buttons target_button = map.Target.Button; + bool pressed = joy.GetButton(source_button) == ButtonState.Pressed; + + switch (map.Target.Type) + { + case ConfigurationType.Axis: + throw new NotImplementedException(); + break; + + case ConfigurationType.Button: + pad.SetButton(target_button, pressed); + break; + } + } + break; + } + } + } + + return pad; + } + + public GamePadCapabilities GetCapabilities(int index) + { + JoystickCapabilities joy = Joystick.GetCapabilities(index); + GamePadCapabilities pad; + if (joy.IsConnected) + { + GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index)); + GamePadAxes mapped_axes = 0; + Buttons mapped_buttons = 0; + + foreach (GamePadConfigurationItem map in configuration) + { + switch (map.Target.Type) + { + case ConfigurationType.Axis: + mapped_axes |= map.Target.Axis; + break; + + case ConfigurationType.Button: + mapped_buttons |= map.Target.Button; + break; + } + } + + pad = new GamePadCapabilities( + GamePadType.GamePad, // Todo: detect different types + mapped_axes, + mapped_buttons, + true); + } + else + { + pad = new GamePadCapabilities(); + } + return pad; + } + + public string GetName(int index) + { + JoystickCapabilities joy = Joystick.GetCapabilities(index); + string name = String.Empty; + if (joy.IsConnected) + { + GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index)); + name = map.Name; + } + return name; + } + + public bool SetVibration(int index, float left, float right) + { + return false; + } + + #region Private Members + + GamePadConfiguration GetConfiguration(Guid guid) + { + if (!configurations.ContainsKey(guid)) + { + string config = database[guid]; + GamePadConfiguration map = new GamePadConfiguration(config); + configurations.Add(guid, map); + } + return configurations[guid]; + } + + bool IsMapped(GamePadConfigurationSource item) + { + return item.Type != ConfigurationType.Unmapped; + } + + #endregion + } +} diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 40ec3700..5dc31dd9 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -23,8 +23,6 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // -using System.Runtime.InteropServices; - #endregion @@ -118,10 +116,91 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)] public static extern void FreeSurface(IntPtr surface); + #region GameContoller + + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerEventState", ExactSpelling = true)] + public static extern EventState GameControllerEventState(EventState state); + + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetAxis", ExactSpelling = true)] + public static extern short GameControllerGetAxis(IntPtr gamecontroller, GameControllerAxis axis); + + /// + /// Gets the SDL joystick layer binding for the specified game controller axis + /// + /// Pointer to a game controller instance returned by GameControllerOpen. + /// A value from the GameControllerAxis enumeration + /// A GameControllerButtonBind instance describing the specified binding + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForAxis", ExactSpelling = true)] + public static extern GameControllerButtonBind GameControllerGetBindForAxis(IntPtr gamecontroller, GameControllerAxis axis); + + /// + /// Gets the SDL joystick layer binding for the specified game controller button + /// + /// Pointer to a game controller instance returned by GameControllerOpen. + /// A value from the GameControllerButton enumeration + /// A GameControllerButtonBind instance describing the specified binding + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForButton", ExactSpelling = true)] + public static extern GameControllerButtonBind GameControllerGetBindForButton( + IntPtr gamecontroller, GameControllerButton button); + + /// + /// Gets the current state of a button on a game controller. + /// + /// A game controller handle previously opened with GameControllerOpen. + /// A zero-based GameControllerButton value. + /// true if the specified button is pressed; false otherwise. + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetButton", ExactSpelling = true)] + public static extern bool GameControllerGetButton(IntPtr gamecontroller, GameControllerButton button); + + /// + /// Retrieve the joystick handle that corresponds to the specified game controller. + /// + /// A game controller handle previously opened with GameControllerOpen. + /// A handle to a joystick, or IntPtr.Zero in case of error. The pointer is owned by the callee. Use SDL.GetError to retrieve error information + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetJoystick", ExactSpelling = true)] + public static extern IntPtr GameControllerGetJoystick(IntPtr gamecontroller); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetCurrentDisplayMode", ExactSpelling = true)] public static extern int GetCurrentDisplayMode(int displayIndex, out DisplayMode mode); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerName", ExactSpelling = true)] + static extern IntPtr GameControllerNameInternal(IntPtr gamecontroller); + + /// + /// Return the name for an openend game controller instance. + /// + /// The name of the game controller name. + /// Pointer to a game controller instance returned by GameControllerOpen. + public static string GameControllerName(IntPtr gamecontroller) + { + unsafe + { + return new string((sbyte*)GameControllerNameInternal(gamecontroller)); + } + } + + /// + /// Opens a game controller for use. + /// + /// + /// A zero-based index for the game controller. + /// This index is the value which will identify this controller in future controller events. + /// + /// A handle to the game controller instance, or IntPtr.Zero in case of error. + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerOpen", ExactSpelling = true)] + public static extern IntPtr GameControllerOpen(int joystick_index); + + #endregion + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetDisplayBounds", ExactSpelling = true)] public static extern int GetDisplayBounds(int displayIndex, out Rect rect); @@ -192,10 +271,27 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_Init", ExactSpelling = true)] public static extern int Init(SystemFlags flags); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_InitSubSystem", ExactSpelling = true)] + public static extern int InitSubSystem(SystemFlags flags); + + /// + /// Determines if the specified joystick is supported by the GameController API. + /// + /// true if joystick_index is supported by the GameController API; false otherwise. + /// The index of the joystick to check. + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_IsGameController", ExactSpelling = true)] + public static extern bool IsGameController(int joystick_index); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickClose", ExactSpelling = true)] public static extern void JoystickClose(IntPtr joystick); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickEventState", ExactSpelling = true)] + public static extern EventState JoystickEventState(EventState enabled); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetAxis", ExactSpelling = true)] public static extern short JoystickGetAxis(IntPtr joystick, int axis); @@ -204,6 +300,10 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetButton", ExactSpelling = true)] public static extern byte JoystickGetButton(IntPtr joystick, int button); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetGUID", ExactSpelling = true)] + public static extern JoystickGuid JoystickGetGUID(IntPtr joystick); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickName", ExactSpelling = true)] static extern IntPtr JoystickNameInternal(IntPtr joystick); @@ -462,6 +562,13 @@ namespace OpenTK.Platform.SDL2 ES = 0x0004 } + enum EventState + { + Query = -1, + Ignore = 0, + Enable = 1 + } + enum EventType { FIRSTEVENT = 0, @@ -501,6 +608,61 @@ namespace OpenTK.Platform.SDL2 LASTEVENT = 0xFFFF } + enum GameControllerAxis : byte + { + Invalid = 0xff, + LeftX = 0, + LeftY, + RightX, + RightY, + TriggerLeft, + TriggerRight, + Max + } + + enum GameControllerButton : byte + { + INVALID = 0xff, + A = 0, + B, + X, + Y, + BACK, + GUIDE, + START, + LEFTSTICK, + RIGHTSTICK, + LEFTSHOULDER, + RIGHTSHOULDER, + DPAD_UP, + DPAD_DOWN, + DPAD_LEFT, + DPAD_RIGHT, + Max + } + + enum GameControllerBindType : byte + { + None = 0, + Button, + Axis, + Hat + } + + [Flags] + enum HatPosition : byte + { + Centered = 0x00, + Up = 0x01, + Right = 0x02, + Down = 0x03, + Left = 0x04, + RightUp = Right | Up, + RightDown = Right | Down, + LeftUp = Left | Up, + LeftDown = Left | Down + } + enum Keycode { UNKNOWN = 0, @@ -1080,6 +1242,41 @@ namespace OpenTK.Platform.SDL2 #region Structs + struct ControllerAxisEvent + { + public EventType Type; + public uint Timestamp; + public int Which; + public GameControllerAxis Axis; + byte padding1; + byte padding2; + byte padding3; + public short Value; + ushort padding4; + } + + struct ControllerButtonEvent + { + public EventType Type; + public uint Timestamp; + public int Which; + public GameControllerButton Button; + public State State; + byte padding1; + byte padding2; + } + + struct ControllerDeviceEvent + { + public EventType Type; + public uint Timestamp; + + /// + /// The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event + /// + public int Which; + } + struct DisplayMode { public uint Format; @@ -1110,21 +1307,21 @@ namespace OpenTK.Platform.SDL2 public MouseWheelEvent Wheel; [FieldOffset(0)] public JoyAxisEvent JoyAxis; + [FieldOffset(0)] + public JoyBallEvent JoyBall; + [FieldOffset(0)] + public JoyHatEvent JoyHat; + [FieldOffset(0)] + public JoyButtonEvent JoyButton; + [FieldOffset(0)] + public JoyDeviceEvent JoyDevice; + [FieldOffset(0)] + public ControllerAxisEvent ControllerAxis; + [FieldOffset(0)] + public ControllerButtonEvent ControllerButton; + [FieldOffset(0)] + public ControllerDeviceEvent ControllerDevice; #if false - [FieldOffset(0)] - public JoyBallEvent jball; - [FieldOffset(0)] - public JoyHatEvent jhat; - [FieldOffset(0)] - public JoyButtonEvent jbutton; - [FieldOffset(0)] - public JoyDeviceEvent jdevice; - [FieldOffset(0)] - public ControllerAxisEvent caxis; - [FieldOffset(0)] - public ControllerButtonEvent cbutton; - [FieldOffset(0)] - public ControllerDeviceEvent cdevice; [FieldOffset(0)] public QuitEvent quit; [FieldOffset(0)] @@ -1142,6 +1339,21 @@ namespace OpenTK.Platform.SDL2 #endif } + [StructLayout(LayoutKind.Explicit)] + struct GameControllerButtonBind + { + [FieldOffset(0)] + public GameControllerBindType BindType; + [FieldOffset(4)] + public Button Button; + [FieldOffset(4)] + public GameControllerAxis Axis; + [FieldOffset(4)] + public int Hat; + [FieldOffset(8)] + public int HatMask; + } + struct JoyAxisEvent { public EventType Type; @@ -1155,6 +1367,68 @@ namespace OpenTK.Platform.SDL2 UInt16 padding4; } + struct JoyBallEvent + { + public EventType Type; + public uint Timestamp; + public int Which; + public byte Ball; + byte padding1; + byte padding2; + byte padding3; + public short Xrel; + public short Yrel; + } + + struct JoyButtonEvent + { + public EventType Type; + public uint Timestamp; + public int Which; + public byte Button; + public State State; + byte padding1; + byte padding2; + } + + struct JoyDeviceEvent + { + public EventType Type; + public uint Timestamp; + public int Which; + } + + struct JoyHatEvent + { + public EventType Type; + public uint Timestamp; + public int Which; + public byte Hat; + public HatPosition Value; + byte padding1; + byte padding2; + } + + struct JoystickGuid + { + unsafe fixed byte data[16]; + + public Guid ToGuid() + { + byte[] bytes = new byte[16]; + + unsafe + { + fixed (byte* pdata = data) + { + Marshal.Copy(new IntPtr(pdata), bytes, 0, bytes.Length); + } + } + + return new Guid(bytes); + } + } + struct KeyboardEvent { public EventType Type; diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs index 9f2447ba..b8015f6b 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs @@ -34,7 +34,7 @@ namespace OpenTK.Platform.SDL2 { class Sdl2Factory : IPlatformFactory { - readonly IInputDriver2 InputDriver = new Sdl2InputDriver(); + readonly Sdl2InputDriver InputDriver = new Sdl2InputDriver(); bool disposed; /// @@ -58,7 +58,7 @@ namespace OpenTK.Platform.SDL2 public INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { - return new Sdl2NativeWindow(x, y, width, height, title, options, device); + return new Sdl2NativeWindow(x, y, width, height, title, options, device, InputDriver); } public IDisplayDeviceDriver CreateDisplayDeviceDriver() @@ -104,6 +104,11 @@ namespace OpenTK.Platform.SDL2 return InputDriver.GamePadDriver; } + public IJoystickDriver2 CreateJoystickDriver() + { + return InputDriver.JoystickDriver; + } + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index 7a571b14..5e88fd6d 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -55,10 +55,21 @@ namespace OpenTK.Platform.SDL2 { lock (SDL.Sync) { + SDL.GameControllerEventState(EventState.Enable); + SDL.JoystickEventState(EventState.Enable); + EventFilterDelegate = Marshal.GetFunctionPointerForDelegate(EventFilterDelegate_GCUnsafe); driver_handle = new IntPtr(count++); DriverHandles.Add(driver_handle, this); SDL.AddEventWatch(EventFilterDelegate, driver_handle); + if (SDL.InitSubSystem(SystemFlags.JOYSTICK) < 0) + { + Debug.Print("[SDL2] InputDriver failed to init Joystick subsystem. Error: {0}", SDL.GetError()); + } + if (SDL.InitSubSystem(SystemFlags.GAMECONTROLLER) < 0) + { + Debug.Print("[SDL2] InputDriver failed to init GameController subsystem. Error: {0}", SDL.GetError()); + } } } @@ -92,6 +103,44 @@ namespace OpenTK.Platform.SDL2 case EventType.MOUSEWHEEL: driver.mouse_driver.ProcessWheelEvent(ev.Wheel); break; + + case EventType.JOYDEVICEADDED: + case EventType.JOYDEVICEREMOVED: + driver.joystick_driver.ProcessJoystickEvent(ev.JoyDevice); + break; + + case EventType.JOYAXISMOTION: + driver.joystick_driver.ProcessJoystickEvent(ev.JoyAxis); + break; + + case EventType.JOYBALLMOTION: + driver.joystick_driver.ProcessJoystickEvent(ev.JoyBall); + break; + + case EventType.JOYBUTTONDOWN: + case EventType.JOYBUTTONUP: + driver.joystick_driver.ProcessJoystickEvent(ev.JoyButton); + break; + + case EventType.JOYHATMOTION: + driver.joystick_driver.ProcessJoystickEvent(ev.JoyHat); + break; + +#if USE_SDL2_GAMECONTROLLER + case EventType.CONTROLLERDEVICEADDED: + case EventType.CONTROLLERDEVICEREMOVED: + driver.joystick_driver.ProcessControllerEvent(ev.ControllerDevice); + break; + + case EventType.CONTROLLERAXISMOTION: + driver.joystick_driver.ProcessControllerEvent(ev.ControllerAxis); + break; + + case EventType.CONTROLLERBUTTONDOWN: + case EventType.CONTROLLERBUTTONUP: + driver.joystick_driver.ProcessControllerEvent(ev.ControllerButton); + break; +#endif } } } @@ -172,7 +221,15 @@ namespace OpenTK.Platform.SDL2 { get { - throw new NotImplementedException(); + return joystick_driver; + } + } + + public IJoystickDriver2 JoystickDriver + { + get + { + return joystick_driver; } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 729cee13..8a08151a 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -32,61 +32,482 @@ using OpenTK.Input; namespace OpenTK.Platform.SDL2 { - class Sdl2JoystickDriver : IJoystickDriver, IGamePadDriver, IDisposable + class Sdl2JoystickDriver : IJoystickDriver, IJoystickDriver2, IGamePadDriver, IDisposable { - struct Sdl2JoystickDetails + const float RangeMultiplier = 1.0f / 32768.0f; + readonly MappedGamePadDriver gamepad_driver = new MappedGamePadDriver(); + bool disposed; + + class Sdl2JoystickDetails { public IntPtr Handle { get; set; } - public float RangeMultiplier { get { return 1.0f / 32768.0f; } } + public Guid Guid { get; set; } + public int PacketNumber { get; set; } public int HatCount { get; set; } public int BallCount { get; set; } + public bool IsConnected { get; set; } } - readonly List joysticks = new List(); + // For IJoystickDriver2 implementation + int last_joystick_instance = 0; + readonly List joysticks = new List(4); + readonly Dictionary sdl_instanceid_to_joysticks = new Dictionary(); + + // For IJoystickDriver implementation IList joysticks_readonly; - bool disposed = false; + +#if USE_SDL2_GAMECONTROLLER + class Sdl2GamePad + { + public IntPtr Handle { get; private set; } + public GamePadState State; + public GamePadCapabilities Capabilities; + + public Sdl2GamePad(IntPtr handle) + { + Handle = handle; + } + } + + int last_controllers_instance = 0; + readonly List controllers = new List(4); + readonly Dictionary sdl_instanceid_to_controllers = new Dictionary(); +#endif public Sdl2JoystickDriver() { joysticks_readonly = joysticks.AsReadOnly(); - - RefreshJoysticks(); - } #region Private Members - void RefreshJoysticks() + JoystickDevice OpenJoystick(int id) { - joysticks.Clear(); + JoystickDevice joystick = null; + int num_axes = 0; + int num_buttons = 0; + int num_hats = 0; + int num_balls = 0; - int count = SDL.NumJoysticks(); - for (int i = 0; i < count; i++) + IntPtr handle = SDL.JoystickOpen(id); + if (handle != IntPtr.Zero) { - JoystickDevice joystick = null; - int num_axes = 0; - int num_buttons = 0; - int num_hats = 0; - int num_balls = 0; + num_axes = SDL.JoystickNumAxes(handle); + num_buttons = SDL.JoystickNumButtons(handle); + num_hats = SDL.JoystickNumHats(handle); + num_balls = SDL.JoystickNumBalls(handle); - IntPtr handle = SDL.JoystickOpen(i); - if (handle != IntPtr.Zero) - { - num_axes = SDL.JoystickNumAxes(handle); - num_buttons = SDL.JoystickNumButtons(handle); - num_hats = SDL.JoystickNumHats(handle); - num_balls = SDL.JoystickNumBalls(handle); + joystick = new JoystickDevice(id, num_axes, num_buttons); + joystick.Description = SDL.JoystickName(handle); + joystick.Details.Handle = handle; + joystick.Details.Guid = SDL.JoystickGetGUID(handle).ToGuid(); + joystick.Details.HatCount = num_hats; + joystick.Details.BallCount = num_balls; - joystick = new JoystickDevice(i, num_axes, num_buttons); - joystick.Description = SDL.JoystickName(handle); - joystick.Details.Handle = handle; - joystick.Details.HatCount = num_hats; - joystick.Details.BallCount = num_balls; - joysticks.Add(joystick); - } + Debug.Print("[SDL2] Joystick device {0} opened successfully. ", id); + Debug.Print("\t\t'{0}' has {1} axes, {2} buttons, {3} hats, {4} balls", + joystick.Description, joystick.Axis.Count, joystick.Button.Count, + joystick.Details.HatCount, joystick.Details.BallCount); + } + else + { + Debug.Print("[SDL2] Failed to open joystick device {0}", id); + } + + return joystick; + } + + bool IsJoystickValid(int id) + { + return id >= 0 && id < joysticks.Count; + } + + bool IsJoystickInstanceValid(int instance_id) + { + return sdl_instanceid_to_joysticks.ContainsKey(instance_id); + } + +#if USE_SDL2_GAMECONTROLLER + bool IsControllerValid(int id) + { + return id >= 0 && id < controllers.Count; + } + + bool IsControllerInstanceValid(int instance_id) + { + return sdl_instanceid_to_controllers.ContainsKey(instance_id); + } + + GamePadAxes GetBoundAxes(IntPtr gamecontroller) + { + GamePadAxes axes = 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftX) ? GamePadAxes.LeftX : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftY) ? GamePadAxes.LeftY : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightX) ? GamePadAxes.RightX : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightY) ? GamePadAxes.RightY : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerLeft) ? GamePadAxes.LeftTrigger : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerRight) ? GamePadAxes.RightTrigger : 0; + return axes; + } + + Buttons GetBoundButtons(IntPtr gamecontroller) + { + Buttons buttons = 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.A) ? Buttons.A : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.B) ? Buttons.B : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.X) ? Buttons.X : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.Y) ? Buttons.Y : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.START) ? Buttons.Start : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.BACK) ? Buttons.Back : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSHOULDER) ? Buttons.LeftShoulder : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSHOULDER) ? Buttons.RightShoulder : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSTICK) ? Buttons.LeftStick : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSTICK) ? Buttons.RightStick : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.GUIDE) ? Buttons.BigButton : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_DOWN) ? Buttons.DPadDown : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_UP) ? Buttons.DPadUp : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_LEFT) ? Buttons.DPadLeft : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_RIGHT) ? Buttons.DPadRight : 0; + return buttons; + } + + bool IsAxisBind(IntPtr gamecontroller, GameControllerAxis axis) + { + GameControllerButtonBind bind = + SDL.GameControllerGetBindForAxis(gamecontroller, axis); + return bind.BindType == GameControllerBindType.Axis; + } + + bool IsButtonBind(IntPtr gamecontroller, GameControllerButton button) + { + GameControllerButtonBind bind = + SDL.GameControllerGetBindForButton(gamecontroller, button); + return bind.BindType == GameControllerBindType.Button; + } + + GamePadAxes TranslateAxis(GameControllerAxis axis) + { + switch (axis) + { + case GameControllerAxis.LeftX: + return GamePadAxes.LeftX; + + case GameControllerAxis.LeftY: + return GamePadAxes.LeftY; + + case GameControllerAxis.RightX: + return GamePadAxes.RightX; + + case GameControllerAxis.RightY: + return GamePadAxes.RightY; + + case GameControllerAxis.TriggerLeft: + return GamePadAxes.LeftTrigger; + + case GameControllerAxis.TriggerRight: + return GamePadAxes.RightTrigger; + + default: + throw new ArgumentOutOfRangeException( + String.Format("[SDL] Unknown axis {0}", axis)); } } + Buttons TranslateButton(GameControllerButton button) + { + switch (button) + { + case GameControllerButton.A: + return Buttons.A; + + case GameControllerButton.B: + return Buttons.B; + + case GameControllerButton.X: + return Buttons.X; + + case GameControllerButton.Y: + return Buttons.Y; + + case GameControllerButton.LEFTSHOULDER: + return Buttons.LeftShoulder; + + case GameControllerButton.RIGHTSHOULDER: + return Buttons.RightShoulder; + + case GameControllerButton.LEFTSTICK: + return Buttons.LeftStick; + + case GameControllerButton.RIGHTSTICK: + return Buttons.RightStick; + + case GameControllerButton.DPAD_UP: + return Buttons.DPadUp; + + case GameControllerButton.DPAD_DOWN: + return Buttons.DPadDown; + + case GameControllerButton.DPAD_LEFT: + return Buttons.DPadLeft; + + case GameControllerButton.DPAD_RIGHT: + return Buttons.DPadRight; + + case GameControllerButton.BACK: + return Buttons.Back; + + case GameControllerButton.START: + return Buttons.Start; + + case GameControllerButton.GUIDE: + return Buttons.BigButton; + + default: + Debug.Print("[SDL2] Unknown button {0}", button); + return 0; + } + } +#endif + + #endregion + + #region Public Members + + public void ProcessJoystickEvent(JoyDeviceEvent ev) + { + int id = ev.Which; + if (id < 0) + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + return; + } + + switch (ev.Type) + { + case EventType.JOYDEVICEADDED: + { + IntPtr handle = SDL.JoystickOpen(id); + if (handle != IntPtr.Zero) + { + int device_id = id; + int instance_id = last_joystick_instance++; + + JoystickDevice joystick = OpenJoystick(id); + if (joystick != null) + { + joystick.Details.IsConnected = true; + if (device_id < joysticks.Count) + { + joysticks[device_id] = joystick; + } + else + { + joysticks.Add(joystick); + } + + sdl_instanceid_to_joysticks.Add(instance_id, device_id); + } + } + } + break; + + case EventType.JOYDEVICEREMOVED: + if (IsJoystickInstanceValid(id)) + { + int instance_id = id; + int device_id = sdl_instanceid_to_joysticks[instance_id]; + + JoystickDevice joystick = (JoystickDevice)joysticks[device_id]; + joystick.Details.IsConnected = false; + + sdl_instanceid_to_joysticks.Remove(instance_id); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + break; + } + } + + public void ProcessJoystickEvent(JoyAxisEvent ev) + { + int id = ev.Which; + if (IsJoystickInstanceValid(id)) + { + int index = sdl_instanceid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; + float value = ev.Value * RangeMultiplier; + joystick.SetAxis((JoystickAxis)ev.Axis, value); + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessJoystickEvent(JoyBallEvent ev) + { + int id = ev.Which; + if (IsJoystickInstanceValid(id)) + { + int index = sdl_instanceid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; + // Todo: does it make sense to support balls? + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessJoystickEvent(JoyButtonEvent ev) + { + int id = ev.Which; + if (IsJoystickInstanceValid(id)) + { + int index = sdl_instanceid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; + joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed); + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessJoystickEvent(JoyHatEvent ev) + { + int id = ev.Which; + if (IsJoystickInstanceValid(id)) + { + int index = sdl_instanceid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; + // Todo: map hat to an extra axis + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + +#if USE_SDL2_GAMECONTROLLER + public void ProcessControllerEvent(ControllerDeviceEvent ev) + { + int id = ev.Which; + if (id < 0) + { + Debug.Print("[SDL2] Invalid controller id {0} in {1}", id, ev.Type); + return; + } + + switch (ev.Type) + { + case EventType.CONTROLLERDEVICEADDED: + IntPtr handle = SDL.GameControllerOpen(id); + if (handle != IntPtr.Zero) + { + // The id variable here corresponds to a device_id between 0 and Sdl.NumJoysticks(). + // It is only used in the ADDED event. All other events use an instance_id which increases + // monotonically in each ADDED event. + // The idea is that device_id refers to the n-th connected joystick, whereas instance_id + // refers to the actual hardware device behind the n-th joystick. + // Yes, it's confusing. + int device_id = id; + int instance_id = last_controllers_instance++; + + Sdl2GamePad pad = new Sdl2GamePad(handle); + + IntPtr joystick = SDL.GameControllerGetJoystick(handle); + if (joystick != IntPtr.Zero) + { + pad.Capabilities = new GamePadCapabilities( + GamePadType.GamePad, + GetBoundAxes(joystick), + GetBoundButtons(joystick), + true); + pad.State.SetConnected(true); + + // Connect this device and add the relevant device index + if (controllers.Count <= id) + { + controllers.Add(pad); + } + else + { + controllers[device_id] = pad; + } + + sdl_instanceid_to_controllers.Add(instance_id, device_id); + } + else + { + Debug.Print("[SDL2] Failed to retrieve joystick from game controller. Error: {0}", SDL.GetError()); + } + } + break; + + case EventType.CONTROLLERDEVICEREMOVED: + if (IsControllerInstanceValid(id)) + { + int instance_id = id; + int device_id = sdl_instanceid_to_controllers[instance_id]; + + controllers[device_id].State.SetConnected(false); + sdl_instanceid_to_controllers.Remove(device_id); + } + else + { + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type); + } + break; + + case EventType.CONTROLLERDEVICEREMAPPED: + if (IsControllerInstanceValid(id)) + { + // Todo: what should we do in this case? + } + else + { + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type); + } + break; + } + } + + public void ProcessControllerEvent(ControllerAxisEvent ev) + { + int instance_id = ev.Which; + if (IsControllerInstanceValid(instance_id)) + { + int id = sdl_instanceid_to_controllers[instance_id]; + controllers[id].State.SetAxis(TranslateAxis(ev.Axis), ev.Value); + } + else + { + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type); + } + } + + public void ProcessControllerEvent(ControllerButtonEvent ev) + { + int instance_id = ev.Which; + if (IsControllerInstanceValid(instance_id)) + { + int id = sdl_instanceid_to_controllers[instance_id]; + controllers[id].State.SetButton(TranslateButton(ev.Button), ev.State == State.Pressed); + } + else + { + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type); + } + } +#endif + #endregion #region IJoystickDriver Members @@ -101,49 +522,114 @@ namespace OpenTK.Platform.SDL2 public void Poll() { - SDL.JoystickUpdate(); - foreach (var j in joysticks) - { - var joystick = (JoystickDevice)j; - IntPtr handle = joystick.Details.Handle; - - for (int i = 0; i < joystick.Axis.Count; i++) - { - var axis = JoystickAxis.Axis0 + i; - joystick.SetAxis(axis, SDL.JoystickGetAxis(handle, i) * joystick.Details.RangeMultiplier); - } - - for (int i = 0; i < joystick.Button.Count; i++) - { - var button = JoystickButton.Button0 + i; - joystick.SetButton(button, SDL.JoystickGetButton(handle, i) != 0); - } - } + // Do nothing } #endregion #region IGamePadDriver Members - public GamePadState GetState() +#if USE_SDL2_GAMECONTOLLER + public GamePadCapabilities GetCapabilities(int index) { - return new GamePadState(); + if (IsControllerValid(index)) + { + return controllers[index].Capabilities; + } + return new GamePadCapabilities(); } public GamePadState GetState(int index) { - if (joysticks.Count >= index) + if (IsControllerValid(index)) { - + return controllers[index].State; } - return new GamePadState(); } - public string GetDeviceName(int index) + public string GetName(int index) { return String.Empty; } +#else + public GamePadCapabilities GetCapabilities(int index) + { + return gamepad_driver.GetCapabilities(index); + } + + public GamePadState GetState(int index) + { + return gamepad_driver.GetState(index); + } + + public string GetName(int index) + { + return gamepad_driver.GetName(index); + } + + public bool SetVibration(int index, float left, float right) + { + return false; + } +#endif + + #endregion + + #region IJoystickDriver2 Members + + JoystickState IJoystickDriver2.GetState(int index) + { + JoystickState state = new JoystickState(); + if (IsJoystickValid(index)) + { + JoystickDevice joystick = + (JoystickDevice)joysticks[index]; + + for (int i = 0; i < joystick.Axis.Count; i++) + { + state.SetAxis(JoystickAxis.Axis0 + i, (short)(joystick.Axis[i] * short.MaxValue + 0.5f)); + } + + for (int i = 0; i < joystick.Button.Count; i++) + { + state.SetButton(JoystickButton.Button0 + i, joystick.Button[i]); + } + + state.SetIsConnected(joystick.Details.IsConnected); + state.SetPacketNumber(joystick.Details.PacketNumber); + } + + return state; + } + + JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) + { + if (IsJoystickValid(index)) + { + JoystickDevice joystick = + (JoystickDevice)joysticks[index]; + + return new JoystickCapabilities( + joystick.Axis.Count, + joystick.Button.Count, + joystick.Details.IsConnected); + } + return new JoystickCapabilities(); + } + + Guid IJoystickDriver2.GetGuid(int index) + { + Guid guid = new Guid(); + if (IsJoystickValid(index)) + { + JoystickDevice joystick = + (JoystickDevice)joysticks[index]; + + return joystick.Details.Guid; + } + return guid; + } #endregion diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index dd26d534..9f24fdf7 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -70,7 +70,7 @@ namespace OpenTK.Platform.SDL2 // Argument for KeyDown and KeyUp events (allocated once to avoid runtime allocations) readonly KeyboardKeyEventArgs key_args = new KeyboardKeyEventArgs(); - readonly IInputDriver input_driver = new Sdl2InputDriver(); + readonly IInputDriver input_driver; readonly EventFilter EventFilterDelegate_GCUnsafe = FilterEvents; readonly IntPtr EventFilterDelegate; @@ -81,10 +81,13 @@ namespace OpenTK.Platform.SDL2 static readonly Sdl2KeyMap map = new Sdl2KeyMap(); public Sdl2NativeWindow(int x, int y, int width, int height, - string title, GameWindowFlags options, DisplayDevice device) + string title, GameWindowFlags options, DisplayDevice device, + IInputDriver input_driver) { lock (sync) { + this.input_driver = input_driver; + var bounds = device.Bounds; var flags = TranslateFlags(options); flags |= WindowFlags.OPENGL; diff --git a/Source/OpenTK/Platform/Windows/WinFactory.cs b/Source/OpenTK/Platform/Windows/WinFactory.cs index 5ea1a8eb..4fa46e2e 100644 --- a/Source/OpenTK/Platform/Windows/WinFactory.cs +++ b/Source/OpenTK/Platform/Windows/WinFactory.cs @@ -130,7 +130,12 @@ namespace OpenTK.Platform.Windows { return InputDriver.GamePadDriver; } - + + public IJoystickDriver2 CreateJoystickDriver() + { + return InputDriver.JoystickDriver; + } + #endregion IInputDriver2 InputDriver diff --git a/Source/OpenTK/Platform/Windows/WinInputBase.cs b/Source/OpenTK/Platform/Windows/WinInputBase.cs index 252a6f8d..7d0865f1 100644 --- a/Source/OpenTK/Platform/Windows/WinInputBase.cs +++ b/Source/OpenTK/Platform/Windows/WinInputBase.cs @@ -164,6 +164,8 @@ namespace OpenTK.Platform.Windows public abstract IKeyboardDriver2 KeyboardDriver { get; } public abstract IGamePadDriver GamePadDriver { get; } + public abstract IJoystickDriver2 JoystickDriver { get; } + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index 32a00c7a..313abdd9 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -36,7 +36,7 @@ using System.Diagnostics; namespace OpenTK.Platform.Windows { - sealed class WinMMJoystick : IJoystickDriver, IGamePadDriver + sealed class WinMMJoystick : IJoystickDriver, IJoystickDriver2 { #region Fields @@ -133,6 +133,27 @@ namespace OpenTK.Platform.Windows return stick; } + void UnplugJoystick(int index) + { + // Reset the system configuration. Without this, + // joysticks that are reconnected on different + // ports are given different ids, making it + // impossible to reconnect a disconnected user. + UnsafeNativeMethods.joyConfigChanged(0); + Debug.Print("[Win] WinMM joystick {0} unplugged", index); + } + + bool IsValid(int index) + { + return index >= 0 && index < UnsafeNativeMethods.joyGetNumDevs(); + } + + static short CalculateOffset(int pos, int min, int max) + { + int offset = (ushort.MaxValue * (pos - min)) / (max - min) - short.MaxValue; + return (short)offset; + } + #endregion #region IJoystickDriver @@ -237,6 +258,95 @@ namespace OpenTK.Platform.Windows #endregion + #region IJoystickDriver2 Members + + public JoystickCapabilities GetCapabilities(int index) + { + if (IsValid(index)) + { + JoyCaps mmcaps; + JoystickError result = UnsafeNativeMethods.joyGetDevCaps(index, out mmcaps, JoyCaps.SizeInBytes); + if (result == JoystickError.NoError) + { + JoystickCapabilities caps = new JoystickCapabilities( + mmcaps.NumAxes, mmcaps.NumButtons, true); + //if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0) + // gpcaps.DPadCount++; + return caps; + } + else if (result == JoystickError.Unplugged) + { + UnplugJoystick(index); + } + } + else + { + Debug.Print("[Win] Invalid WinMM joystick device {0}", index); + } + + return new JoystickCapabilities(); + } + + public JoystickState GetState(int index) + { + JoystickState state = new JoystickState(); + + if (IsValid(index)) + { + JoyInfoEx info = new JoyInfoEx(); + info.Size = JoyInfoEx.SizeInBytes; + info.Flags = JoystickFlags.All; + + JoystickError result = UnsafeNativeMethods.joyGetPosEx(index, ref info); + if (result == JoystickError.NoError) + { + JoyCaps caps; + result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes); + if (result == JoystickError.NoError) + { + state.SetAxis(JoystickAxis.Axis0, CalculateOffset(info.XPos, caps.XMin, caps.XMax)); + state.SetAxis(JoystickAxis.Axis1, CalculateOffset(info.YPos, caps.YMin, caps.YMax)); + state.SetAxis(JoystickAxis.Axis2, CalculateOffset(info.ZPos, caps.ZMin, caps.ZMax)); + state.SetAxis(JoystickAxis.Axis3, CalculateOffset(info.RPos, caps.RMin, caps.RMax)); + state.SetAxis(JoystickAxis.Axis4, CalculateOffset(info.UPos, caps.UMin, caps.UMax)); + state.SetAxis(JoystickAxis.Axis5, CalculateOffset(info.VPos, caps.VMin, caps.VMax)); + + for (int i = 0; i < 16; i++) + { + state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0); + } + + state.SetIsConnected(true); + } + } + + if (result == JoystickError.Unplugged) + { + UnplugJoystick(index); + } + } + else + { + Debug.Print("[Win] Invalid WinMM joystick device {0}", index); + } + + return state; + } + + public Guid GetGuid(int index) + { + Guid guid = new Guid(); + + if (IsValid(index)) + { + // Todo: implement WinMM Guid retrieval + } + + return guid; + } + + #endregion + #region IDisposable public void Dispose() @@ -378,9 +488,11 @@ namespace OpenTK.Platform.Windows [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] public static extern JoystickError joyGetDevCaps(int uJoyID, out JoyCaps pjc, int cbjc); [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] - public static extern uint joyGetPosEx(int uJoyID, ref JoyInfoEx pji); + public static extern JoystickError joyGetPosEx(int uJoyID, ref JoyInfoEx pji); [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] public static extern int joyGetNumDevs(); + [DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity] + public static extern JoystickError joyConfigChanged(int flags); } #endregion @@ -427,21 +539,5 @@ namespace OpenTK.Platform.Windows } #endregion - - //HACK implement - public GamePadState GetState() - { - throw new NotImplementedException(); - } - - public GamePadState GetState(int index) - { - throw new NotImplementedException(); - } - - public string GetDeviceName(int index) - { - throw new NotImplementedException(); - } } } diff --git a/Source/OpenTK/Platform/Windows/WinRawInput.cs b/Source/OpenTK/Platform/Windows/WinRawInput.cs index c905b65b..08e57201 100644 --- a/Source/OpenTK/Platform/Windows/WinRawInput.cs +++ b/Source/OpenTK/Platform/Windows/WinRawInput.cs @@ -43,7 +43,8 @@ namespace OpenTK.Platform.Windows WinRawKeyboard keyboard_driver; WinRawMouse mouse_driver; - WinMMJoystick joystick_driver; + IGamePadDriver gamepad_driver; + IJoystickDriver2 joystick_driver; IntPtr DevNotifyHandle; static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030"); @@ -138,6 +139,15 @@ namespace OpenTK.Platform.Windows keyboard_driver = new WinRawKeyboard(Parent.Handle); mouse_driver = new WinRawMouse(Parent.Handle); joystick_driver = new WinMMJoystick(); + try + { + gamepad_driver = new XInputJoystick(); + } + catch (Exception e) + { + Debug.Print("[Win] XInput driver not supported, falling back to WinMM"); + gamepad_driver = new MappedGamePadDriver(); + } DevNotifyHandle = RegisterForDeviceNotifications(Parent); } @@ -186,6 +196,11 @@ namespace OpenTK.Platform.Windows } public override IGamePadDriver GamePadDriver + { + get { return gamepad_driver; } + } + + public override IJoystickDriver2 JoystickDriver { get { return joystick_driver; } } diff --git a/Source/OpenTK/Platform/Windows/XInputJoystick.cs b/Source/OpenTK/Platform/Windows/XInputJoystick.cs new file mode 100644 index 00000000..910a7f01 --- /dev/null +++ b/Source/OpenTK/Platform/Windows/XInputJoystick.cs @@ -0,0 +1,405 @@ +#region License +// +// XInputJoystick.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using OpenTK.Input; +using System.Runtime.InteropServices; +using System.Security; +using System.Diagnostics; + +namespace OpenTK.Platform.Windows +{ + class XInputJoystick : IGamePadDriver, IDisposable + { + XInput xinput = new XInput(); + + #region IGamePadDriver Members + + public GamePadState GetState(int index) + { + XInputState xstate; + XInputErrorCode error = xinput.GetState((XInputUserIndex)index, out xstate); + + GamePadState state = new GamePadState(); + if (error == XInputErrorCode.Success) + { + state.SetConnected(true); + + state.SetAxis(GamePadAxes.LeftX, xstate.GamePad.ThumbLX); + state.SetAxis(GamePadAxes.LeftY, xstate.GamePad.ThumbLY); + state.SetAxis(GamePadAxes.RightX, xstate.GamePad.ThumbRX); + state.SetAxis(GamePadAxes.RightY, xstate.GamePad.ThumbRY); + + state.SetTriggers(xstate.GamePad.LeftTrigger, xstate.GamePad.RightTrigger); + + state.SetButton(TranslateButtons(xstate.GamePad.Buttons), true); + } + + return state; + } + + public GamePadCapabilities GetCapabilities(int index) + { + XInputDeviceCapabilities xcaps; + XInputErrorCode error = xinput.GetCapabilities( + (XInputUserIndex)index, + XInputCapabilitiesFlags.Default, + out xcaps); + + if (error == XInputErrorCode.Success) + { + GamePadType type = TranslateSubType(xcaps.SubType); + Buttons buttons = TranslateButtons(xcaps.GamePad.Buttons); + GamePadAxes axes = TranslateAxes(ref xcaps.GamePad); + + return new GamePadCapabilities(type, axes, buttons, true); + } + return new GamePadCapabilities(); + } + + public string GetName(int index) + { + return String.Empty; + } + + public bool SetVibration(int index, float left, float right) + { + return false; + } + + #endregion + + #region Private Members + GamePadAxes TranslateAxes(ref XInputGamePad pad) + { + GamePadAxes axes = 0; + axes |= pad.ThumbLX != 0 ? GamePadAxes.LeftX : 0; + axes |= pad.ThumbLY != 0 ? GamePadAxes.LeftY : 0; + axes |= pad.LeftTrigger != 0 ? GamePadAxes.LeftTrigger : 0; + axes |= pad.ThumbRX != 0 ? GamePadAxes.RightX : 0; + axes |= pad.ThumbRY != 0 ? GamePadAxes.RightY : 0; + axes |= pad.RightTrigger != 0 ? GamePadAxes.RightTrigger : 0; + return axes; + } + + Buttons TranslateButtons(XInputButtons xbuttons) + { + Buttons buttons = 0; + buttons |= (xbuttons & XInputButtons.A) != 0 ? Buttons.A : 0; + buttons |= (xbuttons & XInputButtons.B) != 0 ? Buttons.B : 0; + buttons |= (xbuttons & XInputButtons.X) != 0 ? Buttons.X : 0; + buttons |= (xbuttons & XInputButtons.Y) != 0 ? Buttons.Y : 0; + buttons |= (xbuttons & XInputButtons.Start) != 0 ? Buttons.Start : 0; + buttons |= (xbuttons & XInputButtons.Back) != 0 ? Buttons.Back : 0; + //buttons |= (xbuttons & XInputButtons.BigButton) != 0 ? Buttons.BigButton : 0; + buttons |= (xbuttons & XInputButtons.LeftShoulder) != 0 ? Buttons.LeftShoulder : 0; + buttons |= (xbuttons & XInputButtons.RightShoulder) != 0 ? Buttons.RightShoulder : 0; + buttons |= (xbuttons & XInputButtons.LeftThumb) != 0 ? Buttons.LeftStick : 0; + buttons |= (xbuttons & XInputButtons.RightThumb) != 0 ? Buttons.RightStick : 0; + buttons |= (xbuttons & XInputButtons.DPadDown) != 0 ? Buttons.DPadDown : 0; + buttons |= (xbuttons & XInputButtons.DPadUp) != 0 ? Buttons.DPadUp : 0; + buttons |= (xbuttons & XInputButtons.DPadLeft) != 0 ? Buttons.DPadLeft : 0; + buttons |= (xbuttons & XInputButtons.DPadRight) != 0 ? Buttons.DPadRight : 0; + return buttons; + } + + GamePadType TranslateSubType(XInputDeviceSubType xtype) + { + switch (xtype) + { + case XInputDeviceSubType.ArcadePad: return GamePadType.ArcadePad; + case XInputDeviceSubType.ArcadeStick: return GamePadType.ArcadeStick; + case XInputDeviceSubType.DancePad: return GamePadType.DancePad; + case XInputDeviceSubType.DrumKit: return GamePadType.DrumKit; + case XInputDeviceSubType.FlightStick: return GamePadType.FlightStick; + case XInputDeviceSubType.GamePad: return GamePadType.GamePad; + case XInputDeviceSubType.Guitar: return GamePadType.Guitar; + case XInputDeviceSubType.GuitarAlternate: return GamePadType.AlternateGuitar; + case XInputDeviceSubType.GuitarBass: return GamePadType.BassGuitar; + case XInputDeviceSubType.Wheel: return GamePadType.Wheel; + case XInputDeviceSubType.Unknown: + default: + return GamePadType.Unknown; + } + } + + enum XInputErrorCode + { + Success = 0, + DeviceNotConnected + } + + enum XInputDeviceType : byte + { + GamePad + } + + enum XInputDeviceSubType : byte + { + Unknown = 0, + GamePad = 1, + Wheel = 2, + ArcadeStick = 3, + FlightStick = 4, + DancePad = 5, + Guitar = 6, + GuitarAlternate = 7, + DrumKit = 8, + GuitarBass = 0xb, + ArcadePad = 0x13 + } + + enum XInputCapabilities + { + ForceFeedback = 0x0001, + Wireless = 0x0002, + Voice = 0x0004, + PluginModules = 0x0008, + NoNavigation = 0x0010, + } + + enum XInputButtons : ushort + { + DPadUp = 0x0001, + DPadDown = 0x0002, + DPadLeft = 0x0004, + DPadRight = 0x0008, + Start = 0x0010, + Back = 0x0020, + LeftThumb = 0x0040, + RightThumb = 0x0080, + LeftShoulder = 0x0100, + RightShoulder = 0x0200, + A = 0x1000, + B = 0x2000, + X = 0x4000, + Y = 0x8000 + } + + [Flags] + enum XInputCapabilitiesFlags + { + Default = 0, + GamePadOnly = 1 + } + + enum XInputBatteryType : byte + { + Disconnected = 0x00, + Wired = 0x01, + Alkaline = 0x02, + NiMH = 0x03, + Unknown = 0xff + } + + enum XInputBatteryLevel : byte + { + Empty = 0x00, + Low = 0x01, + Medium = 0x02, + Full = 0x03 + } + + enum XInputUserIndex + { + First = 0, + Second, + Third, + Fourth, + Any = 0xff + } + + struct XInputThresholds + { + public const int LeftThumbDeadzone = 7849; + public const int RightThumbDeadzone = 8689; + public const int TriggerThreshold = 30; + } + + struct XInputGamePad + { + public XInputButtons Buttons; + public byte LeftTrigger; + public byte RightTrigger; + public short ThumbLX; + public short ThumbLY; + public short ThumbRX; + public short ThumbRY; + } + + struct XInputState + { + public int PacketNumber; + public XInputGamePad GamePad; + } + + struct XInputVibration + { + public short LeftMotorSpeed; + public short RightMotorSpeed; + } + + struct XInputDeviceCapabilities + { + public XInputDeviceType Type; + public XInputDeviceSubType SubType; + public short Flags; + public XInputGamePad GamePad; + public XInputVibration Vibration; + } + + struct XInputBatteryInformation + { + public XInputBatteryType Type; + public XInputBatteryLevel Level; + } + + class XInput : IDisposable + { + IntPtr dll; + + internal XInput() + { + // Try to load the newest XInput***.dll installed on the system + // The delegates below will be loaded dynamically from that dll + dll = Functions.LoadLibrary("XINPUT1_4"); + if (dll == IntPtr.Zero) + dll = Functions.LoadLibrary("XINPUT1_3"); + if (dll == IntPtr.Zero) + dll = Functions.LoadLibrary("XINPUT1_2"); + if (dll == IntPtr.Zero) + dll = Functions.LoadLibrary("XINPUT1_1"); + if (dll == IntPtr.Zero) + dll = Functions.LoadLibrary("XINPUT9_1_0"); + if (dll == IntPtr.Zero) + throw new NotSupportedException("XInput was not found on this platform"); + + // Load the entry points we are interested in from that dll + GetCapabilities = (XInputGetCapabilities)Load("XInputGetCapabilities", typeof(XInputGetCapabilities)); + GetState = (XInputGetState)Load("XInputGetState", typeof(XInputGetState)); + SetState = (XInputSetState)Load("XInputSetState", typeof(XInputSetState)); + } + + #region Private Members + + Delegate Load(string name, Type type) + { + IntPtr pfunc = Functions.GetProcAddress(dll, name); + if (pfunc != IntPtr.Zero) + return Marshal.GetDelegateForFunctionPointer(pfunc, type); + return null; + } + + #endregion + + #region Internal Members + + internal XInputGetCapabilities GetCapabilities; + internal XInputGetState GetState; + internal XInputSetState SetState; + + [SuppressUnmanagedCodeSecurity] + internal delegate XInputErrorCode XInputGetCapabilities( + XInputUserIndex dwUserIndex, + XInputCapabilitiesFlags dwFlags, + out XInputDeviceCapabilities pCapabilities); + + [SuppressUnmanagedCodeSecurity] + internal delegate XInputErrorCode XInputGetState + ( + XInputUserIndex dwUserIndex, + out XInputState pState + ); + + [SuppressUnmanagedCodeSecurity] + internal delegate XInputErrorCode XInputSetState + ( + XInputUserIndex dwUserIndex, + ref XInputVibration pVibration + ); + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool manual) + { + if (manual) + { + if (dll != IntPtr.Zero) + { + Functions.FreeLibrary(dll); + dll = IntPtr.Zero; + } + } + } + + #endregion + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool manual) + { + if (manual) + { + xinput.Dispose(); + } + else + { + Debug.Print("{0} leaked, did you forget to call Dispose()?", typeof(XInputJoystick).Name); + } + } + +#if DEBUG + ~XInputJoystick() + { + Dispose(false); + } +#endif + + #endregion + } +} diff --git a/Source/OpenTK/Platform/X11/X11Factory.cs b/Source/OpenTK/Platform/X11/X11Factory.cs index 8b8344df..0fb80058 100644 --- a/Source/OpenTK/Platform/X11/X11Factory.cs +++ b/Source/OpenTK/Platform/X11/X11Factory.cs @@ -98,6 +98,12 @@ namespace OpenTK.Platform.X11 return new X11Joystick(); } + public virtual OpenTK.Input.IJoystickDriver2 CreateJoystickDriver() + { + return new X11Joystick(); + } + + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index 85299eb1..cbab7099 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -36,7 +36,7 @@ namespace OpenTK.Platform.X11 { struct X11JoyDetails { } - sealed class X11Joystick : IJoystickDriver, IGamePadDriver + sealed class X11Joystick : IJoystickDriver, IJoystickDriver2, IGamePadDriver { #region Fields @@ -259,21 +259,47 @@ namespace OpenTK.Platform.X11 #endregion - //HACK implement - public GamePadState GetState() + #region IGamePadDriver Members + + public GamePadCapabilities GetCapabilities(int index) { - throw new NotImplementedException(); + return new GamePadCapabilities(); } public GamePadState GetState(int index) { - Poll(); - throw new NotImplementedException(); + return new GamePadState(); } - public string GetDeviceName(int index) + public string GetName(int index) { - throw new NotImplementedException(); + return String.Empty; } + + public bool SetVibration(int index, float left, float right) + { + return false; + } + + #endregion + + #region IJoystickDriver2 Members + + JoystickState IJoystickDriver2.GetState(int index) + { + return new JoystickState(); + } + + JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) + { + return new JoystickCapabilities(); + } + + Guid IJoystickDriver2.GetGuid(int index) + { + return new Guid(); + } + + #endregion } } diff --git a/Source/OpenTK/ToolkitOptions.cs b/Source/OpenTK/ToolkitOptions.cs index 91a16a9a..5541a2bb 100644 --- a/Source/OpenTK/ToolkitOptions.cs +++ b/Source/OpenTK/ToolkitOptions.cs @@ -84,7 +84,7 @@ namespace OpenTK /// Set to false for applications that are not /// DPI-aware (e.g. WinForms.) /// - /// + /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ee308410(v=vs.85).aspx public bool EnableHighResolution { get; set; } ///