From 427b3641a01cb57256a6a0e71159e27b29b52517 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 18 Dec 2013 15:50:59 +0100 Subject: [PATCH 01/69] Implemented SDL2 Joystick and GameController events --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 152 +++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 14 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 9bf07558..60e72876 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -500,6 +500,53 @@ 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 + } + + [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, @@ -1079,6 +1126,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; @@ -1109,21 +1191,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)] @@ -1154,6 +1236,48 @@ 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 KeyboardEvent { public EventType Type; From 9dd97cb3e1b087c61d7df7a78da022ed28e84563 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 18 Dec 2013 17:16:29 +0100 Subject: [PATCH 02/69] Use SDL2 event API for joystick devices --- .../OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 38 +++- .../Platform/SDL2/Sdl2JoystickDriver.cs | 211 ++++++++++++++---- 2 files changed, 204 insertions(+), 45 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index 7a571b14..de174875 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -92,6 +92,42 @@ 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; + + 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; } } } @@ -172,7 +208,7 @@ namespace OpenTK.Platform.SDL2 { get { - throw new NotImplementedException(); + return joystick_driver; } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 729cee13..380bbd67 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -40,6 +40,7 @@ namespace OpenTK.Platform.SDL2 public float RangeMultiplier { get { return 1.0f / 32768.0f; } } public int HatCount { get; set; } public int BallCount { get; set; } + public bool IsConnected { get; set; } } readonly List joysticks = new List(); @@ -49,42 +50,181 @@ namespace OpenTK.Platform.SDL2 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.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; + } + + #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: + { + // Make sure we have enough space to store this instance + if (joysticks.Count <= id) + { + joysticks.Capacity = OpenTK.MathHelper.NextPowerOfTwo(id); + } + + IntPtr handle = SDL.JoystickOpen(id); + if (handle != IntPtr.Zero) + { + JoystickDevice joystick = OpenJoystick(id); + if (joysticks != null) + { + joystick.Details.IsConnected = true; + joysticks[id] = joystick; + } + } + } + break; + + case EventType.JOYDEVICEREMOVED: + { + JoystickDevice joystick = (JoystickDevice)joysticks[id]; + joystick.Details.IsConnected = false; + } + break; + } + } + + public void ProcessJoystickEvent(JoyAxisEvent ev) + { + int id = ev.Which; + if (IsJoystickValid(id)) + { + JoystickDevice joystick = (JoystickDevice)joysticks[id]; + float value = ev.Value * joystick.Details.RangeMultiplier; + joystick.SetAxis((JoystickAxis)ev.Axis, value); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessJoystickEvent(JoyBallEvent ev) + { + int id = ev.Which; + if (IsJoystickValid(id)) + { + JoystickDevice joystick = (JoystickDevice)joysticks[id]; + // Todo: does it make sense to support balls? + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessJoystickEvent(JoyButtonEvent ev) + { + int id = ev.Which; + if (IsJoystickValid(id)) + { + JoystickDevice joystick = (JoystickDevice)joysticks[id]; + joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed); + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessJoystickEvent(JoyHatEvent ev) + { + int id = ev.Which; + if (IsJoystickValid(id)) + { + JoystickDevice joystick = (JoystickDevice)joysticks[id]; + // Todo: map hat to an extra axis + } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } + } + + public void ProcessControllerEvent(ControllerDeviceEvent ev) + { + int id = ev.Which; + + switch (ev.Type) + { + case EventType.CONTROLLERDEVICEADDED: + break; + + case EventType.CONTROLLERDEVICEREMOVED: + break; + + case EventType.CONTROLLERDEVICEREMAPPED: + break; + } + } + + public void ProcessControllerEvent(ControllerAxisEvent ev) + { + int id = ev.Which; + + + } + + public void ProcessControllerEvent(ControllerButtonEvent ev) + { + int id = ev.Which; + + } #endregion @@ -101,24 +241,7 @@ 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 From 9b98228240f3f281b119f43be49abb22f5a6e05f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 10:42:12 +0100 Subject: [PATCH 03/69] Implemented GameController API bindings --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 54 +++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 60e72876..2232e686 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,6 +116,49 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)] public static extern void FreeSurface(IntPtr surface); + [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)); + } + } + + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetAxis", ExactSpelling = true)] + public static extern short GameControllerGetAxis(IntPtr gamecontroller, GameControllerAxis axis); + + /// > + /// 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); + + /// + /// 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); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetCurrentDisplayMode", ExactSpelling = true)] public static extern int GetCurrentDisplayMode(int displayIndex, out DisplayMode mode); @@ -192,6 +233,15 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_Init", ExactSpelling = true)] public static extern int Init(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); From a4d7d79b95f4be1b51fd913759f050171066428f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 16:27:26 +0100 Subject: [PATCH 04/69] Updated internal IGamePadDriver interface --- Source/OpenTK/Input/IGamePadDriver.cs | 14 +--- .../Platform/SDL2/Sdl2JoystickDriver.cs | 66 ++++++++++++++++--- .../OpenTK/Platform/Windows/WinMMJoystick.cs | 9 ++- Source/OpenTK/Platform/X11/X11Joystick.cs | 10 +-- 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/Source/OpenTK/Input/IGamePadDriver.cs b/Source/OpenTK/Input/IGamePadDriver.cs index 718d1bc1..fc24c8f8 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,6 @@ 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); } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 380bbd67..2af3f977 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -34,18 +34,32 @@ namespace OpenTK.Platform.SDL2 { class Sdl2JoystickDriver : IJoystickDriver, IGamePadDriver, IDisposable { + const float RangeMultiplier = 1.0f / 32768.0f; + struct Sdl2JoystickDetails { public IntPtr Handle { get; set; } - public float RangeMultiplier { get { return 1.0f / 32768.0f; } } public int HatCount { get; set; } public int BallCount { get; set; } public bool IsConnected { get; set; } } + class Sdl2GamePad + { + public IntPtr Handle { get; private set; } + public GamePadState State { get; set; } + + public Sdl2GamePad(IntPtr handle) + { + Handle = handle; + } + } + readonly List joysticks = new List(); + readonly Dictionary controllers = new Dictionary(); + IList joysticks_readonly; - bool disposed = false; + bool disposed; public Sdl2JoystickDriver() { @@ -94,6 +108,11 @@ namespace OpenTK.Platform.SDL2 return id >= 0 && id < joysticks.Count; } + bool IsControllerValid(int id) + { + return controllers.ContainsKey(id); + } + #endregion #region Public Members @@ -145,7 +164,7 @@ namespace OpenTK.Platform.SDL2 if (IsJoystickValid(id)) { JoystickDevice joystick = (JoystickDevice)joysticks[id]; - float value = ev.Value * joystick.Details.RangeMultiplier; + float value = ev.Value * RangeMultiplier; joystick.SetAxis((JoystickAxis)ev.Axis, value); } else @@ -203,12 +222,27 @@ namespace OpenTK.Platform.SDL2 switch (ev.Type) { case EventType.CONTROLLERDEVICEADDED: + if (SDL.IsGameController(id)) + { + IntPtr handle = SDL.GameControllerOpen(id); + if (handle != IntPtr.Zero) + { + Sdl2GamePad pad = new Sdl2GamePad(handle); + pad.State.SetConnected(true); + controllers.Add(id, pad); + } + } break; case EventType.CONTROLLERDEVICEREMOVED: + if (IsControllerValid(id)) + { + controllers[id].State.SetConnected(false); + } break; case EventType.CONTROLLERDEVICEREMAPPED: + // Todo: what should we do in this case? break; } } @@ -216,15 +250,27 @@ namespace OpenTK.Platform.SDL2 public void ProcessControllerEvent(ControllerAxisEvent ev) { int id = ev.Which; - - + if (IsControllerValid(id)) + { + controllers[id].State.SetAxis((GamePadAxis)ev.Axis, ev.Value); + } + else + { + Debug.Print("[SDL2] Invalid game controller handle {0} in {1}", id, ev.Type); + } } public void ProcessControllerEvent(ControllerButtonEvent ev) { int id = ev.Which; - - + if (IsControllerValid(id)) + { + controllers[id].State.SetButton((Buttons)ev.Button, ev.State == State.Pressed); + } + else + { + Debug.Print("[SDL2] Invalid game controller handle {0} in {1}", id, ev.Type); + } } #endregion @@ -248,9 +294,9 @@ namespace OpenTK.Platform.SDL2 #region IGamePadDriver Members - public GamePadState GetState() + public GamePadCapabilities GetCapabilities(int index) { - return new GamePadState(); + return new GamePadCapabilities(); } public GamePadState GetState(int index) @@ -263,7 +309,7 @@ namespace OpenTK.Platform.SDL2 return new GamePadState(); } - public string GetDeviceName(int index) + public string GetName(int index) { return String.Empty; } diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index 32a00c7a..ca229700 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -428,8 +428,9 @@ namespace OpenTK.Platform.Windows #endregion - //HACK implement - public GamePadState GetState() + #region IGamePadDriver Members + + public GamePadCapabilities GetCapabilities(int index) { throw new NotImplementedException(); } @@ -439,9 +440,11 @@ namespace OpenTK.Platform.Windows throw new NotImplementedException(); } - public string GetDeviceName(int index) + public string GetName(int index) { throw new NotImplementedException(); } + + #endregion } } diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index 85299eb1..b4948169 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -259,21 +259,23 @@ namespace OpenTK.Platform.X11 #endregion - //HACK implement - public GamePadState GetState() + #region IGamePadDriver Members + + public GamePadCapabilities GetCapabilities(int index) { throw new NotImplementedException(); } public GamePadState GetState(int index) { - Poll(); throw new NotImplementedException(); } - public string GetDeviceName(int index) + public string GetName(int index) { throw new NotImplementedException(); } + + #endregion } } From 9374b6b41b0c773ae3cf31c3c0b4edeaf5f1b5c6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 16:27:57 +0100 Subject: [PATCH 05/69] Implemented GamePadButtons --- Source/OpenTK/Input/Buttons.cs | 213 ++++++++++++++++++++++++++ Source/OpenTK/Input/GamePadButton.cs | 68 -------- Source/OpenTK/Input/GamePadButtons.cs | 131 ++++++++++++++++ 3 files changed, 344 insertions(+), 68 deletions(-) create mode 100644 Source/OpenTK/Input/Buttons.cs delete mode 100644 Source/OpenTK/Input/GamePadButton.cs create mode 100644 Source/OpenTK/Input/GamePadButtons.cs diff --git a/Source/OpenTK/Input/Buttons.cs b/Source/OpenTK/Input/Buttons.cs new file mode 100644 index 00000000..739a8fcd --- /dev/null +++ b/Source/OpenTK/Input/Buttons.cs @@ -0,0 +1,213 @@ +// +// 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 +{ + 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, + + /// The first button of the gamepad. + Button0 = A, + /// The second button of the gamepad. + Button1 = B, + /// The third button of the gamepad. + Button2 = X, + /// The fourth button of the gamepad. + Button3 = Y, + /// The fifth button of the gamepad. + Button4 = Start, + /// The sixth button of the gamepad. + Button5 = Back, + /// The seventh button of the gamepad. + Button6 = LeftStick, + /// The eighth button of the gamepad. + Button7 = RightStick, + /// The ninth button of the gamepad. + Button8 = LeftShoulder, + /// The tenth button of the gamepad. + Button9 = RightShoulder, + /// The eleventh button of the gamepad. + Button10 = Home, + /// The twelfth button of the gamepad. + Button11 = DPadUp, + /// The thirteenth button of the gamepad. + Button12 = DPadDown, + /// The fourteenth button of the gamepad. + Button13 = DPadLeft, + /// The fifteenth button of the gamepad. + Button14 = DPadRight, + /// The sixteenth button of the gamepad. + Button15 = LeftTrigger, + /// The seventeenth button of the gamepad. + Button16 = RightTrigger, + /// The eighteenth button of the gamepad. + Button17 = LeftThumbstickUp, + /// The nineteenth button of the gamepad. + Button18 = LeftThumbstickDown, + /// The twentieth button of the gamepad. + Button19 = LeftThumbstickLeft, + /// The twentieth-one button of the gamepad. + Button20 = LeftThumbstickRight, + /// The twentieth-one button of the gamepad. + Button21 = RightThumbstickUp, + /// The twentieth-one button of the gamepad. + Button22 = RightThumbstickDown, + /// The twentieth-one button of the gamepad. + Button23 = RightThumbstickLeft, + /// The twentieth-one button of the gamepad. + Button24 = RightThumbstickRight, + } +} 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..199e593f --- /dev/null +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -0,0 +1,131 @@ +// #region License +// +// GamePadButtons.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 +{ + + public struct GamePadButtons : IEquatable + { + Buttons buttons; + + public GamePadButtons(Buttons state) + { + buttons = state; + } + + #region Public Members + + public ButtonState A + { + get { return GetButton(Buttons.A); } + } + + public ButtonState B + { + get { return GetButton(Buttons.B); } + } + + public ButtonState X + { + get { return GetButton(Buttons.X); } + } + + public ButtonState Y + { + get { return GetButton(Buttons.Y); } + } + + public ButtonState Back + { + get { return GetButton(Buttons.Back); } + } + + public ButtonState BigButton + { + get { return GetButton(Buttons.BigButton); } + } + + public ButtonState LeftShoulder + { + get { return GetButton(Buttons.LeftShoulder); } + } + + public ButtonState LeftStick + { + get { return GetButton(Buttons.LeftStick); } + } + + public ButtonState RightShoulder + { + get { return GetButton(Buttons.RightShoulder); } + } + + public ButtonState RightStick + { + get { return GetButton(Buttons.RightStick); } + } + + public ButtonState Start + { + get { return GetButton(Buttons.Start); } + } + + public static bool operator ==(GamePadButtons left, GamePadButtons right) + { + return left.Equals(right); + } + + public static bool operator !=(GamePadButtons left, GamePadButtons right) + { + return !left.Equals(right); + } + + #endregion + + #region IEquatable Members + + 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 + } +} + From c8989f3e0d3f219ddd59a8e60fa8f3aba909b10a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 16:28:20 +0100 Subject: [PATCH 06/69] Implemented new GamePad interface (WIP) --- Source/OpenTK/Input/GamePad.cs | 32 +++++-- Source/OpenTK/Input/GamePadAxis.cs | 8 +- Source/OpenTK/Input/GamePadCapabilities.cs | 67 +++++++++++++++ Source/OpenTK/Input/GamePadDPad.cs | 90 ++++++++++++++++++++ Source/OpenTK/Input/GamePadState.cs | 99 ++++++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 5 +- 6 files changed, 290 insertions(+), 11 deletions(-) create mode 100644 Source/OpenTK/Input/GamePadCapabilities.cs create mode 100644 Source/OpenTK/Input/GamePadDPad.cs diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index 303f4516..7a2fc066 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -30,17 +30,39 @@ using System; namespace OpenTK.Input { /// - /// Provides access to GamePad devices. Note: this API is not implemented yet. + /// Provides access to GamePad devices. /// public class GamePad { - #region Constructors + internal const int MaxAxisCount = 10; + internal const int MaxButtonCount = 16; // if this grows over 32 then GamePadState.buttons must be modified + internal const int MaxDPadCount = 2; - static GamePad() + static readonly IGamePadDriver driver = + Platform.Factory.Default.CreateGamePadDriver(); + + /// + /// 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); + } } } diff --git a/Source/OpenTK/Input/GamePadAxis.cs b/Source/OpenTK/Input/GamePadAxis.cs index c1c96c53..dd6b3192 100644 --- a/Source/OpenTK/Input/GamePadAxis.cs +++ b/Source/OpenTK/Input/GamePadAxis.cs @@ -25,11 +25,11 @@ // THE SOFTWARE. using System; -namespace OpenTK +namespace OpenTK.Input { public enum GamePadAxis - { - /// The first axis of the gamepad. + { + /// The first axis of the gamepad. Axis0 = 0, /// The second axis of the gamepad. Axis1, @@ -49,8 +49,6 @@ namespace OpenTK Axis8, /// The tenth axis of the gamepad. Axis9, - /// The last axis of the gamepad. - LastAxis } } diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs new file mode 100644 index 00000000..23c56cd3 --- /dev/null +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -0,0 +1,67 @@ +// #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 +{ + + public struct GamePadCapabilities + { + byte axis_count; + byte button_count; + byte dpad_count; + byte trackball_count; + + public int AxisCount + { + get { return axis_count; } + internal set { axis_count = (byte)value; } + } + + public int ButtonCount + { + get { return button_count; } + internal set { button_count = (byte)value; } + } + + public int DPadCount + { + get { return dpad_count; } + internal set { dpad_count = (byte)value; } + } + + public int TrackballCount + { + get { return trackball_count; } + internal set { trackball_count = (byte)value; } + } + } +} + diff --git a/Source/OpenTK/Input/GamePadDPad.cs b/Source/OpenTK/Input/GamePadDPad.cs new file mode 100644 index 00000000..72389b00 --- /dev/null +++ b/Source/OpenTK/Input/GamePadDPad.cs @@ -0,0 +1,90 @@ +// #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 +{ + + public struct GamePadDPad + { + [Flags] + enum DPadButtons : byte + { + Up = Buttons.DPadUp, + Down = Buttons.DPadDown, + Left = Buttons.DPadLeft, + Right = Buttons.DPadRight + } + + DPadButtons buttons; + + internal GamePadDPad(Buttons state) + { + // DPad butons are stored in the lower 4bits + // of the Buttons enumeration. + buttons = (DPadButtons)((int)state & 0x0f); + } + + public bool IsUp + { + get { return (buttons & DPadButtons.Up) != 0; } + internal set { SetButton(DPadButtons.Up, value); } + } + + public bool IsDown + { + get { return (buttons & DPadButtons.Down) != 0; } + internal set { SetButton(DPadButtons.Down, value); } + } + + public bool IsLeft + { + get { return (buttons & DPadButtons.Left) != 0; } + internal set { SetButton(DPadButtons.Left, value); } + } + + public bool IsRight + { + get { return (buttons & DPadButtons.Right) != 0; } + internal set { SetButton(DPadButtons.Right, value); } + } + + void SetButton(DPadButtons button, bool value) + { + if (value) + { + buttons |= button; + } + else + { + buttons &= ~button; + } + } + } +} diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index 21e3aa91..d45787bb 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -34,6 +34,105 @@ namespace OpenTK.Input /// public struct GamePadState /*: IEquatable*/ { + const float RangeMultiplier = 1.0f / (short.MaxValue + 1); + Buttons buttons; + unsafe fixed short axes[GamePad.MaxAxisCount]; + bool is_connected; + + #region Public Members + + public float GetAxis(GamePadAxis axis) + { + throw new NotImplementedException(); + } + + public GamePadButtons Buttons + { + get { return new GamePadButtons(buttons); } + } + + public GamePadDPad DPad + { + get { return new GamePadDPad(buttons); } + } + + public bool IsConnected + { + get { return is_connected; } + } + + #endregion + + #region Internal Members + + internal void SetAxis(GamePadAxis axis, short value) + { + if (IsAxisValid(axis)) + { + int index = (int)axis; + unsafe + { + fixed (short *paxes = axes) + { + *(paxes + index) = value; + } + } + } + else + { + throw new ArgumentOutOfRangeException("axis"); + } + } + + internal void SetButton(Buttons button, bool pressed) + { + if (IsButtonValid(button)) + { + int index = (int)button; + + Buttons mask = (Buttons)(1 << index); + if (pressed) + { + buttons |= mask; + } + else + { + buttons &= ~mask; + } + } + else + { + throw new ArgumentOutOfRangeException("button"); + } + } + + internal void SetConnected(bool connected) + { + is_connected = connected; + } + + #endregion + + #region Private Members + + bool IsAxisValid(GamePadAxis axis) + { + int index = (int)axis; + return index >= 0 && index < GamePad.MaxAxisCount; + } + + bool IsButtonValid(Buttons button) + { + int index = (int)button; + return index >= 0 && index < GamePad.MaxButtonCount; + } + + bool IsDPadValid(int index) + { + return index >= 0 && index < GamePad.MaxDPadCount; + } + + #endregion } } diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 7bddfcb0..723b4412 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -738,7 +738,6 @@ - @@ -777,6 +776,10 @@ + + + + From 234c15e9c9d0352357ec612c7fcff9401a4d446b Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 16:38:56 +0100 Subject: [PATCH 07/69] Corrected the entrypoint for wglChoosePixelFormat --- Source/OpenTK/Platform/Windows/Bindings/Wgl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs b/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs index 4668f629..e235beb8 100644 --- a/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs +++ b/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs @@ -49,11 +49,11 @@ namespace OpenTK.Platform.Windows [DllImport(Wgl.Library, EntryPoint = "wglMakeCurrent", ExactSpelling = true, SetLastError = true)] internal extern static Boolean MakeCurrent(IntPtr hDc, IntPtr newContext); [SuppressUnmanagedCodeSecurity] - [DllImport(Wgl.Library, EntryPoint = "wglCopyContext", ExactSpelling = true, SetLastError = true)] + [DllImport(Wgl.Library, EntryPoint = "wglChoosePixelFormat", ExactSpelling = true, SetLastError = true)] internal extern static unsafe int ChoosePixelFormat(IntPtr hDc, ref PixelFormatDescriptor pPfd); [SuppressUnmanagedCodeSecurity] [DllImport(Wgl.Library, EntryPoint = "wglDescribePixelFormat", ExactSpelling = true, SetLastError = true)] - internal extern static unsafe int DescribePixelFormat(IntPtr hdc, int ipfd, UInt32 cjpfd, out PixelFormatDescriptor ppfd); + internal extern static unsafe int DescribePixelFormat(IntPtr hdc, int ipfd, int cjpfd, ref PixelFormatDescriptor ppfd); [SuppressUnmanagedCodeSecurity] [DllImport(Wgl.Library, EntryPoint = "wglGetCurrentDC", ExactSpelling = true, SetLastError = true)] internal extern static IntPtr GetCurrentDC(); From 1c8e7bc9931ef9c89e003f4c36b04eb5b7724d3c Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 16:39:45 +0100 Subject: [PATCH 08/69] Use opengl32 instead of gdi32 throughout Since we are dynamically loading opengl32.dll, we are supposed to use the wgl version of functions that exist in both opengl32 and gdi32 dlls. --- Source/OpenTK/Platform/Windows/WinGLContext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index e431ae6b..a534d929 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -389,13 +389,13 @@ namespace OpenTK.Platform.Windows } PixelFormatDescriptor pfd = new PixelFormatDescriptor(); - Functions.DescribePixelFormat( + Wgl.DescribePixelFormat( window.DeviceContext, (int)mode.Index.Value, API.PixelFormatDescriptorSize, ref pfd); Debug.WriteLine(mode.Index.ToString()); - if (!Functions.SetPixelFormat(window.DeviceContext, (int)mode.Index.Value, ref pfd)) + if (!Wgl.SetPixelFormat(window.DeviceContext, (int)mode.Index.Value, ref pfd)) { throw new GraphicsContextException(String.Format( "Requested GraphicsMode not available. SetPixelFormat error: {0}", From 954b1e98b63239f2b188f200c31733beeb3e0fe6 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 16:40:15 +0100 Subject: [PATCH 09/69] Minor code cleanup No need to wrap Wgl.DescribePixelFormat, just call it directly. --- Source/OpenTK/Platform/Windows/WinGraphicsMode.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index 67f3ce19..f62f2315 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -105,17 +105,6 @@ namespace OpenTK.Platform.Windows #region Private Methods - #region DescribePixelFormat - - static int DescribePixelFormat(IntPtr hdc, int ipfd, int cjpfd, ref PixelFormatDescriptor pfd) - { - // Note: DescribePixelFormat found in gdi32 is extremely slow - // on nvidia, for some reason. - return Wgl.DescribePixelFormat(hdc, ipfd, (uint)cjpfd, out pfd); - } - - #endregion - #region GetModesPFD IEnumerable GetModesPFD(IntPtr device) @@ -140,8 +129,9 @@ namespace OpenTK.Platform.Windows { // Iterate through all accelerated formats first. Afterwards, iterate through non-accelerated formats. // This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration. + // Note: DescribePixelFormat found in gdi32 is extremely slow on nvidia, for some reason. int pixel = 0; - while (DescribePixelFormat(device, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0) + while (Wgl.DescribePixelFormat(device, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0) { // Ignore non-accelerated formats. if (!generic_allowed && (pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0) From 3413271d8abec58d2369080cd0348128b2355709 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 21:24:25 +0100 Subject: [PATCH 10/69] Load opengl32.dll before gdi32.dll According to http://stackoverflow.com/questions/199016/wglcreatecontext-in-c-sharp-failing-but-not-in-managed-c, opengl32.dll must be loaded before gdi32.dll. Affect issue #19. --- Source/OpenTK/Platform/Windows/WinFactory.cs | 20 +++++++++++++++++++ .../OpenTK/Platform/Windows/WinGLContext.cs | 17 ++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinFactory.cs b/Source/OpenTK/Platform/Windows/WinFactory.cs index b0fc8224..5435018c 100644 --- a/Source/OpenTK/Platform/Windows/WinFactory.cs +++ b/Source/OpenTK/Platform/Windows/WinFactory.cs @@ -34,6 +34,7 @@ namespace OpenTK.Platform.Windows { using Graphics; using OpenTK.Input; + using System.Runtime.InteropServices; class WinFactory : IPlatformFactory { @@ -41,6 +42,9 @@ namespace OpenTK.Platform.Windows readonly object SyncRoot = new object(); IInputDriver2 inputDriver; + internal static IntPtr OpenGLHandle { get; private set; } + const string OpenGLName = "OPENGL32.DLL"; + public WinFactory() { if (System.Environment.OSVersion.Version.Major <= 4) @@ -48,6 +52,11 @@ namespace OpenTK.Platform.Windows throw new PlatformNotSupportedException("OpenTK requires Windows XP or higher"); } + // Dynamically load opengl32.dll in order to use the extension loading capabilities of Wgl. + // Note: opengl32.dll must be loaded before gdi32.dll, otherwise strange failures may occur + // (such as "error: 2000" when calling wglSetPixelFormat or slowness/lag on specific GPUs). + LoadOpenGL(); + if (System.Environment.OSVersion.Version.Major >= 6) { if (Toolkit.Options.EnableHighResolution) @@ -60,6 +69,17 @@ namespace OpenTK.Platform.Windows } } + static void LoadOpenGL() + { + OpenGLHandle = Functions.LoadLibrary(OpenGLName); + if (OpenGLHandle == IntPtr.Zero) + { + throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", + OpenGLName, Marshal.GetLastWin32Error())); + } + Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", OpenGLHandle)); + } + #region IPlatformFactory Members public virtual INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index a534d929..bfe4c9d1 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -30,9 +30,6 @@ namespace OpenTK.Platform.Windows static readonly object LoadLock = new object(); static readonly object SyncRoot = new object(); - static IntPtr opengl32Handle; - const string opengl32Name = "OPENGL32.DLL"; - bool vsync_supported; readonly WinGraphicsMode ModeSelector; @@ -43,16 +40,6 @@ namespace OpenTK.Platform.Windows { lock (LoadLock) { - // Dynamically load opengl32.dll in order to use the extension loading capabilities of Wgl. - if (opengl32Handle == IntPtr.Zero) - { - opengl32Handle = Functions.LoadLibrary(opengl32Name); - if (opengl32Handle == IntPtr.Zero) - throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", - opengl32Name, Marshal.GetLastWin32Error())); - Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", opengl32Handle)); - } - // We need to create a temp context in order to load // wgl extensions (e.g. for multisampling or GL3). // We cannot rely on OpenTK.Platform.Wgl until we @@ -341,7 +328,7 @@ namespace OpenTK.Platform.Windows IntPtr address = Wgl.GetProcAddress(function_string); if (!IsValid(address)) { - address = Functions.GetProcAddress(opengl32Handle, function_string); + address = Functions.GetProcAddress(WinFactory.OpenGLHandle, function_string); } return address; } @@ -351,7 +338,7 @@ namespace OpenTK.Platform.Windows IntPtr address = Wgl.GetProcAddress(function_string); if (!IsValid(address)) { - address = Functions.GetProcAddress(opengl32Handle, function_string); + address = Functions.GetProcAddress(WinFactory.OpenGLHandle, function_string); } return address; } From 613ca93d894823581668a918e03e4161609778bc Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 22:35:30 +0100 Subject: [PATCH 11/69] Use gdi32 implementations of functions Several functions are defined in both gdi32 and opengl32. Using the opengl32/wgl versions did not appear to help with issue #19. Let's use the gdi32 version instead, as suggested here: https://www.opengl.org/wiki/Platform_specifics:_Windows#The_WGL_functions --- Source/OpenTK/Platform/Windows/WinGLContext.cs | 6 +++--- Source/OpenTK/Platform/Windows/WinGraphicsMode.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index bfe4c9d1..92c21851 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -376,13 +376,13 @@ namespace OpenTK.Platform.Windows } PixelFormatDescriptor pfd = new PixelFormatDescriptor(); - Wgl.DescribePixelFormat( + Functions.DescribePixelFormat( window.DeviceContext, (int)mode.Index.Value, API.PixelFormatDescriptorSize, ref pfd); Debug.WriteLine(mode.Index.ToString()); - - if (!Wgl.SetPixelFormat(window.DeviceContext, (int)mode.Index.Value, ref pfd)) + + if (!Functions.SetPixelFormat(window.DeviceContext, (int)mode.Index.Value, ref pfd)) { throw new GraphicsContextException(String.Format( "Requested GraphicsMode not available. SetPixelFormat error: {0}", diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index f62f2315..b2826b5e 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -131,7 +131,7 @@ namespace OpenTK.Platform.Windows // This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration. // Note: DescribePixelFormat found in gdi32 is extremely slow on nvidia, for some reason. int pixel = 0; - while (Wgl.DescribePixelFormat(device, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0) + while (Functions.DescribePixelFormat(device, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0) { // Ignore non-accelerated formats. if (!generic_allowed && (pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0) From 4ced172bdcc46be1ca278ec70be3dd5764da211b Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 23:31:04 +0100 Subject: [PATCH 12/69] Minor code cleanup --- Source/OpenTK/Platform/Windows/WinFactory.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinFactory.cs b/Source/OpenTK/Platform/Windows/WinFactory.cs index 5435018c..5ea1a8eb 100644 --- a/Source/OpenTK/Platform/Windows/WinFactory.cs +++ b/Source/OpenTK/Platform/Windows/WinFactory.cs @@ -28,13 +28,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; +using OpenTK.Graphics; +using OpenTK.Input; + namespace OpenTK.Platform.Windows { - using Graphics; - using OpenTK.Input; - using System.Runtime.InteropServices; class WinFactory : IPlatformFactory { From 146e8f3fb1405623eac61ff09f0e1264ab9d2c4f Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Wed, 18 Dec 2013 14:16:49 +0100 Subject: [PATCH 13/69] Fixed GetModesARB implementation The correct way to query number of available pixel formats is to use Wgl.Arb.GetPixelFormatAttrib(NumberPixelFormatsArb), not Wgl.Arb.ChoosePixelFormats. This fixes an issue where Intel drivers would fail to report any pixel formats in GetModesARB, even when WGL_ARB_pixel_format is supported. --- .../Platform/Windows/WinGraphicsMode.cs | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index b2826b5e..0efc07ce 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -155,6 +155,11 @@ namespace OpenTK.Platform.Windows #region GetModesARB + // Queries pixel formats through the WGL_ARB_pixel_format extension + // This method only returns accelerated formats. If no format offers + // hardware acceleration (e.g. we are running in a VM or in a remote desktop + // connection), this method will return 0 formats and we will fall back to + // GetModesPFD. IEnumerable GetModesARB(IntPtr device) { // See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt @@ -166,6 +171,10 @@ namespace OpenTK.Platform.Windows yield break; } + // Define the list of attributes we are interested in. + // We will use each available pixel format for these + // attributes using GetPixelFormatAttrib. + // The results will be stored in the 'values' array below. int[] attribs = new int[] { (int)WGL_ARB_pixel_format.AccelerationArb, @@ -193,46 +202,41 @@ namespace OpenTK.Platform.Windows 0 }; + // Allocate storage for the results of GetPixelFormatAttrib queries int[] values = new int[attribs.Length]; - int[] attribs_values = new int[] - { - (int)WGL_ARB_pixel_format.AccelerationArb, - (int)WGL_ARB_pixel_format.FullAccelerationArb, - (int)WGL_ARB_pixel_format.SupportOpenglArb, 1, - (int)WGL_ARB_pixel_format.DrawToWindowArb, 1, - 0, 0 - }; - - int[] num_formats = new int[1]; // Get the number of available formats - if (Wgl.Arb.ChoosePixelFormat(device, attribs_values, null, 0, null, num_formats)) + int num_formats; + int num_formats_attrib = (int)WGL_ARB_pixel_format.NumberPixelFormatsArb; + if (Wgl.Arb.GetPixelFormatAttrib(device, 0, 0, 1, ref num_formats_attrib, out num_formats)) { - // Create an array big enough to hold all available formats and get those formats - int[] pixel = new int[num_formats[0]]; - - if (Wgl.Arb.ChoosePixelFormat(device, attribs_values, null, pixel.Length, pixel, num_formats)) + for (int p = 1; p < num_formats; p++) { - foreach (int p in pixel) + // Get the format attributes for this pixel format + if (!Wgl.Arb.GetPixelFormatAttrib(device, p, 0, attribs.Length - 1, attribs, values)) { - // Find out what we really got as a format: - if (!Wgl.Arb.GetPixelFormatAttrib(device, p, 0, attribs.Length - 1, attribs, values)) - { - Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p); - continue; - } - - GraphicsMode mode = new GraphicsMode(new IntPtr(p), - new ColorFormat(values[1], values[2], values[3], values[4]), - values[6], - values[7], - values[8] != 0 ? values[9] : 0, - new ColorFormat(values[10], values[11], values[12], values[13]), - values[15] == 1 ? 2 : 1, - values[16] == 1 ? true : false); - - yield return mode; + Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p); + continue; } + + // Skip formats that don't offer full hardware acceleration + WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0]; + if (acceleration != WGL_ARB_pixel_format.FullAccelerationArb) + { + continue; + } + + // Construct a new GraphicsMode to describe this format + GraphicsMode mode = new GraphicsMode(new IntPtr(p), + new ColorFormat(values[1], values[2], values[3], values[4]), + values[6], + values[7], + values[8] != 0 ? values[9] : 0, + new ColorFormat(values[10], values[11], values[12], values[13]), + values[15] == 1 ? 2 : 1, + values[16] == 1 ? true : false); + + yield return mode; } } } From c42090835f4b88cf14936d93ba9b43ffbc398059 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Wed, 18 Dec 2013 14:29:06 +0100 Subject: [PATCH 14/69] Cleaned up temporary context construction The temporary context is now retained until the actual context has been constructed. If we don't do this, then WGL_ARB_create_context may fail to work correctly on specific GPUs (e.g. Intel). This may affect issue #19. --- .../OpenTK/Platform/Windows/WinGLContext.cs | 217 ++++++++++-------- 1 file changed, 126 insertions(+), 91 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 92c21851..9d4bf4c5 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -28,88 +28,111 @@ namespace OpenTK.Platform.Windows internal sealed class WinGLContext : DesktopGraphicsContext { static readonly object LoadLock = new object(); - static readonly object SyncRoot = new object(); bool vsync_supported; readonly WinGraphicsMode ModeSelector; - #region --- Contructors --- - - static WinGLContext() + // We need to create a temp context in order to load + // wgl extensions (e.g. for multisampling or GL3). + // We cannot rely on any WGL extensions before + // we load them with the temporary context. + class TemporaryContext : IDisposable { - lock (LoadLock) + public ContextHandle Context; + + public TemporaryContext(INativeWindow native) { - // We need to create a temp context in order to load - // wgl extensions (e.g. for multisampling or GL3). - // We cannot rely on OpenTK.Platform.Wgl until we - // create the context and call Wgl.LoadAll(). - Debug.Print("Creating temporary context for wgl extensions."); - using (INativeWindow native = new NativeWindow()) + Debug.WriteLine("[WGL] Creating temporary context to load extensions"); + + if (native == null) + throw new ArgumentNullException(); + + // Create temporary context and load WGL entry points + // First, set a compatible pixel format to the device context + // of the temp window + WinWindowInfo window = native.WindowInfo as WinWindowInfo; + WinGraphicsMode selector = new WinGraphicsMode(window.DeviceContext); + WinGLContext.SetGraphicsModePFD(selector, GraphicsMode.Default, window); + + bool success = false; + + // Then, construct a temporary context and load all wgl extensions + Context = new ContextHandle(Wgl.CreateContext(window.DeviceContext)); + if (Context != ContextHandle.Zero) { - // Create temporary context and load WGL entry points - // First, set a compatible pixel format to the device context - // of the temp window - WinWindowInfo window = native.WindowInfo as WinWindowInfo; - WinGraphicsMode selector = new WinGraphicsMode(window.DeviceContext); - SetGraphicsModePFD(selector, GraphicsMode.Default, window); - - // Then, construct a temporary context and load all wgl extensions - ContextHandle temp_context = new ContextHandle(Wgl.CreateContext(window.DeviceContext)); - if (temp_context != ContextHandle.Zero) + // Make the context current. + // Note: on some video cards and on some virtual machines, wglMakeCurrent + // may fail with an errorcode of 6 (INVALID_HANDLE). The suggested workaround + // is to call wglMakeCurrent in a loop until it succeeds. + // See https://www.opengl.org/discussion_boards/showthread.php/171058-nVidia-wglMakeCurrent()-multiple-threads + // Sigh... + for (int retry = 0; retry < 5 && !success; retry++) { - // Make the context current. - // Note: on some video cards and on some virtual machines, wglMakeCurrent - // may fail with an errorcode of 6 (INVALID_HANDLE). The suggested workaround - // is to call wglMakeCurrent in a loop until it succeeds. - // See https://www.opengl.org/discussion_boards/showthread.php/171058-nVidia-wglMakeCurrent()-multiple-threads - // Sigh... - for (int retry = 0; retry < 5; retry++) - { - bool success = Wgl.MakeCurrent(window.DeviceContext, temp_context.Handle); - if (!success) - { - Debug.Print("wglMakeCurrent failed with error: {0}. Retrying", Marshal.GetLastWin32Error()); - System.Threading.Thread.Sleep(10); - } - else - { - // wglMakeCurrent succeeded, we are done here! - break; - } - } + success = Wgl.MakeCurrent(window.DeviceContext, Context.Handle); + if (!success) + { + Debug.Print("wglMakeCurrent failed with error: {0}. Retrying", Marshal.GetLastWin32Error()); + System.Threading.Thread.Sleep(10); + } + } + } + else + { + Debug.Print("[WGL] CreateContext failed with error: {0}", Marshal.GetLastWin32Error()); + } - // Load wgl extensions and destroy temporary context - Wgl.LoadAll(); - Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); - Wgl.DeleteContext(temp_context.Handle); - } - else - { - Debug.Print("wglCreateContext failed with error: {0}", Marshal.GetLastWin32Error()); - } + if (!success) + { + Debug.WriteLine("[WGL] Failed to create temporary context"); + } + } + + public void Dispose() + { + if (Context != ContextHandle.Zero) + { + Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); + Wgl.DeleteContext(Context.Handle); } } } + #region --- Contructors --- + public WinGLContext(GraphicsMode format, WinWindowInfo window, IGraphicsContext sharedContext, int major, int minor, GraphicsContextFlags flags) { // There are many ways this code can break when accessed by multiple threads. The biggest offender is // the sharedContext stuff, which will only become valid *after* this constructor returns. // The easiest solution is to serialize all context construction - hence the big lock, below. - lock (SyncRoot) + lock (LoadLock) { if (window == null) throw new ArgumentNullException("window", "Must point to a valid window."); if (window.Handle == IntPtr.Zero) throw new ArgumentException("window", "Must be a valid window."); - Debug.Print("OpenGL will be bound to window:{0} on thread:{1}", window.Handle, - System.Threading.Thread.CurrentThread.ManagedThreadId); - - lock (LoadLock) + IntPtr current_context = Wgl.GetCurrentContext(); + INativeWindow temp_window = null; + TemporaryContext temp_context = null; + try { + if (current_context == IntPtr.Zero) + { + // Create temporary context to load WGL extensions + temp_window = new NativeWindow(); + temp_context = new TemporaryContext(temp_window); + current_context = Wgl.GetCurrentContext(); + if (current_context != IntPtr.Zero && current_context == temp_context.Context.Handle) + { + Wgl.LoadAll(); + } + } + + Debug.Print("OpenGL will be bound to window:{0} on thread:{1}", window.Handle, + System.Threading.Thread.CurrentThread.ManagedThreadId); + ModeSelector = new WinGraphicsMode(window.DeviceContext); Mode = SetGraphicsModePFD(ModeSelector, format, (WinWindowInfo)window); @@ -146,43 +169,56 @@ namespace OpenTK.Platform.Windows if (Handle == ContextHandle.Zero) Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error()); } - catch (EntryPointNotFoundException e) { Debug.Print(e.ToString()); } - catch (NullReferenceException e) { Debug.Print(e.ToString()); } + catch (Exception e) { Debug.Print(e.ToString()); } + } + + if (Handle == ContextHandle.Zero) + { + // Failed to create GL3-level context, fall back to GL2. + Debug.Write("Falling back to GL2... "); + Handle = new ContextHandle(Wgl.CreateContext(window.DeviceContext)); + if (Handle == ContextHandle.Zero) + Handle = new ContextHandle(Wgl.CreateContext(window.DeviceContext)); + if (Handle == ContextHandle.Zero) + throw new GraphicsContextException( + String.Format("Context creation failed. Wgl.CreateContext() error: {0}.", + Marshal.GetLastWin32Error())); + } + + Debug.WriteLine(String.Format("success! (id: {0})", Handle)); + } + finally + { + if (temp_context != null) + { + temp_context.Dispose(); + temp_context = null; + } + if (temp_window != null) + { + temp_window.Dispose(); + temp_window = null; } } + } - if (Handle == ContextHandle.Zero) - { - // Failed to create GL3-level context, fall back to GL2. - Debug.Write("Falling back to GL2... "); - Handle = new ContextHandle(Wgl.CreateContext(window.DeviceContext)); - if (Handle == ContextHandle.Zero) - Handle = new ContextHandle(Wgl.CreateContext(window.DeviceContext)); - if (Handle == ContextHandle.Zero) - throw new GraphicsContextException( - String.Format("Context creation failed. Wgl.CreateContext() error: {0}.", - Marshal.GetLastWin32Error())); - } + // Todo: is this comment still true? + // On intel drivers, wgl entry points appear to change + // when creating multiple contexts. As a workaround, + // we reload Wgl entry points every time we create a + // new context - this solves the issue without any apparent + // side-effects (i.e. the old contexts can still be handled + // using the new entry points.) + // Sigh... + Wgl.MakeCurrent(window.DeviceContext, Handle.Handle); + Wgl.LoadAll(); - Debug.WriteLine(String.Format("success! (id: {0})", Handle)); - - // Todo: is this comment still true? - // On intel drivers, wgl entry points appear to change - // when creating multiple contexts. As a workaround, - // we reload Wgl entry points every time we create a - // new context - this solves the issue without any apparent - // side-effects (i.e. the old contexts can still be handled - // using the new entry points.) - // Sigh... - Wgl.LoadAll(); - - if (sharedContext != null) - { - Marshal.GetLastWin32Error(); - Debug.Write(String.Format("Sharing state with context {0}: ", sharedContext)); - bool result = Wgl.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle); - Debug.WriteLine(result ? "success!" : "failed with win32 error " + Marshal.GetLastWin32Error()); - } + if (sharedContext != null) + { + Marshal.GetLastWin32Error(); + Debug.Write(String.Format("Sharing state with context {0}: ", sharedContext)); + bool result = Wgl.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle); + Debug.WriteLine(result ? "success!" : "failed with win32 error " + Marshal.GetLastWin32Error()); } } @@ -231,7 +267,6 @@ namespace OpenTK.Platform.Windows public override void MakeCurrent(IWindowInfo window) { - lock (SyncRoot) lock (LoadLock) { bool success; @@ -347,7 +382,7 @@ namespace OpenTK.Platform.Windows { // See https://www.opengl.org/wiki/Load_OpenGL_Functions long a = address.ToInt64(); - bool is_valid = (a < -1 )|| (a > 3); + bool is_valid = (a < -1) || (a > 3); return is_valid; } @@ -379,7 +414,7 @@ namespace OpenTK.Platform.Windows Functions.DescribePixelFormat( window.DeviceContext, (int)mode.Index.Value, API.PixelFormatDescriptorSize, ref pfd); - + Debug.WriteLine(mode.Index.ToString()); if (!Functions.SetPixelFormat(window.DeviceContext, (int)mode.Index.Value, ref pfd)) From b81aaf9a124d93e613c48236d60bbed78f5c0abb Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Wed, 18 Dec 2013 14:29:18 +0100 Subject: [PATCH 15/69] No point in using 16bpp color --- Source/Examples/OpenGL/1.x/ImmediateMode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Examples/OpenGL/1.x/ImmediateMode.cs b/Source/Examples/OpenGL/1.x/ImmediateMode.cs index ca31c83b..c44c2f5b 100644 --- a/Source/Examples/OpenGL/1.x/ImmediateMode.cs +++ b/Source/Examples/OpenGL/1.x/ImmediateMode.cs @@ -34,7 +34,7 @@ namespace Examples.Tutorial #region --- Constructor --- public T03_Immediate_Mode_Cube() - : base(800, 600, new GraphicsMode(16, 16)) + : base(800, 600) { } #endregion From ecd7db99c185295a9a4d7b1611ba46e65047068e Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Wed, 18 Dec 2013 14:51:00 +0100 Subject: [PATCH 16/69] Replace Wgl.GetCurrentDC with cached dc with --- Source/OpenTK/Platform/Windows/WinGLContext.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 9d4bf4c5..0e727df7 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -29,6 +29,7 @@ namespace OpenTK.Platform.Windows { static readonly object LoadLock = new object(); + IntPtr device_context; bool vsync_supported; readonly WinGraphicsMode ModeSelector; @@ -210,7 +211,7 @@ namespace OpenTK.Platform.Windows // side-effects (i.e. the old contexts can still be handled // using the new entry points.) // Sigh... - Wgl.MakeCurrent(window.DeviceContext, Handle.Handle); + MakeCurrent(window); Wgl.LoadAll(); if (sharedContext != null) @@ -284,9 +285,13 @@ namespace OpenTK.Platform.Windows success = Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); } + device_context = wnd.DeviceContext; + if (!success) + { throw new GraphicsContextException(String.Format( "Failed to make context {0} current. Error: {1}", this, Marshal.GetLastWin32Error())); + } } } @@ -435,11 +440,10 @@ namespace OpenTK.Platform.Windows { get { - return Wgl.GetCurrentDC(); + return device_context; } } - #endregion #endregion From 6493ab018838678c27b9dd81435ae021772e6259 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Thu, 19 Dec 2013 10:39:36 +0100 Subject: [PATCH 17/69] Threads with message pump require STA comparment May affect issue #19 --- Source/OpenTK/Platform/Windows/WinInputBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/OpenTK/Platform/Windows/WinInputBase.cs b/Source/OpenTK/Platform/Windows/WinInputBase.cs index 44d82ee7..252a6f8d 100644 --- a/Source/OpenTK/Platform/Windows/WinInputBase.cs +++ b/Source/OpenTK/Platform/Windows/WinInputBase.cs @@ -58,6 +58,7 @@ namespace OpenTK.Platform.Windows WndProc = WindowProcedure; InputThread = new Thread(ProcessEvents); + InputThread.SetApartmentState(ApartmentState.STA); InputThread.IsBackground = true; InputThread.Start(); From 1edfa8f3dec4472d5bdf7c2a2b6b3777c09f835d Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Fri, 20 Dec 2013 08:42:36 +0100 Subject: [PATCH 18/69] Simplify ProcessEvents implementation Instead of combining PeekMessage+GetMessage, we can simply call PeekMessage(Remove) to achieve the same effect. This also allows us to remove the IsIdle property, which is no longer used anywhere. --- Source/OpenTK/Platform/Windows/API.cs | 14 ++++++++++- Source/OpenTK/Platform/Windows/WinGLNative.cs | 24 +------------------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index b9107d7a..c05398e0 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -349,7 +349,7 @@ namespace OpenTK.Platform.Windows [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("User32.dll"), CLSCompliant(false)] [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool PeekMessage(ref MSG msg, IntPtr hWnd, int messageFilterMin, int messageFilterMax, int flags); + internal static extern bool PeekMessage(ref MSG msg, IntPtr hWnd, int messageFilterMin, int messageFilterMax, PeekMessageFlags flags); #endregion @@ -4028,6 +4028,18 @@ namespace OpenTK.Platform.Windows #endregion + #region PeekMessageFlags + + [Flags] + enum PeekMessageFlags : uint + { + NoRemove = 0, + Remove = 1, + NoYield = 2 + } + + #endregion + #region ShowWindowCommand /// diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index aae5d109..ccdcbd39 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -583,19 +583,6 @@ namespace OpenTK.Platform.Windows #endregion - #region IsIdle - - bool IsIdle - { - get - { - MSG message = new MSG(); - return !Functions.PeekMessage(ref message, window.Handle, 0, 0, 0); - } - } - - #endregion - #region CreateWindow IntPtr CreateWindow(int x, int y, int width, int height, string title, GameWindowFlags options, DisplayDevice device, IntPtr parentHandle) @@ -1217,20 +1204,11 @@ namespace OpenTK.Platform.Windows #region public void ProcessEvents() - private int ret; MSG msg; public void ProcessEvents() { - while (!IsIdle) + while (Functions.PeekMessage(ref msg, window.Handle, 0, 0, PeekMessageFlags.Remove)) { - ret = Functions.GetMessage(ref msg, window.Handle, 0, 0); - if (ret == -1) - { - throw new PlatformException(String.Format( - "An error happened while processing the message queue. Windows error: {0}", - Marshal.GetLastWin32Error())); - } - Functions.TranslateMessage(ref msg); Functions.DispatchMessage(ref msg); } From 6696bef3eb45b41317d3e6716898c8db99620130 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Fri, 20 Dec 2013 23:29:47 +0100 Subject: [PATCH 19/69] Explicitly set WindowProcedure calling convention --- Source/OpenTK/Platform/Windows/API.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index c05398e0..316a85ae 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -4313,6 +4313,8 @@ namespace OpenTK.Platform.Windows #region --- Callbacks --- + [SuppressUnmanagedCodeSecurity] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam); #region Message From d877061d6a52c6b1b0bee4ef7192b4223f9d2985 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Fri, 20 Dec 2013 23:31:42 +0100 Subject: [PATCH 20/69] Fix issue #19 Don't filter window messages passed to our window (see http://blogs.msdn.com/b/oldnewthing/archive/2005/02/09/369804.aspx). Additionally, return the correct values for all messages we are actually handling and clean up unmanaged memory after we are done with the window. --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index ccdcbd39..d2134aae 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -257,7 +257,7 @@ namespace OpenTK.Platform.Windows if (new_focused_state != Focused) FocusedChanged(this, EventArgs.Empty); - break; + return IntPtr.Zero; case WindowMessage.ENTERMENULOOP: case WindowMessage.ENTERSIZEMOVE: @@ -269,7 +269,7 @@ namespace OpenTK.Platform.Windows if (!CursorVisible) UngrabCursor(); - break; + return IntPtr.Zero; case WindowMessage.EXITMENULOOP: case WindowMessage.EXITSIZEMOVE: @@ -327,7 +327,7 @@ namespace OpenTK.Platform.Windows } } } - break; + return IntPtr.Zero; case WindowMessage.STYLECHANGED: unsafe @@ -347,8 +347,8 @@ namespace OpenTK.Platform.Windows // Ensure cursor remains grabbed if (!CursorVisible) GrabCursor(); - - break; + + return IntPtr.Zero; case WindowMessage.SIZE: SizeMessage state = (SizeMessage)wParam.ToInt64(); @@ -373,7 +373,7 @@ namespace OpenTK.Platform.Windows GrabCursor(); } - break; + return IntPtr.Zero; #endregion @@ -386,7 +386,7 @@ namespace OpenTK.Platform.Windows key_press.KeyChar = (char)wParam.ToInt64(); KeyPress(this, key_press); - break; + return IntPtr.Zero; case WindowMessage.MOUSEMOVE: Point point = new Point( @@ -403,62 +403,62 @@ namespace OpenTK.Platform.Windows MouseEnter(this, EventArgs.Empty); } - break; + return IntPtr.Zero; case WindowMessage.MOUSELEAVE: mouse_outside_window = true; // Mouse tracking is disabled automatically by the OS MouseLeave(this, EventArgs.Empty); - break; + return IntPtr.Zero; case WindowMessage.MOUSEWHEEL: // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f; - break; + return IntPtr.Zero; case WindowMessage.LBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[MouseButton.Left] = true; - break; + return IntPtr.Zero; case WindowMessage.MBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[MouseButton.Middle] = true; - break; + return IntPtr.Zero; case WindowMessage.RBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[MouseButton.Right] = true; - break; + return IntPtr.Zero; case WindowMessage.XBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; - break; + return IntPtr.Zero; case WindowMessage.LBUTTONUP: Functions.ReleaseCapture(); mouse[MouseButton.Left] = false; - break; + return IntPtr.Zero; case WindowMessage.MBUTTONUP: Functions.ReleaseCapture(); mouse[MouseButton.Middle] = false; - break; + return IntPtr.Zero; case WindowMessage.RBUTTONUP: Functions.ReleaseCapture(); mouse[MouseButton.Right] = false; - break; + return IntPtr.Zero; case WindowMessage.XBUTTONUP: Functions.ReleaseCapture(); mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; - break; + return IntPtr.Zero; // Keyboard events: case WindowMessage.KEYDOWN: @@ -494,7 +494,7 @@ namespace OpenTK.Platform.Windows case WindowMessage.KILLFOCUS: keyboard.ClearKeys(); - break; + return IntPtr.Zero; #endregion @@ -515,7 +515,7 @@ namespace OpenTK.Platform.Windows invisible_since_creation = true; } - break; + return IntPtr.Zero; case WindowMessage.CLOSE: System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); @@ -532,18 +532,16 @@ namespace OpenTK.Platform.Windows case WindowMessage.DESTROY: exists = false; - - Functions.UnregisterClass(ClassName, Instance); - window.Dispose(); - child_window.Dispose(); - Closed(this, EventArgs.Empty); + return IntPtr.Zero; + case WindowMessage.GETICON: break; #endregion } + Debug.Print(message.ToString()); return Functions.DefWindowProc(handle, message, wParam, lParam); } @@ -854,8 +852,8 @@ namespace OpenTK.Platform.Windows icon = value; if (window.Handle != IntPtr.Zero) { - Functions.SendMessage(window.Handle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); - Functions.SendMessage(window.Handle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); + Functions.PostMessage(window.Handle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); + Functions.PostMessage(window.Handle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); } IconChanged(this, EventArgs.Empty); } @@ -1207,7 +1205,7 @@ namespace OpenTK.Platform.Windows MSG msg; public void ProcessEvents() { - while (Functions.PeekMessage(ref msg, window.Handle, 0, 0, PeekMessageFlags.Remove)) + while (Functions.PeekMessage(ref msg, IntPtr.Zero, 0, 0, PeekMessageFlags.Remove)) { Functions.TranslateMessage(ref msg); Functions.DispatchMessage(ref msg); @@ -1298,8 +1296,14 @@ namespace OpenTK.Platform.Windows { // Safe to clean managed resources DestroyWindow(); + Functions.UnregisterClass(ClassName, Instance); + window.Dispose(); + child_window.Dispose(); + if (Icon != null) Icon.Dispose(); + if (ClassName != IntPtr.Zero) + Marshal.FreeHGlobal(ClassName); } else { From 1e4228456f5c5f284b4c111e2bb3fa97c0a423b6 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 00:41:55 +0100 Subject: [PATCH 21/69] Revert "Fix issue #19" This reverts commit 2c14ec5f800b8ef6527f05ba26d6c6cca4b417d8. --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index d2134aae..ccdcbd39 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -257,7 +257,7 @@ namespace OpenTK.Platform.Windows if (new_focused_state != Focused) FocusedChanged(this, EventArgs.Empty); - return IntPtr.Zero; + break; case WindowMessage.ENTERMENULOOP: case WindowMessage.ENTERSIZEMOVE: @@ -269,7 +269,7 @@ namespace OpenTK.Platform.Windows if (!CursorVisible) UngrabCursor(); - return IntPtr.Zero; + break; case WindowMessage.EXITMENULOOP: case WindowMessage.EXITSIZEMOVE: @@ -327,7 +327,7 @@ namespace OpenTK.Platform.Windows } } } - return IntPtr.Zero; + break; case WindowMessage.STYLECHANGED: unsafe @@ -347,8 +347,8 @@ namespace OpenTK.Platform.Windows // Ensure cursor remains grabbed if (!CursorVisible) GrabCursor(); - - return IntPtr.Zero; + + break; case WindowMessage.SIZE: SizeMessage state = (SizeMessage)wParam.ToInt64(); @@ -373,7 +373,7 @@ namespace OpenTK.Platform.Windows GrabCursor(); } - return IntPtr.Zero; + break; #endregion @@ -386,7 +386,7 @@ namespace OpenTK.Platform.Windows key_press.KeyChar = (char)wParam.ToInt64(); KeyPress(this, key_press); - return IntPtr.Zero; + break; case WindowMessage.MOUSEMOVE: Point point = new Point( @@ -403,62 +403,62 @@ namespace OpenTK.Platform.Windows MouseEnter(this, EventArgs.Empty); } - return IntPtr.Zero; + break; case WindowMessage.MOUSELEAVE: mouse_outside_window = true; // Mouse tracking is disabled automatically by the OS MouseLeave(this, EventArgs.Empty); - return IntPtr.Zero; + break; case WindowMessage.MOUSEWHEEL: // This is due to inconsistent behavior of the WParam value on 64bit arch, whese // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 mouse.WheelPrecise += ((long)wParam << 32 >> 48) / 120.0f; - return IntPtr.Zero; + break; case WindowMessage.LBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[MouseButton.Left] = true; - return IntPtr.Zero; + break; case WindowMessage.MBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[MouseButton.Middle] = true; - return IntPtr.Zero; + break; case WindowMessage.RBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[MouseButton.Right] = true; - return IntPtr.Zero; + break; case WindowMessage.XBUTTONDOWN: Functions.SetCapture(window.Handle); mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; - return IntPtr.Zero; + break; case WindowMessage.LBUTTONUP: Functions.ReleaseCapture(); mouse[MouseButton.Left] = false; - return IntPtr.Zero; + break; case WindowMessage.MBUTTONUP: Functions.ReleaseCapture(); mouse[MouseButton.Middle] = false; - return IntPtr.Zero; + break; case WindowMessage.RBUTTONUP: Functions.ReleaseCapture(); mouse[MouseButton.Right] = false; - return IntPtr.Zero; + break; case WindowMessage.XBUTTONUP: Functions.ReleaseCapture(); mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; - return IntPtr.Zero; + break; // Keyboard events: case WindowMessage.KEYDOWN: @@ -494,7 +494,7 @@ namespace OpenTK.Platform.Windows case WindowMessage.KILLFOCUS: keyboard.ClearKeys(); - return IntPtr.Zero; + break; #endregion @@ -515,7 +515,7 @@ namespace OpenTK.Platform.Windows invisible_since_creation = true; } - return IntPtr.Zero; + break; case WindowMessage.CLOSE: System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); @@ -532,16 +532,18 @@ namespace OpenTK.Platform.Windows case WindowMessage.DESTROY: exists = false; - Closed(this, EventArgs.Empty); - return IntPtr.Zero; - case WindowMessage.GETICON: + Functions.UnregisterClass(ClassName, Instance); + window.Dispose(); + child_window.Dispose(); + + Closed(this, EventArgs.Empty); + break; #endregion } - Debug.Print(message.ToString()); return Functions.DefWindowProc(handle, message, wParam, lParam); } @@ -852,8 +854,8 @@ namespace OpenTK.Platform.Windows icon = value; if (window.Handle != IntPtr.Zero) { - Functions.PostMessage(window.Handle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); - Functions.PostMessage(window.Handle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); + Functions.SendMessage(window.Handle, WindowMessage.SETICON, (IntPtr)0, icon == null ? IntPtr.Zero : value.Handle); + Functions.SendMessage(window.Handle, WindowMessage.SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : value.Handle); } IconChanged(this, EventArgs.Empty); } @@ -1205,7 +1207,7 @@ namespace OpenTK.Platform.Windows MSG msg; public void ProcessEvents() { - while (Functions.PeekMessage(ref msg, IntPtr.Zero, 0, 0, PeekMessageFlags.Remove)) + while (Functions.PeekMessage(ref msg, window.Handle, 0, 0, PeekMessageFlags.Remove)) { Functions.TranslateMessage(ref msg); Functions.DispatchMessage(ref msg); @@ -1296,14 +1298,8 @@ namespace OpenTK.Platform.Windows { // Safe to clean managed resources DestroyWindow(); - Functions.UnregisterClass(ClassName, Instance); - window.Dispose(); - child_window.Dispose(); - if (Icon != null) Icon.Dispose(); - if (ClassName != IntPtr.Zero) - Marshal.FreeHGlobal(ClassName); } else { From 6dc474f59581e3560ede7975f347610beadf4202 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 00:43:05 +0100 Subject: [PATCH 22/69] Clean fix issue #19 Isolate and commit fix for issue #19 without potential for regressions. --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index ccdcbd39..6125ffd3 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -1207,7 +1207,7 @@ namespace OpenTK.Platform.Windows MSG msg; public void ProcessEvents() { - while (Functions.PeekMessage(ref msg, window.Handle, 0, 0, PeekMessageFlags.Remove)) + while (Functions.PeekMessage(ref msg, IntPtr.Zero, 0, 0, PeekMessageFlags.Remove)) { Functions.TranslateMessage(ref msg); Functions.DispatchMessage(ref msg); From 8b1566b24458395bd194c29a981c29a3d63f2d26 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 00:50:25 +0100 Subject: [PATCH 23/69] Implemented KeyDown and KeyUp messages --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 6125ffd3..48f7c4b1 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -98,6 +98,8 @@ namespace OpenTK.Platform.Windows public static readonly uint AltLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LMENU, 0); public static readonly uint AltRightScanCode = Functions.MapVirtualKey(VirtualKeys.RMENU, 0); + KeyboardKeyEventArgs key_down = new KeyboardKeyEventArgs(); + KeyboardKeyEventArgs key_up = new KeyboardKeyEventArgs(); KeyPressEventArgs key_press = new KeyPressEventArgs((char)0); int cursor_visible_count = 0; @@ -485,6 +487,18 @@ namespace OpenTK.Platform.Windows if (is_valid) { keyboard.SetKey(key, (byte)scancode, pressed); + + if (pressed) + { + key_down.Key = key; + KeyDown(this, key_down); + } + else + { + key_up.Key = key; + KeyUp(this, key_up); + } + } return IntPtr.Zero; From 98600b00085d13d7d9628b4c630601201763b4fa Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 22:41:10 +0100 Subject: [PATCH 24/69] Display renderer information --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 73d81002..cdb5afa2 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -218,7 +218,10 @@ namespace Examples.Tests 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, String.Format("[1 - 4]: change WindowState (current: {0}).", this.WindowState), line++); From 030cf937a0d397bebc751bc59972b76155fac33b Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 22:41:35 +0100 Subject: [PATCH 25/69] Turn 1-element array to ref/out param --- Source/OpenTK/Platform/Windows/Bindings/Wgl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs b/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs index e235beb8..e1bd92ad 100644 --- a/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs +++ b/Source/OpenTK/Platform/Windows/Bindings/Wgl.cs @@ -202,14 +202,14 @@ namespace OpenTK.Platform.Windows } public static - Boolean ChoosePixelFormat(IntPtr hdc, int[] piAttribIList, Single[] pfAttribFList, Int32 nMaxFormats, [Out] int[] piFormats, [Out] Int32[] nNumFormats) + Boolean ChoosePixelFormat(IntPtr hdc, int[] piAttribIList, Single[] pfAttribFList, Int32 nMaxFormats, [Out] int[] piFormats, out int nNumFormats) { unsafe { fixed (int* piAttribIList_ptr = piAttribIList) fixed (Single* pfAttribFList_ptr = pfAttribFList) fixed (int* piFormats_ptr = piFormats) - fixed (Int32* nNumFormats_ptr = nNumFormats) + fixed (Int32* nNumFormats_ptr = &nNumFormats) { return Delegates.wglChoosePixelFormatARB((IntPtr)hdc, (int*)piAttribIList_ptr, (Single*)pfAttribFList_ptr, (UInt32)nMaxFormats, (int*)piFormats_ptr, (UInt32*)nNumFormats_ptr); } From 0ad87bec3dd9c8bff1712487dba226ae9ae895bf Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 22:43:35 +0100 Subject: [PATCH 26/69] Prioritize accelerated formats first Instead of creating a list of all available formats and iterating through that, we let the driver decide which is the best accelerated format to use for the user parameters. If no such format exists, we fall back to generic acceleration or software acceleration, in turn. This affects issue #21 --- .../Platform/Windows/WinGraphicsMode.cs | 410 +++++++++++------- 1 file changed, 250 insertions(+), 160 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index 0efc07ce..e7cf613e 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -37,27 +37,28 @@ namespace OpenTK.Platform.Windows { class WinGraphicsMode : IGraphicsMode { - #region Fields + enum AccelerationType + { + // Software acceleration + None = 0, + // Partial acceleration (Direct3D emulation) + MCD, + // Full acceleration + ICD, + } - readonly List modes = new List(); static readonly object SyncRoot = new object(); - - #endregion + readonly IntPtr Device; + readonly List modes = new List(); #region Constructors public WinGraphicsMode(IntPtr device) { - lock (SyncRoot) - { - modes.AddRange(GetModesARB(device)); - if (modes.Count == 0) - modes.AddRange(GetModesPFD(device)); - if (modes.Count == 0) - throw new GraphicsModeException( - "No GraphicsMode available. This should never happen, please report a bug at http://www.opentk.com"); - modes.Sort(new GraphicsModeComparer()); - } + if (device == IntPtr.Zero) + throw new ArgumentException(); + + Device = device; } #endregion @@ -67,57 +68,172 @@ namespace OpenTK.Platform.Windows public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples, ColorFormat accum, int buffers, bool stereo) { - GraphicsMode mode = null; - do + GraphicsMode mode = new GraphicsMode(color, depth, stencil, samples,accum, buffers, stereo); + GraphicsMode created_mode = ChoosePixelFormatARB(Device, mode); + + // If ChoosePixelFormatARB failed, iterate through all acceleration types in turn (ICD, MCD, None) + // This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration. + created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.ICD); + created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.MCD); + created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.None); + + if (created_mode == null) { - mode = modes.Find(delegate(GraphicsMode current) - { - return ModeSelector(current, color, depth, stencil, samples, accum, buffers, stereo); - }); - } while (mode == null && RelaxParameters( - ref color, ref depth, ref stencil, ref samples, ref accum, ref buffers, ref stereo)); + throw new GraphicsModeException("The requested GraphicsMode is not supported"); + } - if (mode == null) - mode = modes[0]; - - return mode; - } - - bool RelaxParameters(ref ColorFormat color, ref int depth, ref int stencil, ref int samples, - ref ColorFormat accum, ref int buffers, ref bool stereo) - { - if (stereo) { stereo = false; return true; } - if (buffers != 2) { buffers = 2; return true; } - if (accum != 0) { accum = 0; return true; } - if (samples != 0) { samples = 0; return true; } - if (depth < 16) { depth = 16; return true; } - if (depth != 24) { depth = 24; return true; } - if (stencil > 0 && stencil != 8) { stencil = 8; return true; } - if (stencil == 8) { stencil = 0; return true; } - if (color < 8) { color = 8; return true; } - if (color < 16) { color = 16; return true; } - if (color < 24) { color = 24; return true; } - if (color < 32 || color > 32) { color = 32; return true; } - return false; // We tried everything we could, no match found. + return created_mode; } #endregion #region Private Methods - #region GetModesPFD + #region ChoosePixelFormatARB - IEnumerable GetModesPFD(IntPtr device) + // Queries pixel formats through the WGL_ARB_pixel_format extension + // This method only returns accelerated formats. If no format offers + // hardware acceleration (e.g. we are running in a VM or in a remote desktop + // connection), this method will return 0 formats and we will fall back to + // ChoosePixelFormatPFD. + GraphicsMode ChoosePixelFormatARB(IntPtr device, GraphicsMode mode) { - Debug.WriteLine(String.Format("Device context: {0}", device)); + GraphicsMode created_mode = null; + if (Wgl.Delegates.wglChoosePixelFormatARB != null) + { + List attributes = new List(); + attributes.Add((int)WGL_ARB_pixel_format.AccelerationArb); + attributes.Add((int)WGL_ARB_pixel_format.FullAccelerationArb); - Debug.WriteLine("Retrieving PFD pixel formats... "); + if (mode.ColorFormat.BitsPerPixel > 0) + { + attributes.Add((int)WGL_ARB_pixel_format.RedBitsArb); + attributes.Add(mode.ColorFormat.Red); + attributes.Add((int)WGL_ARB_pixel_format.GreenBitsArb); + attributes.Add(mode.ColorFormat.Green); + attributes.Add((int)WGL_ARB_pixel_format.BlueBitsArb); + attributes.Add(mode.ColorFormat.Blue); + attributes.Add((int)WGL_ARB_pixel_format.AlphaBitsArb); + attributes.Add(mode.ColorFormat.Alpha); + attributes.Add((int)WGL_ARB_pixel_format.ColorBitsArb); + attributes.Add(mode.ColorFormat.BitsPerPixel); + } + + if (mode.Depth > 0) + { + attributes.Add((int)WGL_ARB_pixel_format.DepthBitsArb); + attributes.Add(mode.Depth); + } + + if (mode.Stencil > 0) + { + attributes.Add((int)WGL_ARB_pixel_format.StencilBitsArb); + attributes.Add(mode.Stencil); + } + + if (mode.AccumulatorFormat.BitsPerPixel > 0) + { + attributes.Add((int)WGL_ARB_pixel_format.AccumRedBitsArb); + attributes.Add(mode.AccumulatorFormat.Red); + attributes.Add((int)WGL_ARB_pixel_format.AccumGreenBitsArb); + attributes.Add(mode.AccumulatorFormat.Green); + attributes.Add((int)WGL_ARB_pixel_format.AccumBlueBitsArb); + attributes.Add(mode.AccumulatorFormat.Blue); + attributes.Add((int)WGL_ARB_pixel_format.AccumAlphaBitsArb); + attributes.Add(mode.AccumulatorFormat.Alpha); + attributes.Add((int)WGL_ARB_pixel_format.AccumBitsArb); + attributes.Add(mode.AccumulatorFormat.BitsPerPixel); + } + + if (mode.Samples > 0) + { + attributes.Add((int)WGL_ARB_multisample.SampleBuffersArb); + attributes.Add(1); + attributes.Add((int)WGL_ARB_multisample.SamplesArb); + attributes.Add(mode.Samples); + } + + if (mode.Buffers > 0) + { + attributes.Add((int)WGL_ARB_pixel_format.DoubleBufferArb); + attributes.Add(mode.Buffers); + } + + if (mode.Stereo) + { + attributes.Add((int)WGL_ARB_pixel_format.StereoArb); + attributes.Add(1); + } + + attributes.Add(0); + attributes.Add(0); + + int[] format = new int[1]; + int count; + if (Wgl.Arb.ChoosePixelFormat(device, attributes.ToArray(), null, format.Length, format, out count)) + { + created_mode = DescribePixelFormatARB(device, format[0]); + } + else + { + Debug.Print("[WGL] ChoosePixelFormatARB failed with {0}", Marshal.GetLastWin32Error()); + } + } + else + { + Debug.Print("[WGL] ChoosePixelFormatARB not supported"); + } + + return created_mode; + } + + #endregion + + #region ChoosePixelFormatPFD + + GraphicsMode ChoosePixelFormatPFD(IntPtr Device, GraphicsMode mode, AccelerationType requested_acceleration_type) + { PixelFormatDescriptor pfd = new PixelFormatDescriptor(); - pfd.Size = API.PixelFormatDescriptorSize; - pfd.Version = API.PixelFormatDescriptorVersion; - pfd.Flags = - PixelFormatDescriptorFlags.SUPPORT_OPENGL | - PixelFormatDescriptorFlags.DRAW_TO_WINDOW; + pfd.Size = (short)BlittableValueType.Stride; + + if (mode.ColorFormat.BitsPerPixel > 0) + { + pfd.RedBits = (byte)mode.ColorFormat.Red; + pfd.GreenBits = (byte)mode.ColorFormat.Green; + pfd.BlueBits = (byte)mode.ColorFormat.Blue; + pfd.AlphaBits = (byte)mode.ColorFormat.Alpha; + pfd.ColorBits = (byte)mode.ColorFormat.BitsPerPixel; + } + + if (mode.Depth > 0) + { + pfd.DepthBits = (byte)mode.Depth; + } + + if (mode.Stencil > 0) + { + pfd.StencilBits = (byte)mode.Stencil; + } + + if (mode.AccumulatorFormat.BitsPerPixel > 0) + { + pfd.AccumRedBits = (byte)mode.AccumulatorFormat.Red; + pfd.AccumGreenBits = (byte)mode.AccumulatorFormat.Green; + pfd.AccumBlueBits = (byte)mode.AccumulatorFormat.Blue; + pfd.AccumAlphaBits = (byte)mode.AccumulatorFormat.Alpha; + pfd.AccumBits = (byte)mode.AccumulatorFormat.BitsPerPixel; + } + + if (mode.Buffers > 0) + { + pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER; + } + + if (mode.Stereo) + { + pfd.Flags |= PixelFormatDescriptorFlags.DRAW_TO_WINDOW; + pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_OPENGL; + } // Make sure we don't turn off Aero on Vista and newer. if (Environment.OSVersion.Version.Major >= 6) @@ -125,109 +241,102 @@ namespace OpenTK.Platform.Windows pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION; } - foreach (bool generic_allowed in new bool[] { false, true }) + GraphicsMode created_mode = null; + int pixelformat = Functions.ChoosePixelFormat(Device, ref pfd); + if (pixelformat > 0) { - // Iterate through all accelerated formats first. Afterwards, iterate through non-accelerated formats. - // This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration. - // Note: DescribePixelFormat found in gdi32 is extremely slow on nvidia, for some reason. - int pixel = 0; - while (Functions.DescribePixelFormat(device, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0) + AccelerationType acceleration_type = AccelerationType.ICD; + if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0) { - // Ignore non-accelerated formats. - if (!generic_allowed && (pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0) - continue; + if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_ACCELERATED) != 0) + { + acceleration_type = AccelerationType.MCD; + } + else + { + acceleration_type = AccelerationType.None; + } + } - GraphicsMode fmt = new GraphicsMode((IntPtr)pixel, - new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits), - pfd.DepthBits, - pfd.StencilBits, - 0, - new ColorFormat(pfd.AccumBits), - (pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1, - (pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0); - - yield return fmt; + if (acceleration_type == requested_acceleration_type) + { + created_mode = DescribePixelFormatPFD(ref pfd, pixelformat); } } + return created_mode; } #endregion - #region GetModesARB + #region DescribePixelFormatPFD - // Queries pixel formats through the WGL_ARB_pixel_format extension - // This method only returns accelerated formats. If no format offers - // hardware acceleration (e.g. we are running in a VM or in a remote desktop - // connection), this method will return 0 formats and we will fall back to - // GetModesPFD. - IEnumerable GetModesARB(IntPtr device) + static GraphicsMode DescribePixelFormatPFD(ref PixelFormatDescriptor pfd, int pixelformat) { - // See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt - // for more details - Debug.Write("Retrieving ARB pixel formats.... "); - if (Wgl.Delegates.wglChoosePixelFormatARB == null || Wgl.Delegates.wglGetPixelFormatAttribivARB == null) + return new GraphicsMode( + new IntPtr(pixelformat), + new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits), + pfd.DepthBits, + pfd.StencilBits, + 0, // MSAA not supported + new ColorFormat(pfd.AccumRedBits, pfd.AccumGreenBits, pfd.AccumBlueBits, pfd.AccumAlphaBits), + (pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1, + (pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0); + } + + #endregion + + #region DescribePixelFormatARB + + GraphicsMode DescribePixelFormatARB(IntPtr device, int pixelformat) + { + GraphicsMode created_mode = null; + // See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt for more details + if (Wgl.Delegates.wglGetPixelFormatAttribivARB != null) { - Debug.WriteLine("failed."); - yield break; - } - - // Define the list of attributes we are interested in. - // We will use each available pixel format for these - // attributes using GetPixelFormatAttrib. - // The results will be stored in the 'values' array below. - int[] attribs = new int[] - { - (int)WGL_ARB_pixel_format.AccelerationArb, - - (int)WGL_ARB_pixel_format.RedBitsArb, - (int)WGL_ARB_pixel_format.GreenBitsArb, - (int)WGL_ARB_pixel_format.BlueBitsArb, - (int)WGL_ARB_pixel_format.AlphaBitsArb, - (int)WGL_ARB_pixel_format.ColorBitsArb, - - (int)WGL_ARB_pixel_format.DepthBitsArb, - (int)WGL_ARB_pixel_format.StencilBitsArb, - - (int)WGL_ARB_multisample.SampleBuffersArb, - (int)WGL_ARB_multisample.SamplesArb, - - (int)WGL_ARB_pixel_format.AccumRedBitsArb, - (int)WGL_ARB_pixel_format.AccumGreenBitsArb, - (int)WGL_ARB_pixel_format.AccumBlueBitsArb, - (int)WGL_ARB_pixel_format.AccumAlphaBitsArb, - (int)WGL_ARB_pixel_format.AccumBitsArb, - - (int)WGL_ARB_pixel_format.DoubleBufferArb, - (int)WGL_ARB_pixel_format.StereoArb, - 0 - }; - - // Allocate storage for the results of GetPixelFormatAttrib queries - int[] values = new int[attribs.Length]; - - // Get the number of available formats - int num_formats; - int num_formats_attrib = (int)WGL_ARB_pixel_format.NumberPixelFormatsArb; - if (Wgl.Arb.GetPixelFormatAttrib(device, 0, 0, 1, ref num_formats_attrib, out num_formats)) - { - for (int p = 1; p < num_formats; p++) + // Define the list of attributes we are interested in. + // The results will be stored in the 'values' array below. + int[] attribs = new int[] { - // Get the format attributes for this pixel format - if (!Wgl.Arb.GetPixelFormatAttrib(device, p, 0, attribs.Length - 1, attribs, values)) - { - Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p); - continue; - } + (int)WGL_ARB_pixel_format.AccelerationArb, - // Skip formats that don't offer full hardware acceleration - WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0]; - if (acceleration != WGL_ARB_pixel_format.FullAccelerationArb) - { - continue; - } + (int)WGL_ARB_pixel_format.RedBitsArb, + (int)WGL_ARB_pixel_format.GreenBitsArb, + (int)WGL_ARB_pixel_format.BlueBitsArb, + (int)WGL_ARB_pixel_format.AlphaBitsArb, + (int)WGL_ARB_pixel_format.ColorBitsArb, + + (int)WGL_ARB_pixel_format.DepthBitsArb, + (int)WGL_ARB_pixel_format.StencilBitsArb, + + (int)WGL_ARB_multisample.SampleBuffersArb, + (int)WGL_ARB_multisample.SamplesArb, + (int)WGL_ARB_pixel_format.AccumRedBitsArb, + (int)WGL_ARB_pixel_format.AccumGreenBitsArb, + (int)WGL_ARB_pixel_format.AccumBlueBitsArb, + (int)WGL_ARB_pixel_format.AccumAlphaBitsArb, + (int)WGL_ARB_pixel_format.AccumBitsArb, + + (int)WGL_ARB_pixel_format.DoubleBufferArb, + (int)WGL_ARB_pixel_format.StereoArb, + 0 + }; + + // Allocate storage for the results of GetPixelFormatAttrib queries + int[] values = new int[attribs.Length]; + + // Get the format attributes for this pixel format + if (!Wgl.Arb.GetPixelFormatAttrib(device, pixelformat, 0, attribs.Length - 1, attribs, values)) + { + Debug.Print("[Warning] Failed to detect attributes for PixelFormat: {0}.", pixelformat); + } + + // Skip formats that don't offer full hardware acceleration + WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0]; + if (acceleration == WGL_ARB_pixel_format.FullAccelerationArb) + { // Construct a new GraphicsMode to describe this format - GraphicsMode mode = new GraphicsMode(new IntPtr(p), + created_mode = new GraphicsMode(new IntPtr(pixelformat), new ColorFormat(values[1], values[2], values[3], values[4]), values[6], values[7], @@ -235,28 +344,9 @@ namespace OpenTK.Platform.Windows new ColorFormat(values[10], values[11], values[12], values[13]), values[15] == 1 ? 2 : 1, values[16] == 1 ? true : false); - - yield return mode; } } - } - - #endregion - - #region ModeSelector - - bool ModeSelector(GraphicsMode current, ColorFormat color, int depth, int stencil, int samples, - ColorFormat accum, int buffers, bool stereo) - { - bool result = - (color != ColorFormat.Empty ? current.ColorFormat >= color : true) && - (depth != 0 ? current.Depth >= depth : true) && - (stencil != 0 ? current.Stencil >= stencil : true) && - (samples != 0 ? current.Samples >= samples : true) && - (accum != ColorFormat.Empty ? current.AccumulatorFormat >= accum : true) && - (buffers != 0 ? current.Buffers >= buffers : true) && - current.Stereo == stereo; - return result; + return created_mode; } #endregion From dfd90c8a43c13d873441e450116b83bc32c2485f Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sat, 21 Dec 2013 23:35:55 +0100 Subject: [PATCH 27/69] Fixed DescribePixelFormatPFD When using the PFD codepath, we now call DescribePixelFormat to retrieve an exact interpretation of the pixel format selected by the driver. --- .../Platform/Windows/WinGraphicsMode.cs | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index e7cf613e..541c13ab 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -191,7 +191,7 @@ namespace OpenTK.Platform.Windows #region ChoosePixelFormatPFD - GraphicsMode ChoosePixelFormatPFD(IntPtr Device, GraphicsMode mode, AccelerationType requested_acceleration_type) + GraphicsMode ChoosePixelFormatPFD(IntPtr device, GraphicsMode mode, AccelerationType requested_acceleration_type) { PixelFormatDescriptor pfd = new PixelFormatDescriptor(); pfd.Size = (short)BlittableValueType.Stride; @@ -209,6 +209,10 @@ namespace OpenTK.Platform.Windows { pfd.DepthBits = (byte)mode.Depth; } + else + { + pfd.Flags |= PixelFormatDescriptorFlags.DEPTH_DONTCARE; + } if (mode.Stencil > 0) { @@ -224,10 +228,14 @@ namespace OpenTK.Platform.Windows pfd.AccumBits = (byte)mode.AccumulatorFormat.BitsPerPixel; } - if (mode.Buffers > 0) + if (mode.Buffers > 1) { pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER; } + else if (mode.Buffers == 0) + { + pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER_DONTCARE; + } if (mode.Stereo) { @@ -242,7 +250,7 @@ namespace OpenTK.Platform.Windows } GraphicsMode created_mode = null; - int pixelformat = Functions.ChoosePixelFormat(Device, ref pfd); + int pixelformat = Functions.ChoosePixelFormat(device, ref pfd); if (pixelformat > 0) { AccelerationType acceleration_type = AccelerationType.ICD; @@ -260,7 +268,7 @@ namespace OpenTK.Platform.Windows if (acceleration_type == requested_acceleration_type) { - created_mode = DescribePixelFormatPFD(ref pfd, pixelformat); + created_mode = DescribePixelFormatPFD(device, ref pfd, pixelformat); } } return created_mode; @@ -270,17 +278,22 @@ namespace OpenTK.Platform.Windows #region DescribePixelFormatPFD - static GraphicsMode DescribePixelFormatPFD(ref PixelFormatDescriptor pfd, int pixelformat) + static GraphicsMode DescribePixelFormatPFD(IntPtr device, ref PixelFormatDescriptor pfd, int pixelformat) { - return new GraphicsMode( - new IntPtr(pixelformat), - new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits), - pfd.DepthBits, - pfd.StencilBits, - 0, // MSAA not supported - new ColorFormat(pfd.AccumRedBits, pfd.AccumGreenBits, pfd.AccumBlueBits, pfd.AccumAlphaBits), - (pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1, - (pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0); + GraphicsMode created_mode = null; + if (Functions.DescribePixelFormat(device, pixelformat, pfd.Size, ref pfd) > 0) + { + created_mode = new GraphicsMode( + new IntPtr(pixelformat), + new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits), + pfd.DepthBits, + pfd.StencilBits, + 0, // MSAA not supported when using PixelFormatDescriptor + new ColorFormat(pfd.AccumRedBits, pfd.AccumGreenBits, pfd.AccumBlueBits, pfd.AccumAlphaBits), + (pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1, + (pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0); + } + return created_mode; } #endregion From dec02d5534ebbb2498594f8765f7e2f0e9e75d11 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 09:20:40 +0100 Subject: [PATCH 28/69] Improved WGL mode selection Fixed WGL_ARB_pixel_format attribute selection for doublebuffering, stereoscopic rendering and hardware acceleration. Implemented minimization strategy to select the optimal PixelFormatDescriptor in the fallback path. --- .../Platform/Windows/WinGraphicsMode.cs | 165 ++++++++++-------- 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index 541c13ab..a9544f71 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -105,18 +105,28 @@ namespace OpenTK.Platform.Windows attributes.Add((int)WGL_ARB_pixel_format.AccelerationArb); attributes.Add((int)WGL_ARB_pixel_format.FullAccelerationArb); - if (mode.ColorFormat.BitsPerPixel > 0) + if (mode.ColorFormat.Red > 0) { attributes.Add((int)WGL_ARB_pixel_format.RedBitsArb); attributes.Add(mode.ColorFormat.Red); + } + + if (mode.ColorFormat.Green > 0) + { attributes.Add((int)WGL_ARB_pixel_format.GreenBitsArb); attributes.Add(mode.ColorFormat.Green); + } + + if (mode.ColorFormat.Blue > 0) + { attributes.Add((int)WGL_ARB_pixel_format.BlueBitsArb); attributes.Add(mode.ColorFormat.Blue); + } + + if (mode.ColorFormat.Alpha > 0) + { attributes.Add((int)WGL_ARB_pixel_format.AlphaBitsArb); attributes.Add(mode.ColorFormat.Alpha); - attributes.Add((int)WGL_ARB_pixel_format.ColorBitsArb); - attributes.Add(mode.ColorFormat.BitsPerPixel); } if (mode.Depth > 0) @@ -131,18 +141,28 @@ namespace OpenTK.Platform.Windows attributes.Add(mode.Stencil); } - if (mode.AccumulatorFormat.BitsPerPixel > 0) + if (mode.AccumulatorFormat.Red > 0) { attributes.Add((int)WGL_ARB_pixel_format.AccumRedBitsArb); attributes.Add(mode.AccumulatorFormat.Red); + } + + if (mode.AccumulatorFormat.Green > 0) + { attributes.Add((int)WGL_ARB_pixel_format.AccumGreenBitsArb); attributes.Add(mode.AccumulatorFormat.Green); + } + + if (mode.AccumulatorFormat.Blue > 0) + { attributes.Add((int)WGL_ARB_pixel_format.AccumBlueBitsArb); attributes.Add(mode.AccumulatorFormat.Blue); + } + + if (mode.AccumulatorFormat.Alpha > 0) + { attributes.Add((int)WGL_ARB_pixel_format.AccumAlphaBitsArb); attributes.Add(mode.AccumulatorFormat.Alpha); - attributes.Add((int)WGL_ARB_pixel_format.AccumBitsArb); - attributes.Add(mode.AccumulatorFormat.BitsPerPixel); } if (mode.Samples > 0) @@ -156,13 +176,11 @@ namespace OpenTK.Platform.Windows if (mode.Buffers > 0) { attributes.Add((int)WGL_ARB_pixel_format.DoubleBufferArb); - attributes.Add(mode.Buffers); } if (mode.Stereo) { attributes.Add((int)WGL_ARB_pixel_format.StereoArb); - attributes.Add(1); } attributes.Add(0); @@ -181,7 +199,7 @@ namespace OpenTK.Platform.Windows } else { - Debug.Print("[WGL] ChoosePixelFormatARB not supported"); + Debug.WriteLine("[WGL] ChoosePixelFormatARB not supported on this context"); } return created_mode; @@ -191,94 +209,99 @@ namespace OpenTK.Platform.Windows #region ChoosePixelFormatPFD - GraphicsMode ChoosePixelFormatPFD(IntPtr device, GraphicsMode mode, AccelerationType requested_acceleration_type) + static bool Compare(int got, int requested, ref int distance) { - PixelFormatDescriptor pfd = new PixelFormatDescriptor(); - pfd.Size = (short)BlittableValueType.Stride; - - if (mode.ColorFormat.BitsPerPixel > 0) + if (got < requested) { - pfd.RedBits = (byte)mode.ColorFormat.Red; - pfd.GreenBits = (byte)mode.ColorFormat.Green; - pfd.BlueBits = (byte)mode.ColorFormat.Blue; - pfd.AlphaBits = (byte)mode.ColorFormat.Alpha; - pfd.ColorBits = (byte)mode.ColorFormat.BitsPerPixel; - } - - if (mode.Depth > 0) - { - pfd.DepthBits = (byte)mode.Depth; + return false; } else { - pfd.Flags |= PixelFormatDescriptorFlags.DEPTH_DONTCARE; + distance += got - requested; + return true; } + } - if (mode.Stencil > 0) + static AccelerationType GetAccelerationType(ref PixelFormatDescriptor pfd) + { + AccelerationType type = AccelerationType.ICD; + if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0) { - pfd.StencilBits = (byte)mode.Stencil; + if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_ACCELERATED) != 0) + { + type = AccelerationType.MCD; + } + else + { + type = AccelerationType.None; + } } + return type; + } - if (mode.AccumulatorFormat.BitsPerPixel > 0) - { - pfd.AccumRedBits = (byte)mode.AccumulatorFormat.Red; - pfd.AccumGreenBits = (byte)mode.AccumulatorFormat.Green; - pfd.AccumBlueBits = (byte)mode.AccumulatorFormat.Blue; - pfd.AccumAlphaBits = (byte)mode.AccumulatorFormat.Alpha; - pfd.AccumBits = (byte)mode.AccumulatorFormat.BitsPerPixel; - } - - if (mode.Buffers > 1) - { - pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER; - } - else if (mode.Buffers == 0) - { - pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER_DONTCARE; - } + GraphicsMode ChoosePixelFormatPFD(IntPtr device, GraphicsMode mode, AccelerationType requested_acceleration_type) + { + PixelFormatDescriptor pfd = new PixelFormatDescriptor(); + PixelFormatDescriptorFlags flags = 0; + flags |= PixelFormatDescriptorFlags.DRAW_TO_WINDOW; + flags |= PixelFormatDescriptorFlags.SUPPORT_OPENGL; if (mode.Stereo) { - pfd.Flags |= PixelFormatDescriptorFlags.DRAW_TO_WINDOW; - pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_OPENGL; + flags |= PixelFormatDescriptorFlags.STEREO; + } + if (mode.Buffers > 1) + { + // On Win7 64bit + Nvidia 650M, no pixel format advertises DOUBLEBUFFER. + // Adding this check here causes mode selection to fail. + // Does not appear to be supported by DescribePixelFormat + //flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER; + } + if (System.Environment.OSVersion.Version.Major >= 6) + { + flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION; } - // Make sure we don't turn off Aero on Vista and newer. - if (Environment.OSVersion.Version.Major >= 6) - { - pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION; - } + int count = Functions.DescribePixelFormat(device, 1, API.PixelFormatDescriptorSize, ref pfd); - GraphicsMode created_mode = null; - int pixelformat = Functions.ChoosePixelFormat(device, ref pfd); - if (pixelformat > 0) + int best = 0; + int best_dist = int.MaxValue; + for (int index = 1; index <= count; index++) { - AccelerationType acceleration_type = AccelerationType.ICD; - if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0) - { - if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_ACCELERATED) != 0) - { - acceleration_type = AccelerationType.MCD; - } - else - { - acceleration_type = AccelerationType.None; - } - } + int dist = 0; + bool valid = Functions.DescribePixelFormat(device, index, API.PixelFormatDescriptorSize, ref pfd) != 0; + valid &= GetAccelerationType(ref pfd) == requested_acceleration_type; + valid &= (pfd.Flags & flags) == flags; + valid &= pfd.PixelType == PixelType.RGBA; // indexed modes not currently supported + valid &= Compare(pfd.ColorBits, mode.ColorFormat.BitsPerPixel, ref dist); + valid &= Compare(pfd.RedBits, mode.ColorFormat.Red, ref dist); + valid &= Compare(pfd.GreenBits, mode.ColorFormat.Green, ref dist); + valid &= Compare(pfd.BlueBits, mode.ColorFormat.Blue, ref dist); + valid &= Compare(pfd.AlphaBits, mode.ColorFormat.Alpha, ref dist); + valid &= Compare(pfd.AccumBits, mode.AccumulatorFormat.BitsPerPixel, ref dist); + valid &= Compare(pfd.AccumRedBits, mode.AccumulatorFormat.Red, ref dist); + valid &= Compare(pfd.AccumGreenBits, mode.AccumulatorFormat.Green, ref dist); + valid &= Compare(pfd.AccumBlueBits, mode.AccumulatorFormat.Blue, ref dist); + valid &= Compare(pfd.AccumAlphaBits, mode.AccumulatorFormat.Alpha, ref dist); + valid &= Compare(pfd.DepthBits, mode.Depth, ref dist); + valid &= Compare(pfd.StencilBits, mode.Stencil, ref dist); - if (acceleration_type == requested_acceleration_type) + if (valid && dist < best_dist) { - created_mode = DescribePixelFormatPFD(device, ref pfd, pixelformat); + best = index; + best_dist = dist; } } - return created_mode; + + return DescribePixelFormatPFD(device, ref pfd, best); } #endregion #region DescribePixelFormatPFD - static GraphicsMode DescribePixelFormatPFD(IntPtr device, ref PixelFormatDescriptor pfd, int pixelformat) + static GraphicsMode DescribePixelFormatPFD(IntPtr device, ref PixelFormatDescriptor pfd, + int pixelformat) { GraphicsMode created_mode = null; if (Functions.DescribePixelFormat(device, pixelformat, pfd.Size, ref pfd) > 0) @@ -345,7 +368,7 @@ namespace OpenTK.Platform.Windows } // Skip formats that don't offer full hardware acceleration - WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0]; + WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)values[0]; if (acceleration == WGL_ARB_pixel_format.FullAccelerationArb) { // Construct a new GraphicsMode to describe this format From dbabb6c8383fb9a666d42bacc2c13d0791e456b5 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 10:35:05 +0100 Subject: [PATCH 29/69] Added WGL_DRAW_TO_WINDOW_ARB flag Without this flag, OpenGL rendering does not work as expected. Additionally, all WGL_ARB_pixel_format attributes are expected to be specified in key-value pairs. Fixed double-buffering and stereoscoping rendering attributes. --- Source/OpenTK/Platform/Windows/WinGraphicsMode.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index a9544f71..9de28341 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -104,6 +104,9 @@ namespace OpenTK.Platform.Windows List attributes = new List(); attributes.Add((int)WGL_ARB_pixel_format.AccelerationArb); attributes.Add((int)WGL_ARB_pixel_format.FullAccelerationArb); + + attributes.Add((int)WGL_ARB_pixel_format.DrawToWindowArb); + attributes.Add(1); if (mode.ColorFormat.Red > 0) { @@ -176,11 +179,13 @@ namespace OpenTK.Platform.Windows if (mode.Buffers > 0) { attributes.Add((int)WGL_ARB_pixel_format.DoubleBufferArb); + attributes.Add(mode.Buffers > 1 ? 1 : 0); } if (mode.Stereo) { attributes.Add((int)WGL_ARB_pixel_format.StereoArb); + attributes.Add(1); } attributes.Add(0); From 12cceacf608ceddc3d7c0359a28da7409aec0e06 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 11:10:05 +0100 Subject: [PATCH 30/69] Fixed crash in MakeCurrent(null) MakeCurrent(null) should set the bound device context to zero. --- Source/OpenTK/Platform/Windows/WinGLContext.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 0e727df7..837c3e76 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -279,14 +279,14 @@ namespace OpenTK.Platform.Windows throw new ArgumentException("window", "Must point to a valid window."); success = Wgl.MakeCurrent(wnd.DeviceContext, Handle.Handle); + device_context = wnd.DeviceContext; } else { success = Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); + device_context = IntPtr.Zero; } - device_context = wnd.DeviceContext; - if (!success) { throw new GraphicsContextException(String.Format( From 7692243cd2250c9ed374a918144b3c4d04f5ebb4 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 15:40:01 +0100 Subject: [PATCH 31/69] Remove bogus GraphicsMode.Index check. Fixes #22 GraphicsMode.Index is set by the platform-specific context constructor, which is invoked after the X11GLControl constructor. It does not make sense to check GraphicsMode.Index in the X11GLControl constructor, as it is never set at that point. --- Source/GLControl/X11GLControl.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/GLControl/X11GLControl.cs b/Source/GLControl/X11GLControl.cs index 98c0de82..19d036b7 100644 --- a/Source/GLControl/X11GLControl.cs +++ b/Source/GLControl/X11GLControl.cs @@ -76,8 +76,6 @@ namespace OpenTK throw new ArgumentNullException("mode"); if (control == null) throw new ArgumentNullException("control"); - if (!mode.Index.HasValue) - throw new GraphicsModeException("Invalid or unsupported GraphicsMode."); this.mode = mode; From cbc39f922df6d3dbc2b670299943749c7c7993d6 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:01:04 +0100 Subject: [PATCH 32/69] Implemented IEquatable<> interface --- Source/OpenTK/Input/GamePadButtons.cs | 30 ++++++++++++- Source/OpenTK/Input/GamePadCapabilities.cs | 46 ++++++++++++++++++- Source/OpenTK/Input/GamePadDPad.cs | 51 +++++++++++++++++++++- Source/OpenTK/Input/GamePadState.cs | 32 +++++++++++++- 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs index 199e593f..5d423ddb 100644 --- a/Source/OpenTK/Input/GamePadButtons.cs +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -107,9 +107,37 @@ namespace OpenTK.Input return !left.Equals(right); } + public override string ToString() + { + return String.Format( + "{{ABXYLR: {0}{1}{2}{3}{4}{5}; Back: {6}; BigButton: {7}; LStick: {8}; RStick: {9}}}", + A == ButtonState.Pressed ? "1" : "0", + B == ButtonState.Pressed ? "1" : "0", + X == ButtonState.Pressed ? "1" : "0", + Y == ButtonState.Pressed ? "1" : "0", + LeftShoulder == ButtonState.Pressed ? "1" : "0", + RightShoulder == ButtonState.Pressed ? "1" : "0", + Back == ButtonState.Pressed ? "1" : "0", + BigButton == ButtonState.Pressed ? "1" : "0", + LeftStick == ButtonState.Pressed ? "1" : "0", + RightStick == ButtonState.Pressed ? "1" : "0"); + } + + public override int GetHashCode() + { + return buttons.GetHashCode(); + } + + public override bool Equals(object obj) + { + return + obj is GamePadButtons && + Equals((GamePadButtons)obj); + } + #endregion - #region IEquatable Members + #region IEquatable Members public bool Equals(GamePadButtons other) { diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs index 23c56cd3..767e0198 100644 --- a/Source/OpenTK/Input/GamePadCapabilities.cs +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -32,7 +32,7 @@ using System; namespace OpenTK.Input { - public struct GamePadCapabilities + public struct GamePadCapabilities : IEquatable { byte axis_count; byte button_count; @@ -62,6 +62,50 @@ namespace OpenTK.Input get { return trackball_count; } internal set { trackball_count = (byte)value; } } + + public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right) + { + return left.Equals(right); + } + + public static bool operator !=(GamePadCapabilities left, GamePadCapabilities right) + { + return !left.Equals(right); + } + + public override string ToString() + { + return String.Format( + "{{Axes: {0}; Buttons: {1}; DPads: {2}; Trackballs: {3}}}", + AxisCount, ButtonCount, DPadCount, TrackballCount); + } + + public override int GetHashCode() + { + return + AxisCount.GetHashCode() ^ ButtonCount.GetHashCode() ^ + DPadCount.GetHashCode() ^ TrackballCount.GetHashCode(); + } + + public override bool Equals(object obj) + { + return + obj is GamePadCapabilities && + Equals((GamePadCapabilities)obj); + } + + #region IEquatable Members + + public bool Equals(GamePadCapabilities other) + { + return + AxisCount == other.AxisCount && + ButtonCount == other.ButtonCount && + DPadCount == other.DPadCount && + TrackballCount == other.TrackballCount; + } + + #endregion } } diff --git a/Source/OpenTK/Input/GamePadDPad.cs b/Source/OpenTK/Input/GamePadDPad.cs index 72389b00..06ba2262 100644 --- a/Source/OpenTK/Input/GamePadDPad.cs +++ b/Source/OpenTK/Input/GamePadDPad.cs @@ -31,7 +31,7 @@ using System; namespace OpenTK.Input { - public struct GamePadDPad + public struct GamePadDPad : IEquatable { [Flags] enum DPadButtons : byte @@ -44,6 +44,8 @@ namespace OpenTK.Input DPadButtons buttons; + #region Public Members + internal GamePadDPad(Buttons state) { // DPad butons are stored in the lower 4bits @@ -75,6 +77,42 @@ namespace OpenTK.Input internal set { SetButton(DPadButtons.Right, value); } } + public static bool operator ==(GamePadDPad left, GamePadDPad right) + { + return left.Equals(right); + } + + public static bool operator !=(GamePadDPad left, GamePadDPad right) + { + return !left.Equals(right); + } + + public override string ToString() + { + return String.Format( + "{{ULDR: {0}{1}{2}{3}}}", + IsUp ? "1" : "0", + IsLeft ? "1" : "0", + IsDown ? "1" : "0", + IsRight ? "1" : "0"); + } + + public override int GetHashCode() + { + return buttons.GetHashCode(); + } + + 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) @@ -86,5 +124,16 @@ namespace OpenTK.Input buttons &= ~button; } } + + #endregion + + #region IEquatable Members + + 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 d45787bb..e85187f8 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -32,7 +32,7 @@ namespace OpenTK.Input /// /// Encapsulates the state of a GamePad device. /// - public struct GamePadState /*: IEquatable*/ + public struct GamePadState : IEquatable { const float RangeMultiplier = 1.0f / (short.MaxValue + 1); @@ -62,6 +62,36 @@ namespace OpenTK.Input get { return is_connected; } } + public override string ToString() + { + return String.Format( + "{{Buttons: {0}; DPad: {1}; IsConnected: {2}", + Buttons, DPad, IsConnected); + } + + public override int GetHashCode() + { + return Buttons.GetHashCode() ^ DPad.GetHashCode() ^ IsConnected.GetHashCode(); + } + + public override bool Equals(object obj) + { + return + obj is GamePadState && + Equals((GamePadState)obj); + } + + #endregion + + #region IEquatable Members + public bool Equals(GamePadState other) + { + return + Buttons == other.Buttons && + DPad == other.DPad && + IsConnected == other.IsConnected; + } + #endregion #region Internal Members From ef580daf7545ed75e2feaf4b9fa3c0df473e894e Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:07:30 +0100 Subject: [PATCH 33/69] More compact string representation --- Source/OpenTK/Input/GamePadButtons.cs | 22 +++++++++++----------- Source/OpenTK/Input/GamePadDPad.cs | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs index 5d423ddb..0bc2988f 100644 --- a/Source/OpenTK/Input/GamePadButtons.cs +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -110,17 +110,17 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{ABXYLR: {0}{1}{2}{3}{4}{5}; Back: {6}; BigButton: {7}; LStick: {8}; RStick: {9}}}", - A == ButtonState.Pressed ? "1" : "0", - B == ButtonState.Pressed ? "1" : "0", - X == ButtonState.Pressed ? "1" : "0", - Y == ButtonState.Pressed ? "1" : "0", - LeftShoulder == ButtonState.Pressed ? "1" : "0", - RightShoulder == ButtonState.Pressed ? "1" : "0", - Back == ButtonState.Pressed ? "1" : "0", - BigButton == ButtonState.Pressed ? "1" : "0", - LeftStick == ButtonState.Pressed ? "1" : "0", - RightStick == ButtonState.Pressed ? "1" : "0"); + "{{{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}}}", + A == ButtonState.Pressed ? "A" : String.Empty, + B == ButtonState.Pressed ? "B" : String.Empty, + X == ButtonState.Pressed ? "X" : String.Empty, + Y == ButtonState.Pressed ? "Y" : String.Empty, + LeftShoulder == ButtonState.Pressed ? "L" : String.Empty, + RightShoulder == ButtonState.Pressed ? "R" : String.Empty, + Back == ButtonState.Pressed ? " Back" : String.Empty, + BigButton == ButtonState.Pressed ? " Big" : String.Empty, + LeftStick == ButtonState.Pressed ? " LStick" : String.Empty, + RightStick == ButtonState.Pressed ? " RStick" : String.Empty); } public override int GetHashCode() diff --git a/Source/OpenTK/Input/GamePadDPad.cs b/Source/OpenTK/Input/GamePadDPad.cs index 06ba2262..375036a2 100644 --- a/Source/OpenTK/Input/GamePadDPad.cs +++ b/Source/OpenTK/Input/GamePadDPad.cs @@ -90,11 +90,11 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{ULDR: {0}{1}{2}{3}}}", - IsUp ? "1" : "0", - IsLeft ? "1" : "0", - IsDown ? "1" : "0", - IsRight ? "1" : "0"); + "{{{0}{1}{2}{3}}}", + IsUp ? "U" : String.Empty, + IsLeft ? "L" : String.Empty, + IsDown ? "D" : String.Empty, + IsRight ? "R" : String.Empty); } public override int GetHashCode() From 487e67473faa4820ed5e7e0e3bc4628b9927fed9 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:07:40 +0100 Subject: [PATCH 34/69] Added state information for GamePads --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index cdb5afa2..99724300 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -239,6 +239,8 @@ namespace Examples.Tests DrawKeyboard(gfx, keyboard, line++); DrawMouse(gfx, mouse, line++); DrawJoysticks(gfx, Joysticks, line++); + + line = DrawGamePads(gfx, line); } } @@ -249,7 +251,19 @@ namespace Examples.Tests PixelType.UnsignedByte, data.Scan0); TextBitmap.UnlockBits(data); } - + + int DrawGamePads(Graphics gfx, int line) + { + DrawString(gfx, "GamePads:", line++); + for (int i = 0; i < 4; i++) + { + GamePadCapabilities caps = GamePad.GetCapabilities(i); + GamePadState state = GamePad.GetState(i); + DrawString(gfx, caps.ToString(), line++); + DrawString(gfx, state.ToString(), line++); + } + return line; + } protected override void OnLoad(EventArgs e) { From 1e62821bf19598f97437b2052c34553364ccc295 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:32:18 +0100 Subject: [PATCH 35/69] Implemented GamePadThumbSticks --- Source/OpenTK/Input/GamePadAxis.cs | 28 ++---- Source/OpenTK/Input/GamePadState.cs | 50 ++++++---- Source/OpenTK/Input/GamePadThumbSticks.cs | 111 ++++++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 1 + 4 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 Source/OpenTK/Input/GamePadThumbSticks.cs diff --git a/Source/OpenTK/Input/GamePadAxis.cs b/Source/OpenTK/Input/GamePadAxis.cs index dd6b3192..f619ab6a 100644 --- a/Source/OpenTK/Input/GamePadAxis.cs +++ b/Source/OpenTK/Input/GamePadAxis.cs @@ -27,28 +27,12 @@ using System; namespace OpenTK.Input { - public enum GamePadAxis + internal 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, - } + LeftX, + LeftY, + RightX, + RightY + } } diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index e85187f8..ad603017 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -37,14 +37,17 @@ namespace OpenTK.Input const float RangeMultiplier = 1.0f / (short.MaxValue + 1); Buttons buttons; - unsafe fixed short axes[GamePad.MaxAxisCount]; + short left_stick_x; + short left_stick_y; + short right_stick_x; + short right_stick_y; bool is_connected; #region Public Members - public float GetAxis(GamePadAxis axis) + public GamePadThumbSticks ThumbSticks { - throw new NotImplementedException(); + get { return new GamePadThumbSticks(left_stick_x, left_stick_y, right_stick_x, right_stick_y); } } public GamePadButtons Buttons @@ -65,13 +68,15 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{Buttons: {0}; DPad: {1}; IsConnected: {2}", - Buttons, DPad, IsConnected); + "{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}", + ThumbSticks, Buttons, DPad, IsConnected); } public override int GetHashCode() { - return Buttons.GetHashCode() ^ DPad.GetHashCode() ^ IsConnected.GetHashCode(); + return + ThumbSticks.GetHashCode() ^ Buttons.GetHashCode() ^ + DPad.GetHashCode() ^ IsConnected.GetHashCode(); } public override bool Equals(object obj) @@ -87,6 +92,7 @@ namespace OpenTK.Input public bool Equals(GamePadState other) { return + ThumbSticks == other.ThumbSticks && Buttons == other.Buttons && DPad == other.DPad && IsConnected == other.IsConnected; @@ -98,20 +104,26 @@ namespace OpenTK.Input internal void SetAxis(GamePadAxis axis, short value) { - if (IsAxisValid(axis)) + switch (axis) { - int index = (int)axis; - unsafe - { - fixed (short *paxes = axes) - { - *(paxes + index) = value; - } - } - } - else - { - throw new ArgumentOutOfRangeException("axis"); + case GamePadAxis.LeftX: + left_stick_x = value; + break; + + case GamePadAxis.LeftY: + left_stick_y = value; + break; + + case GamePadAxis.RightX: + right_stick_x = value; + break; + + case GamePadAxis.RightY: + right_stick_x = value; + break; + + default: + throw new ArgumentOutOfRangeException("axis"); } } diff --git a/Source/OpenTK/Input/GamePadThumbSticks.cs b/Source/OpenTK/Input/GamePadThumbSticks.cs new file mode 100644 index 00000000..e7c9d187 --- /dev/null +++ b/Source/OpenTK/Input/GamePadThumbSticks.cs @@ -0,0 +1,111 @@ +using System; +// #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.Collections.Generic; +using System.Text; + +namespace OpenTK.Input +{ + 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 + + public Vector2 Left + { + get { return new Vector2(left_x * ConversionFactor, left_y * ConversionFactor); } + } + + public Vector2 Right + { + get { return new Vector2(right_x * ConversionFactor, right_y * ConversionFactor); } + } + + public static bool operator ==(GamePadThumbSticks left, GamePadThumbSticks right) + { + return left.Equals(right); + } + + public static bool operator !=(GamePadThumbSticks left, GamePadThumbSticks right) + { + return !left.Equals(right); + } + + public override string ToString() + { + return String.Format( + "{{Left: {0}; Right: {1}}}", + Left, Right); + } + + public override int GetHashCode() + { + return + left_x.GetHashCode() ^ left_y.GetHashCode() ^ + right_x.GetHashCode() ^ right_y.GetHashCode(); + } + + public override bool Equals(object obj) + { + return + obj is GamePadThumbSticks && + Equals((GamePadThumbSticks)obj); + } + + #endregion + + #region IEquatable Members + + 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/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 723b4412..aa83bbee 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -133,6 +133,7 @@ + From 7016ad33124cff28ce310d14cb92ba4927b22e45 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:32:24 +0100 Subject: [PATCH 36/69] Added missing newline --- Source/OpenTK/Input/GamePadDPad.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/OpenTK/Input/GamePadDPad.cs b/Source/OpenTK/Input/GamePadDPad.cs index 375036a2..8e1a0588 100644 --- a/Source/OpenTK/Input/GamePadDPad.cs +++ b/Source/OpenTK/Input/GamePadDPad.cs @@ -26,6 +26,7 @@ // THE SOFTWARE. // // #endregion + using System; namespace OpenTK.Input From 16d5055cb12b9840a48274d0f1e30820c73bc6ce Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:34:34 +0100 Subject: [PATCH 37/69] Cleaned up using directives --- Source/OpenTK/Input/GamePadThumbSticks.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Input/GamePadThumbSticks.cs b/Source/OpenTK/Input/GamePadThumbSticks.cs index e7c9d187..59eb4ee3 100644 --- a/Source/OpenTK/Input/GamePadThumbSticks.cs +++ b/Source/OpenTK/Input/GamePadThumbSticks.cs @@ -1,5 +1,4 @@ -using System; -// #region License +// #region License // // GamePadThumbSticks.cs // @@ -29,8 +28,7 @@ // #endregion -using System.Collections.Generic; -using System.Text; +using System; namespace OpenTK.Input { From 98b4883efd98b571cb4a949dfbbd6cf6fbafd4ed Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 22:47:50 +0100 Subject: [PATCH 38/69] Fixed x/y axis mixup. --- Source/OpenTK/Input/GamePadState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index ad603017..b0501f6c 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -119,7 +119,7 @@ namespace OpenTK.Input break; case GamePadAxis.RightY: - right_stick_x = value; + right_stick_y = value; break; default: From 6d0427b928c37fc444d99c7ec8ebd5a126fec5dc Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 23:12:53 +0100 Subject: [PATCH 39/69] Added SDL_InitSubSystem method --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 2232e686..163d393b 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -233,6 +233,10 @@ 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. /// From 5958db383d63d0112cf9eac41c0666da068741a7 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 23:13:21 +0100 Subject: [PATCH 40/69] Delay joystick initialization until necessary --- Source/OpenTK/Configuration.cs | 4 ++-- Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) 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/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index de174875..d2fa0093 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -59,6 +59,7 @@ namespace OpenTK.Platform.SDL2 driver_handle = new IntPtr(count++); DriverHandles.Add(driver_handle, this); SDL.AddEventWatch(EventFilterDelegate, driver_handle); + SDL.InitSubSystem(SystemFlags.JOYSTICK | SystemFlags.GAMECONTROLLER); } } From c1783c9f264543843d5f84fbac6854640beafc9c Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 23:54:55 +0100 Subject: [PATCH 41/69] Reuse Sdl2Factory.InputDriver in Sdl2NativeWindow --- Source/OpenTK/Platform/SDL2/Sdl2Factory.cs | 4 ++-- Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs index 9f2447ba..4c39156f 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() 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; From 9936fa4cc52246a8614846e412c55c9656a4624a Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 23:55:28 +0100 Subject: [PATCH 42/69] Log errors in subsystem initialization --- Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index d2fa0093..3a69be40 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -59,7 +59,14 @@ namespace OpenTK.Platform.SDL2 driver_handle = new IntPtr(count++); DriverHandles.Add(driver_handle, this); SDL.AddEventWatch(EventFilterDelegate, driver_handle); - SDL.InitSubSystem(SystemFlags.JOYSTICK | SystemFlags.GAMECONTROLLER); + 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()); + } } } From 033ba43b7091b8008c1196813d7333e5dcfda4e1 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 23:55:46 +0100 Subject: [PATCH 43/69] Fixed expansion of joysticks collection --- Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 2af3f977..ea0f2433 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -55,7 +55,7 @@ namespace OpenTK.Platform.SDL2 } } - readonly List joysticks = new List(); + readonly List joysticks = new List(4); readonly Dictionary controllers = new Dictionary(); IList joysticks_readonly; @@ -131,16 +131,18 @@ namespace OpenTK.Platform.SDL2 case EventType.JOYDEVICEADDED: { // Make sure we have enough space to store this instance - if (joysticks.Count <= id) + if (joysticks.Capacity <= id) { - joysticks.Capacity = OpenTK.MathHelper.NextPowerOfTwo(id); + joysticks.Capacity = OpenTK.MathHelper.NextPowerOfTwo(id + 1); + for (int i = joysticks.Count; i < joysticks.Capacity; i++) + joysticks.Add(null); // Expand the joysticks list } IntPtr handle = SDL.JoystickOpen(id); if (handle != IntPtr.Zero) { JoystickDevice joystick = OpenJoystick(id); - if (joysticks != null) + if (joystick != null) { joystick.Details.IsConnected = true; joysticks[id] = joystick; From 8350e8e2cec262def63e745e506c7bf3b078c053 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 00:17:13 +0100 Subject: [PATCH 44/69] More robust handling of device add/remove events --- .../Platform/SDL2/Sdl2JoystickDriver.cs | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index ea0f2433..6c7e2728 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -56,6 +56,7 @@ namespace OpenTK.Platform.SDL2 } readonly List joysticks = new List(4); + readonly Dictionary sdl_joyid_to_joysticks = new Dictionary(); readonly Dictionary controllers = new Dictionary(); IList joysticks_readonly; @@ -130,14 +131,6 @@ namespace OpenTK.Platform.SDL2 { case EventType.JOYDEVICEADDED: { - // Make sure we have enough space to store this instance - if (joysticks.Capacity <= id) - { - joysticks.Capacity = OpenTK.MathHelper.NextPowerOfTwo(id + 1); - for (int i = joysticks.Count; i < joysticks.Capacity; i++) - joysticks.Add(null); // Expand the joysticks list - } - IntPtr handle = SDL.JoystickOpen(id); if (handle != IntPtr.Zero) { @@ -145,7 +138,13 @@ namespace OpenTK.Platform.SDL2 if (joystick != null) { joystick.Details.IsConnected = true; - joysticks[id] = joystick; + if (!sdl_joyid_to_joysticks.ContainsKey(id)) + { + sdl_joyid_to_joysticks.Add(id, joysticks.Count); + joysticks.Add(null); + } + int index = sdl_joyid_to_joysticks[id]; + joysticks[index] = joystick; } } } @@ -153,7 +152,8 @@ namespace OpenTK.Platform.SDL2 case EventType.JOYDEVICEREMOVED: { - JoystickDevice joystick = (JoystickDevice)joysticks[id]; + int index = sdl_joyid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; joystick.Details.IsConnected = false; } break; @@ -165,7 +165,8 @@ namespace OpenTK.Platform.SDL2 int id = ev.Which; if (IsJoystickValid(id)) { - JoystickDevice joystick = (JoystickDevice)joysticks[id]; + int index = sdl_joyid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; float value = ev.Value * RangeMultiplier; joystick.SetAxis((JoystickAxis)ev.Axis, value); } @@ -180,7 +181,8 @@ namespace OpenTK.Platform.SDL2 int id = ev.Which; if (IsJoystickValid(id)) { - JoystickDevice joystick = (JoystickDevice)joysticks[id]; + int index = sdl_joyid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; // Todo: does it make sense to support balls? } else @@ -194,7 +196,8 @@ namespace OpenTK.Platform.SDL2 int id = ev.Which; if (IsJoystickValid(id)) { - JoystickDevice joystick = (JoystickDevice)joysticks[id]; + int index = sdl_joyid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed); } else @@ -208,7 +211,8 @@ namespace OpenTK.Platform.SDL2 int id = ev.Which; if (IsJoystickValid(id)) { - JoystickDevice joystick = (JoystickDevice)joysticks[id]; + int index = sdl_joyid_to_joysticks[id]; + JoystickDevice joystick = (JoystickDevice)joysticks[index]; // Todo: map hat to an extra axis } else @@ -224,15 +228,15 @@ namespace OpenTK.Platform.SDL2 switch (ev.Type) { case EventType.CONTROLLERDEVICEADDED: - if (SDL.IsGameController(id)) + IntPtr handle = SDL.GameControllerOpen(id); + if (handle != IntPtr.Zero) { - IntPtr handle = SDL.GameControllerOpen(id); - if (handle != IntPtr.Zero) - { - Sdl2GamePad pad = new Sdl2GamePad(handle); - pad.State.SetConnected(true); - controllers.Add(id, pad); - } + Sdl2GamePad pad = new Sdl2GamePad(handle); + pad.State.SetConnected(true); + // Check whether the device has ever been connected before + if (!controllers.ContainsKey(id)) + controllers.Add(id, null); + controllers[id] = pad; } break; From 61f0918544a676676203facebb6e3a34bfa2d5de Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 01:29:12 +0100 Subject: [PATCH 45/69] Fixed rendering of joysticks and gamepads Joysticks and gamepad states would overlap, causing some lines to be unreadable. This is now fixed. --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 99724300..cc3b29b1 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -163,7 +163,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,6 +192,8 @@ namespace Examples.Tests line++; } + + return line; } protected override void OnUpdateFrame(FrameEventArgs e) @@ -238,9 +240,8 @@ namespace Examples.Tests DrawString(gfx, TypedText.ToString(), line++); DrawKeyboard(gfx, keyboard, line++); DrawMouse(gfx, mouse, line++); - DrawJoysticks(gfx, Joysticks, line++); - - line = DrawGamePads(gfx, line); + line = DrawJoysticks(gfx, Joysticks, line++); + line = DrawGamePads(gfx, line++); } } From c89ddaa2254a0f380501ccc1b69f8a16e974a4fe Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 01:49:49 +0100 Subject: [PATCH 46/69] Display start button in ToString() --- Source/OpenTK/Input/GamePadButtons.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs index 0bc2988f..aeb97b59 100644 --- a/Source/OpenTK/Input/GamePadButtons.cs +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -118,6 +118,7 @@ namespace OpenTK.Input LeftShoulder == ButtonState.Pressed ? "L" : String.Empty, RightShoulder == ButtonState.Pressed ? "R" : String.Empty, Back == ButtonState.Pressed ? " Back" : String.Empty, + Start == ButtonState.Pressed ? " Start" : String.Empty, BigButton == ButtonState.Pressed ? " Big" : String.Empty, LeftStick == ButtonState.Pressed ? " LStick" : String.Empty, RightStick == ButtonState.Pressed ? " RStick" : String.Empty); From f9394b9ba6cced5185999139aaf76d719c0fd0ca Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 01:50:13 +0100 Subject: [PATCH 47/69] Removed unnecessary IsButtonValid method --- Source/OpenTK/Input/GamePad.cs | 1 - Source/OpenTK/Input/GamePadState.cs | 22 +++------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index 7a2fc066..a0a63f49 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -35,7 +35,6 @@ namespace OpenTK.Input public class GamePad { internal const int MaxAxisCount = 10; - internal const int MaxButtonCount = 16; // if this grows over 32 then GamePadState.buttons must be modified internal const int MaxDPadCount = 2; static readonly IGamePadDriver driver = diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index b0501f6c..ceec371f 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -129,23 +129,13 @@ namespace OpenTK.Input internal void SetButton(Buttons button, bool pressed) { - if (IsButtonValid(button)) + if (pressed) { - int index = (int)button; - - Buttons mask = (Buttons)(1 << index); - if (pressed) - { - buttons |= mask; - } - else - { - buttons &= ~mask; - } + buttons |= button; } else { - throw new ArgumentOutOfRangeException("button"); + buttons &= ~button; } } @@ -164,12 +154,6 @@ namespace OpenTK.Input return index >= 0 && index < GamePad.MaxAxisCount; } - bool IsButtonValid(Buttons button) - { - int index = (int)button; - return index >= 0 && index < GamePad.MaxButtonCount; - } - bool IsDPadValid(int index) { return index >= 0 && index < GamePad.MaxDPadCount; From a7db0d76db7e5b409b55f5f132725b0e551885d3 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 01:50:25 +0100 Subject: [PATCH 48/69] Implemented GamePad API (WIP) --- .../OpenTK/Platform/Windows/WinMMJoystick.cs | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index ca229700..07c208cc 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -133,6 +133,11 @@ namespace OpenTK.Platform.Windows return stick; } + bool IsValid(int index) + { + return index >= 0 && index < UnsafeNativeMethods.joyGetNumDevs(); + } + #endregion #region IJoystickDriver @@ -432,12 +437,64 @@ namespace OpenTK.Platform.Windows public GamePadCapabilities GetCapabilities(int index) { - throw new NotImplementedException(); + GamePadCapabilities gpcaps = new GamePadCapabilities(); + + if (IsValid(index)) + { + JoyCaps caps; + JoystickError result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes); + if (result == JoystickError.NoError) + { + gpcaps.AxisCount = caps.NumAxes; + gpcaps.ButtonCount = caps.NumButtons; + if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0) + gpcaps.DPadCount++; + } + } + else + { + Debug.Print("[Win] Invalid WinMM joystick device {0}", index); + } + + return gpcaps; } public GamePadState GetState(int index) { - throw new NotImplementedException(); + GamePadState state = new GamePadState(); + + if (IsValid(index)) + { + JoyInfoEx info = new JoyInfoEx(); + info.Size = JoyInfoEx.SizeInBytes; + info.Flags = JoystickFlags.All; + UnsafeNativeMethods.joyGetPosEx(index, ref info); + + state.SetAxis(GamePadAxis.LeftX, (short)info.XPos); + state.SetAxis(GamePadAxis.LeftY, (short)info.YPos); + state.SetAxis(GamePadAxis.RightX, (short)info.ZPos); + state.SetAxis(GamePadAxis.RightY, (short)info.RPos); + //state.SetAxis(GamePadAxis.RightX, (short)info.ZPos); + //state.SetAxis(GamePadAxis.RightY, (short)info.RPos); + + state.SetButton(Buttons.A, (info.Buttons & 1 << 0) != 0); + state.SetButton(Buttons.B, (info.Buttons & 1 << 1) != 0); + state.SetButton(Buttons.X, (info.Buttons & 1 << 2) != 0); + state.SetButton(Buttons.Y, (info.Buttons & 1 << 3) != 0); + state.SetButton(Buttons.LeftShoulder, (info.Buttons & 1 << 4) != 0); + state.SetButton(Buttons.RightShoulder, (info.Buttons & 1 << 5) != 0); + state.SetButton(Buttons.Back, (info.Buttons & 1 << 6) != 0); + state.SetButton(Buttons.Start, (info.Buttons & 1 << 7) != 0); + state.SetButton(Buttons.LeftStick, (info.Buttons & 1 << 8) != 0); + state.SetButton(Buttons.RightStick, (info.Buttons & 1 << 9) != 0); + state.SetButton(Buttons.BigButton, (info.Buttons & 1 << 10) != 0); + } + else + { + Debug.Print("[Win] Invalid WinMM joystick device {0}", index); + } + + return state; } public string GetName(int index) From d8803662085537520ef1500af0cd44735c18d6c1 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 19:19:41 +0100 Subject: [PATCH 49/69] Enabled HIDInput IGamePadDriver implementation --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 8e3c6011..c3e55612 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, IGamePadDriver { #region Fields @@ -291,7 +291,7 @@ 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 this; } } #endregion @@ -366,6 +366,25 @@ namespace OpenTK.Platform.MacOS #endregion + #region IGamePadDriver Members + + public GamePadState GetState(int index) + { + return new GamePadState(); + } + + public GamePadCapabilities GetCapabilities(int index) + { + return new GamePadCapabilities(); + } + + public string GetName(int index) + { + throw new NotImplementedException(); + } + + #endregion + #region NativeMethods class NativeMethods From f7e2c2ea7c8b19aac50a6c86c58e21b95d664a2c Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 20:30:58 +0100 Subject: [PATCH 50/69] Initial implementation of GamePadTriggers --- Source/OpenTK/Input/GamePadTriggers.cs | 102 +++++++++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 1 + 2 files changed, 103 insertions(+) create mode 100644 Source/OpenTK/Input/GamePadTriggers.cs diff --git a/Source/OpenTK/Input/GamePadTriggers.cs b/Source/OpenTK/Input/GamePadTriggers.cs new file mode 100644 index 00000000..ecff49c7 --- /dev/null +++ b/Source/OpenTK/Input/GamePadTriggers.cs @@ -0,0 +1,102 @@ +// #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 +{ + 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 + + public float Left + { + get { return left * ConversionFactor; } + } + + public float Right + { + get { return right * ConversionFactor; } + } + + public static bool operator ==(GamePadTriggers left, GamePadTriggers right) + { + return left.Equals(right); + } + + public static bool operator !=(GamePadTriggers left, GamePadTriggers right) + { + return !left.Equals(right); + } + + public override string ToString() + { + return String.Format( + "{{Left: {0}; Right: {1}}}", + Left, Right); + } + + public override int GetHashCode() + { + return + left.GetHashCode() ^ right.GetHashCode(); + } + + public override bool Equals(object obj) + { + return + obj is GamePadTriggers && + Equals((GamePadTriggers)obj); + } + + #endregion + + #region IEquatable Members + + public bool Equals(GamePadTriggers other) + { + return + left == other.left && + right == other.right; + } + + #endregion + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index aa83bbee..629191e5 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -134,6 +134,7 @@ + From 95c3d00aa27a3c8700caef58e80dc2ed3e5c66a5 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 22:00:10 +0100 Subject: [PATCH 51/69] Refresh text continuously --- .../Examples/OpenTK/Test/GameWindowStates.cs | 67 ++++++++----------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index cc3b29b1..15be8241 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) @@ -197,52 +196,40 @@ namespace Examples.Tests } 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++); - line = DrawJoysticks(gfx, Joysticks, line++); - line = DrawGamePads(gfx, 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( From 1d84c1d814c34fcccd48b9a730eedf3f2e0220d4 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 00:15:28 +0100 Subject: [PATCH 52/69] Implemented GamePad Capabilities and Type --- Source/OpenTK/Input/GamePadCapabilities.cs | 194 ++++++++++++++++++--- Source/OpenTK/Input/GamePadType.cs | 45 +++++ Source/OpenTK/OpenTK.csproj | 1 + 3 files changed, 220 insertions(+), 20 deletions(-) create mode 100644 Source/OpenTK/Input/GamePadType.cs diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs index 767e0198..16707aba 100644 --- a/Source/OpenTK/Input/GamePadCapabilities.cs +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -39,28 +39,152 @@ namespace OpenTK.Input byte dpad_count; byte trackball_count; - public int AxisCount + Buttons buttons; + byte gamepad_type; + bool is_connected; + + #region Constructors + + public GamePadCapabilities(GamePadType type, Buttons buttons, bool is_connected) + : this() { - get { return axis_count; } - internal set { axis_count = (byte)value; } + gamepad_type = (byte)type; + this.buttons = buttons; + this.is_connected = is_connected; } - public int ButtonCount + #endregion + + #region Public Members + + public GamePadType GamePadType { - get { return button_count; } - internal set { button_count = (byte)value; } + get { return (GamePadType)gamepad_type; } } - public int DPadCount + public bool HasDPadUpButton { - get { return dpad_count; } - internal set { dpad_count = (byte)value; } + get { return (buttons & Buttons.DPadUp) != 0; } } - public int TrackballCount + public bool HasDPadLeftButton { - get { return trackball_count; } - internal set { trackball_count = (byte)value; } + get { return (buttons & Buttons.DPadLeft) != 0; } + } + + public bool HasDPadDownButton + { + get { return (buttons & Buttons.DPadDown) != 0; } + } + + public bool HasDPadRightButton + { + get { return (buttons & Buttons.DPadRight) != 0; } + } + + public bool HasAButton + { + get { return (buttons & Buttons.A) != 0; } + } + + public bool HasBButton + { + get { return (buttons & Buttons.B) != 0; } + } + + public bool HasXButton + { + get { return (buttons & Buttons.X) != 0; } + } + + public bool HasYButton + { + get { return (buttons & Buttons.Y) != 0; } + } + + public bool HasLeftStickButton + { + get { return (buttons & Buttons.LeftStick) != 0; } + } + + public bool HasRightStickButton + { + get { return (buttons & Buttons.RightStick) != 0; } + } + + public bool HasLeftShoulderButton + { + get { return (buttons & Buttons.LeftShoulder) != 0; } + } + + public bool HasRightShoulderButton + { + get { return (buttons & Buttons.RightShoulder) != 0; } + } + + public bool HasBackButton + { + get { return (buttons & Buttons.Back) != 0; } + } + + public bool HasBigButton + { + get { return (buttons & Buttons.BigButton) != 0; } + } + + public bool HasStartButton + { + get { return (buttons & Buttons.Start) != 0; } + } + + public bool HasLeftXThumbStick + { + get { return false; } + } + + public bool HasLeftYThumbStick + { + get { return false; } + } + + public bool HasRightXThumbStick + { + get { return false; } + } + + public bool HasRightYThumbStick + { + get { return false; } + } + + public bool HasLeftTrigger + { + get { return false; } + } + + public bool HasRightTrigger + { + get { return false; } + } + + public bool HasLeftVibrationMotor + { + get { return false; } + } + + public bool HasRightVibrationMotor + { + get { return false; } + } + + public bool HasVoiceSupport + { + get { return false; } + } + + public bool IsConnected + { + get { return is_connected; } } public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right) @@ -76,15 +200,16 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{Axes: {0}; Buttons: {1}; DPads: {2}; Trackballs: {3}}}", - AxisCount, ButtonCount, DPadCount, TrackballCount); + "{{Type: {0}; Buttons: {1}; Connected: {2}}}", + GamePadType, Convert.ToString((int)buttons, 2), IsConnected); } public override int GetHashCode() { return - AxisCount.GetHashCode() ^ ButtonCount.GetHashCode() ^ - DPadCount.GetHashCode() ^ TrackballCount.GetHashCode(); + buttons.GetHashCode() ^ + is_connected.GetHashCode() ^ + gamepad_type.GetHashCode(); } public override bool Equals(object obj) @@ -94,15 +219,44 @@ namespace OpenTK.Input Equals((GamePadCapabilities)obj); } + #endregion + + #region Internal Members + + internal int AxisCount + { + get { return axis_count; } + set { axis_count = (byte)value; } + } + + internal int ButtonCount + { + get { return button_count; } + set { button_count = (byte)value; } + } + + internal int DPadCount + { + get { return dpad_count; } + set { dpad_count = (byte)value; } + } + + internal int TrackballCount + { + get { return trackball_count; } + set { trackball_count = (byte)value; } + } + + #endregion + #region IEquatable Members public bool Equals(GamePadCapabilities other) { return - AxisCount == other.AxisCount && - ButtonCount == other.ButtonCount && - DPadCount == other.DPadCount && - TrackballCount == other.TrackballCount; + buttons == other.buttons && + is_connected == other.is_connected && + gamepad_type == other.gamepad_type; } #endregion diff --git a/Source/OpenTK/Input/GamePadType.cs b/Source/OpenTK/Input/GamePadType.cs new file mode 100644 index 00000000..a4eddef8 --- /dev/null +++ b/Source/OpenTK/Input/GamePadType.cs @@ -0,0 +1,45 @@ +// #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 +{ + public enum GamePadType + { + Unknown = 0, + ArcadeStick, + DancePad, + FlightStick, + Guitar, + Wheel, + AlternateGuitar, + BigButtonPad, + DrumKit, + GamePad, + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 629191e5..81fec8c7 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -135,6 +135,7 @@ + From de362177c5d753ae6d4ee0e9deb4d621c3647ca2 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 00:15:43 +0100 Subject: [PATCH 53/69] Removed all instances of refresh_text --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 15be8241..8359a552 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -39,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) @@ -98,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; From 61e2dc3d8651a901cc9c93e180fe6c4511df44fc Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 01:36:44 +0100 Subject: [PATCH 54/69] Added missing left/right triggers --- Source/OpenTK/Input/GamePadState.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index ceec371f..22111b53 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -41,6 +41,8 @@ namespace OpenTK.Input short left_stick_y; short right_stick_x; short right_stick_y; + byte left_trigger; + byte right_trigger; bool is_connected; #region Public Members From 78078d07420377406bb06adf30f76e36687a0b10 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 01:37:34 +0100 Subject: [PATCH 55/69] Implemented initial XInput IGamePadDriver --- Source/OpenTK/OpenTK.csproj | 1 + .../OpenTK/Platform/Windows/XInputJoystick.cs | 299 ++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 Source/OpenTK/Platform/Windows/XInputJoystick.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 81fec8c7..c7de48e4 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -158,6 +158,7 @@ + Code diff --git a/Source/OpenTK/Platform/Windows/XInputJoystick.cs b/Source/OpenTK/Platform/Windows/XInputJoystick.cs new file mode 100644 index 00000000..f3e0c04e --- /dev/null +++ b/Source/OpenTK/Platform/Windows/XInputJoystick.cs @@ -0,0 +1,299 @@ +// #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; + +namespace OpenTK.Platform.Windows +{ + class XInputJoystick : IGamePadDriver + { + #region IGamePadDriver Members + + public GamePadState GetState(int index) + { + XInputState xstate; + XInput910.GetState((XInputUserIndex)index, out xstate); + + GamePadState state = new GamePadState(); + state.SetButton(Buttons.A, (xstate.GamePad.Buttons & XInputButtons.A) != 0); + state.SetButton(Buttons.B, (xstate.GamePad.Buttons & XInputButtons.B) != 0); + state.SetButton(Buttons.X, (xstate.GamePad.Buttons & XInputButtons.X) != 0); + state.SetButton(Buttons.Y, (xstate.GamePad.Buttons & XInputButtons.Y) != 0); + state.SetButton(Buttons.Start, (xstate.GamePad.Buttons & XInputButtons.Start) != 0); + state.SetButton(Buttons.Back, (xstate.GamePad.Buttons & XInputButtons.Back) != 0); + //state.SetButton(Buttons.BigButton, (xstate.GamePad.Buttons & XInputButtons.???) != 0); + state.SetButton(Buttons.LeftShoulder, (xstate.GamePad.Buttons & XInputButtons.LeftShoulder) != 0); + state.SetButton(Buttons.RightShoulder, (xstate.GamePad.Buttons & XInputButtons.RightShoulder) != 0); + state.SetButton(Buttons.LeftStick, (xstate.GamePad.Buttons & XInputButtons.LeftThumb) != 0); + state.SetButton(Buttons.RightStick, (xstate.GamePad.Buttons & XInputButtons.RightThumb) != 0); + state.SetButton(Buttons.DPadDown, (xstate.GamePad.Buttons & XInputButtons.DPadDown) != 0); + state.SetButton(Buttons.DPadUp, (xstate.GamePad.Buttons & XInputButtons.DPadUp) != 0); + state.SetButton(Buttons.DPadLeft, (xstate.GamePad.Buttons & XInputButtons.DPadLeft) != 0); + state.SetButton(Buttons.DPadRight, (xstate.GamePad.Buttons & XInputButtons.DPadRight) != 0); + + return state; + } + + public GamePadCapabilities GetCapabilities(int index) + { + throw new NotImplementedException(); + } + + public string GetName(int index) + { + throw new NotImplementedException(); + } + + #endregion + + #region Private Members + + enum XInputErrorCode + { + Success = 0, + DeviceNotConnected + } + + enum XInputType + { + GamePad + } + + enum XInputSubType + { + Unknown = 0, + GamePad = 1, + Wheel = 2, + ArcadeStick = 3, + FlightStick = 4, + DancePad = 5, + Guitar = 6, + GuitarAlternate = 7, + DrumKit = 8, + GuitarBass = 0xb, + ArcadePad = 0x13 + } + + enum XInputCapabilities + { + Ffb = 0x0001, + Wireless = 0x0002, + Voice = 0x0004, + Pmd = 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 byte Type; + public byte SubType; + public short Flags; + public XInputGamePad GamePad; + public XInputVibration Vibration; + } + + struct XInputBatteryInformation + { + public XInputBatteryType Type; + public XInputBatteryLevel Level; + } + + static class XInput910 + { + const string dll = "XINPUT9_1_0"; + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputGetCapabilities", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode GetCapabilities( + XInputUserIndex dwUserIndex, + XInputCapabilitiesFlags dwFlags, + ref XInputDeviceCapabilities pCapabilities); + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputGetState", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode GetState + ( + XInputUserIndex dwUserIndex, + out XInputState pState + ); + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputSetState", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode SetState + ( + XInputUserIndex dwUserIndex, + ref XInputVibration pVibration + ); + } + + static class XInput13 + { + const string dll = "XINPUT1_3"; + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputGetCapabilities", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode GetCapabilities( + XInputUserIndex dwUserIndex, + XInputCapabilitiesFlags dwFlags, + ref XInputDeviceCapabilities pCapabilities); + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputGetState", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode GetState + ( + XInputUserIndex dwUserIndex, + out XInputState pState + ); + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputSetState", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode SetState + ( + XInputUserIndex dwUserIndex, + ref XInputVibration pVibration + ); + } + + static class XInput14 + { + const string dll = "XINPUT1_4"; + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputGetCapabilities", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode GetCapabilities( + XInputUserIndex dwUserIndex, + XInputCapabilitiesFlags dwFlags, + ref XInputDeviceCapabilities pCapabilities); + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputGetState", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode GetState + ( + XInputUserIndex dwUserIndex, + out XInputState pState + ); + + [SuppressUnmanagedCodeSecurity] + [DllImport(dll, EntryPoint = "XInputSetState", ExactSpelling = true, SetLastError = false)] + public static extern XInputErrorCode SetState + ( + XInputUserIndex dwUserIndex, + ref XInputVibration pVibration + ); + } + + #endregion + } +} From 5215891a4fbc8882aa2cf77174119d902f6f23bd Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 03:16:32 +0100 Subject: [PATCH 56/69] Added IJoystickDevice2 interface --- Source/OpenTK/Input/GamePadCapabilities.cs | 33 -------- Source/OpenTK/Input/GamePadType.cs | 4 +- Source/OpenTK/Input/IInputDriver2.cs | 1 + Source/OpenTK/Input/IJoystickDriver2.cs | 41 ++++++++++ Source/OpenTK/Input/Joystick.cs | 51 ++++++++++++ Source/OpenTK/Input/JoystickAxis.cs | 62 +++++++++++++++ Source/OpenTK/Input/JoystickButton.cs | 78 ++++++++++++++++++ Source/OpenTK/Input/JoystickCapabilities.cs | 75 ++++++++++++++++++ Source/OpenTK/Input/JoystickDevice.cs | 74 ----------------- Source/OpenTK/Input/JoystickState.cs | 79 +++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 6 ++ Source/OpenTK/Platform/Factory.cs | 10 +++ Source/OpenTK/Platform/IPlatformFactory.cs | 2 + Source/OpenTK/Platform/MacOS/CarbonInput.cs | 5 ++ Source/OpenTK/Platform/MacOS/HIDInput.cs | 20 ++++- Source/OpenTK/Platform/MacOS/MacOSFactory.cs | 5 ++ Source/OpenTK/Platform/SDL2/Sdl2Factory.cs | 5 ++ .../OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 8 ++ .../Platform/SDL2/Sdl2JoystickDriver.cs | 16 +++- Source/OpenTK/Platform/Windows/WinFactory.cs | 7 +- .../OpenTK/Platform/Windows/WinInputBase.cs | 2 + .../OpenTK/Platform/Windows/WinMMJoystick.cs | 8 +- Source/OpenTK/Platform/Windows/WinRawInput.cs | 5 ++ .../OpenTK/Platform/Windows/XInputJoystick.cs | 4 +- Source/OpenTK/Platform/X11/X11Factory.cs | 6 ++ Source/OpenTK/Platform/X11/X11Joystick.cs | 16 +++- 26 files changed, 503 insertions(+), 120 deletions(-) create mode 100644 Source/OpenTK/Input/IJoystickDriver2.cs create mode 100644 Source/OpenTK/Input/Joystick.cs create mode 100644 Source/OpenTK/Input/JoystickAxis.cs create mode 100644 Source/OpenTK/Input/JoystickButton.cs create mode 100644 Source/OpenTK/Input/JoystickCapabilities.cs create mode 100644 Source/OpenTK/Input/JoystickState.cs diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs index 16707aba..c637f771 100644 --- a/Source/OpenTK/Input/GamePadCapabilities.cs +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -34,11 +34,6 @@ namespace OpenTK.Input public struct GamePadCapabilities : IEquatable { - byte axis_count; - byte button_count; - byte dpad_count; - byte trackball_count; - Buttons buttons; byte gamepad_type; bool is_connected; @@ -221,34 +216,6 @@ namespace OpenTK.Input #endregion - #region Internal Members - - internal int AxisCount - { - get { return axis_count; } - set { axis_count = (byte)value; } - } - - internal int ButtonCount - { - get { return button_count; } - set { button_count = (byte)value; } - } - - internal int DPadCount - { - get { return dpad_count; } - set { dpad_count = (byte)value; } - } - - internal int TrackballCount - { - get { return trackball_count; } - set { trackball_count = (byte)value; } - } - - #endregion - #region IEquatable Members public bool Equals(GamePadCapabilities other) diff --git a/Source/OpenTK/Input/GamePadType.cs b/Source/OpenTK/Input/GamePadType.cs index a4eddef8..1524e024 100644 --- a/Source/OpenTK/Input/GamePadType.cs +++ b/Source/OpenTK/Input/GamePadType.cs @@ -1,4 +1,4 @@ -// #region License +#region License // // GamePadType.cs // @@ -25,7 +25,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// #endregion +#endregion namespace OpenTK.Input { 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..1359d1de --- /dev/null +++ b/Source/OpenTK/Input/IJoystickDriver2.cs @@ -0,0 +1,41 @@ +#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); + } +} diff --git a/Source/OpenTK/Input/Joystick.cs b/Source/OpenTK/Input/Joystick.cs new file mode 100644 index 00000000..0458abed --- /dev/null +++ b/Source/OpenTK/Input/Joystick.cs @@ -0,0 +1,51 @@ +#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 +{ + public class Joystick + { + static readonly IJoystickDriver2 implementation = + Platform.Factory.Default.CreateJoystickDriver(); + + public static JoystickCapabilities GetCapabilities(int index) + { + return implementation.GetCapabilities(index); + } + + public static JoystickState GetState(int index) + { + return implementation.GetState(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..cf3a9ca6 --- /dev/null +++ b/Source/OpenTK/Input/JoystickButton.cs @@ -0,0 +1,78 @@ +#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 +{ + #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 +} diff --git a/Source/OpenTK/Input/JoystickCapabilities.cs b/Source/OpenTK/Input/JoystickCapabilities.cs new file mode 100644 index 00000000..5265d209 --- /dev/null +++ b/Source/OpenTK/Input/JoystickCapabilities.cs @@ -0,0 +1,75 @@ +#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 +{ + public struct JoystickCapabilities + { + byte axis_count; + byte button_count; + byte dpad_count; + byte trackball_count; + + #region Public Members + + public int AxisCount + { + get { return axis_count; } + set { axis_count = (byte)value; } + } + + public int ButtonCount + { + get { return button_count; } + set { button_count = (byte)value; } + } + + #endregion + + #region Private Members + + int DPadCount + { + get { return dpad_count; } + set { dpad_count = (byte)value; } + } + + int TrackballCount + { + get { return trackball_count; } + set { trackball_count = (byte)value; } + } + + #endregion + } +} diff --git a/Source/OpenTK/Input/JoystickDevice.cs b/Source/OpenTK/Input/JoystickDevice.cs index 6f99cdac..d02d5b4c 100644 --- a/Source/OpenTK/Input/JoystickDevice.cs +++ b/Source/OpenTK/Input/JoystickDevice.cs @@ -271,49 +271,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 +333,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..28775e69 --- /dev/null +++ b/Source/OpenTK/Input/JoystickState.cs @@ -0,0 +1,79 @@ +#region License +// +// JoystickState.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 +{ + public struct JoystickState + { + const int MaxAxes = 10; // JoystickAxis defines 10 axes + const float ConversionFactor = 1.0f / short.MaxValue; + unsafe fixed short axes[MaxAxes]; + JoystickButton buttons; + + public float GetAxis(JoystickAxis axis) + { + return GetAxis((int)axis); + } + + public float GetAxis(int axis) + { + float value = 0.0f; + if (axis >= 0 && axis < MaxAxes) + { + unsafe + { + fixed (short* paxis = axes) + { + value = *(paxis + axis) * ConversionFactor; + } + } + } + else + { + Debug.Print("[Joystick] Invalid axis {0}", axis); + } + return value; + } + + public bool IsButtonDown(JoystickButton button) + { + return (buttons & button) != 0; + } + + public bool IsButtonUp(JoystickButton button) + { + return (buttons & button) == 0; + } + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index c7de48e4..01e5c793 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -138,8 +138,14 @@ + + + + + + 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 c3e55612..613d1c64 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, IGamePadDriver, IJoystickDriver2 { #region Fields @@ -93,7 +93,7 @@ namespace OpenTK.Platform.MacOS #endregion - #region Private Members + #region Private Members IOHIDManagerRef CreateHIDManager() { @@ -292,6 +292,8 @@ namespace OpenTK.Platform.MacOS public IMouseDriver2 MouseDriver { get { return this; } } public IKeyboardDriver2 KeyboardDriver { get { return this; } } public IGamePadDriver GamePadDriver { get { return this; } } + public IJoystickDriver2 JoystickDriver { get { return this; } } + #endregion @@ -385,6 +387,20 @@ namespace OpenTK.Platform.MacOS #endregion + #region IJoystickDriver2 Members + + JoystickState IJoystickDriver2.GetState(int index) + { + throw new NotImplementedException(); + } + + JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) + { + throw new NotImplementedException(); + } + + #endregion + #region NativeMethods class NativeMethods diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs index ad540311..e7dd5f05 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/SDL2/Sdl2Factory.cs b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs index 4c39156f..b8015f6b 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs @@ -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 3a69be40..023af9a2 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -220,6 +220,14 @@ namespace OpenTK.Platform.SDL2 } } + public IJoystickDriver2 JoystickDriver + { + get + { + return joystick_driver; + } + } + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 6c7e2728..701c38d6 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -32,7 +32,7 @@ using OpenTK.Input; namespace OpenTK.Platform.SDL2 { - class Sdl2JoystickDriver : IJoystickDriver, IGamePadDriver, IDisposable + class Sdl2JoystickDriver : IJoystickDriver, IJoystickDriver2, IGamePadDriver, IDisposable { const float RangeMultiplier = 1.0f / 32768.0f; @@ -322,6 +322,20 @@ namespace OpenTK.Platform.SDL2 #endregion + #region IJoystickDriver2 Members + + JoystickState IJoystickDriver2.GetState(int index) + { + throw new NotImplementedException(); + } + + JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) + { + throw new NotImplementedException(); + } + + #endregion + #region IDisposable Members void Dispose(bool manual) 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 07c208cc..e7c71541 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -445,10 +445,10 @@ namespace OpenTK.Platform.Windows JoystickError result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes); if (result == JoystickError.NoError) { - gpcaps.AxisCount = caps.NumAxes; - gpcaps.ButtonCount = caps.NumButtons; - if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0) - gpcaps.DPadCount++; + //gpcaps.AxisCount = caps.NumAxes; + //gpcaps.ButtonCount = caps.NumButtons; + //if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0) + // gpcaps.DPadCount++; } } else diff --git a/Source/OpenTK/Platform/Windows/WinRawInput.cs b/Source/OpenTK/Platform/Windows/WinRawInput.cs index c905b65b..04025efd 100644 --- a/Source/OpenTK/Platform/Windows/WinRawInput.cs +++ b/Source/OpenTK/Platform/Windows/WinRawInput.cs @@ -190,6 +190,11 @@ namespace OpenTK.Platform.Windows get { return joystick_driver; } } + public override IJoystickDriver2 JoystickDriver + { + get { throw new NotImplementedException(); } + } + #endregion } } diff --git a/Source/OpenTK/Platform/Windows/XInputJoystick.cs b/Source/OpenTK/Platform/Windows/XInputJoystick.cs index f3e0c04e..c6d7def1 100644 --- a/Source/OpenTK/Platform/Windows/XInputJoystick.cs +++ b/Source/OpenTK/Platform/Windows/XInputJoystick.cs @@ -1,4 +1,4 @@ -// #region License +#region License // // XInputJoystick.cs // @@ -25,7 +25,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// #endregion +#endregion using System; using System.Collections.Generic; 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 b4948169..36fac9f2 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 @@ -277,5 +277,19 @@ namespace OpenTK.Platform.X11 } #endregion + + #region IJoystickDriver2 Members + + JoystickState IJoystickDriver2.GetState(int index) + { + throw new NotImplementedException(); + } + + JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) + { + throw new NotImplementedException(); + } + + #endregion } } From 25a0e552f8f8c10a2e94427eac499aa5cf19552b Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 12:47:09 +0100 Subject: [PATCH 57/69] Implemented thumbsticks and trigger caps --- .../Input/{GamePadAxis.cs => GamePadAxes.cs} | 13 +- Source/OpenTK/Input/GamePadCapabilities.cs | 23 +- Source/OpenTK/Input/GamePadState.cs | 23 +- Source/OpenTK/Input/GamePadType.cs | 2 + .../Platform/SDL2/Sdl2JoystickDriver.cs | 2 +- .../OpenTK/Platform/Windows/WinMMJoystick.cs | 8 +- .../OpenTK/Platform/Windows/XInputJoystick.cs | 267 ++++++++++++------ 7 files changed, 230 insertions(+), 108 deletions(-) rename Source/OpenTK/Input/{GamePadAxis.cs => GamePadAxes.cs} (85%) diff --git a/Source/OpenTK/Input/GamePadAxis.cs b/Source/OpenTK/Input/GamePadAxes.cs similarity index 85% rename from Source/OpenTK/Input/GamePadAxis.cs rename to Source/OpenTK/Input/GamePadAxes.cs index f619ab6a..3ddea306 100644 --- a/Source/OpenTK/Input/GamePadAxis.cs +++ b/Source/OpenTK/Input/GamePadAxes.cs @@ -27,12 +27,15 @@ using System; namespace OpenTK.Input { - internal enum GamePadAxis + [Flags] + internal enum GamePadAxes : byte { - LeftX, - LeftY, - RightX, - RightY + LeftX = 1 << 0, + LeftY = 1 << 1, + LeftTrigger = 1 << 2, + RightX = 1 << 3, + RightY = 1 << 4, + RightTrigger = 1 << 5, } } diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs index c637f771..2db7b612 100644 --- a/Source/OpenTK/Input/GamePadCapabilities.cs +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -35,15 +35,17 @@ namespace OpenTK.Input public struct GamePadCapabilities : IEquatable { Buttons buttons; + GamePadAxes axes; byte gamepad_type; bool is_connected; #region Constructors - public GamePadCapabilities(GamePadType type, Buttons buttons, bool is_connected) + 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; } @@ -134,32 +136,32 @@ namespace OpenTK.Input public bool HasLeftXThumbStick { - get { return false; } + get { return (axes & GamePadAxes.LeftX) != 0; } } public bool HasLeftYThumbStick { - get { return false; } + get { return (axes & GamePadAxes.LeftY) != 0; } } public bool HasRightXThumbStick { - get { return false; } + get { return (axes & GamePadAxes.RightX) != 0; } } public bool HasRightYThumbStick { - get { return false; } + get { return (axes & GamePadAxes.RightY) != 0; } } public bool HasLeftTrigger { - get { return false; } + get { return (axes & GamePadAxes.LeftTrigger) != 0; } } public bool HasRightTrigger { - get { return false; } + get { return (axes & GamePadAxes.RightTrigger) != 0; } } public bool HasLeftVibrationMotor @@ -195,8 +197,11 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{Type: {0}; Buttons: {1}; Connected: {2}}}", - GamePadType, Convert.ToString((int)buttons, 2), IsConnected); + "{{Type: {0}; Axes: {1}; Buttons: {2}; Connected: {3}}}", + GamePadType, + Convert.ToString((int)axes, 2), + Convert.ToString((int)buttons, 2), + IsConnected); } public override int GetHashCode() diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index 22111b53..bd196982 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -62,6 +62,11 @@ namespace OpenTK.Input get { return new GamePadDPad(buttons); } } + public GamePadTriggers Triggers + { + get { return new GamePadTriggers(left_trigger, right_trigger); } + } + public bool IsConnected { get { return is_connected; } @@ -104,23 +109,23 @@ namespace OpenTK.Input #region Internal Members - internal void SetAxis(GamePadAxis axis, short value) + internal void SetAxis(GamePadAxes axis, short value) { switch (axis) { - case GamePadAxis.LeftX: + case GamePadAxes.LeftX: left_stick_x = value; break; - case GamePadAxis.LeftY: + case GamePadAxes.LeftY: left_stick_y = value; break; - case GamePadAxis.RightX: + case GamePadAxes.RightX: right_stick_x = value; break; - case GamePadAxis.RightY: + case GamePadAxes.RightY: right_stick_y = value; break; @@ -146,11 +151,17 @@ namespace OpenTK.Input is_connected = connected; } + internal void SetTriggers(byte left, byte right) + { + left_trigger = left; + right_trigger = right; + } + #endregion #region Private Members - bool IsAxisValid(GamePadAxis axis) + bool IsAxisValid(GamePadAxes axis) { int index = (int)axis; return index >= 0 && index < GamePad.MaxAxisCount; diff --git a/Source/OpenTK/Input/GamePadType.cs b/Source/OpenTK/Input/GamePadType.cs index 1524e024..37311627 100644 --- a/Source/OpenTK/Input/GamePadType.cs +++ b/Source/OpenTK/Input/GamePadType.cs @@ -41,5 +41,7 @@ namespace OpenTK.Input BigButtonPad, DrumKit, GamePad, + ArcadePad, + BassGuitar, } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 701c38d6..d5caa2c1 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -258,7 +258,7 @@ namespace OpenTK.Platform.SDL2 int id = ev.Which; if (IsControllerValid(id)) { - controllers[id].State.SetAxis((GamePadAxis)ev.Axis, ev.Value); + controllers[id].State.SetAxis((GamePadAxes)ev.Axis, ev.Value); } else { diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index e7c71541..1821b136 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -470,10 +470,10 @@ namespace OpenTK.Platform.Windows info.Flags = JoystickFlags.All; UnsafeNativeMethods.joyGetPosEx(index, ref info); - state.SetAxis(GamePadAxis.LeftX, (short)info.XPos); - state.SetAxis(GamePadAxis.LeftY, (short)info.YPos); - state.SetAxis(GamePadAxis.RightX, (short)info.ZPos); - state.SetAxis(GamePadAxis.RightY, (short)info.RPos); + state.SetAxis(GamePadAxes.LeftX, (short)info.XPos); + state.SetAxis(GamePadAxes.LeftY, (short)info.YPos); + state.SetAxis(GamePadAxes.RightX, (short)info.ZPos); + state.SetAxis(GamePadAxes.RightY, (short)info.RPos); //state.SetAxis(GamePadAxis.RightX, (short)info.ZPos); //state.SetAxis(GamePadAxis.RightY, (short)info.RPos); diff --git a/Source/OpenTK/Platform/Windows/XInputJoystick.cs b/Source/OpenTK/Platform/Windows/XInputJoystick.cs index c6d7def1..23710df0 100644 --- a/Source/OpenTK/Platform/Windows/XInputJoystick.cs +++ b/Source/OpenTK/Platform/Windows/XInputJoystick.cs @@ -33,41 +33,56 @@ using System.Text; using OpenTK.Input; using System.Runtime.InteropServices; using System.Security; +using System.Diagnostics; namespace OpenTK.Platform.Windows { - class XInputJoystick : IGamePadDriver + class XInputJoystick : IGamePadDriver, IDisposable { + XInput xinput = new XInput(); + #region IGamePadDriver Members public GamePadState GetState(int index) { XInputState xstate; - XInput910.GetState((XInputUserIndex)index, out xstate); + XInputErrorCode error = xinput.GetState((XInputUserIndex)index, out xstate); GamePadState state = new GamePadState(); - state.SetButton(Buttons.A, (xstate.GamePad.Buttons & XInputButtons.A) != 0); - state.SetButton(Buttons.B, (xstate.GamePad.Buttons & XInputButtons.B) != 0); - state.SetButton(Buttons.X, (xstate.GamePad.Buttons & XInputButtons.X) != 0); - state.SetButton(Buttons.Y, (xstate.GamePad.Buttons & XInputButtons.Y) != 0); - state.SetButton(Buttons.Start, (xstate.GamePad.Buttons & XInputButtons.Start) != 0); - state.SetButton(Buttons.Back, (xstate.GamePad.Buttons & XInputButtons.Back) != 0); - //state.SetButton(Buttons.BigButton, (xstate.GamePad.Buttons & XInputButtons.???) != 0); - state.SetButton(Buttons.LeftShoulder, (xstate.GamePad.Buttons & XInputButtons.LeftShoulder) != 0); - state.SetButton(Buttons.RightShoulder, (xstate.GamePad.Buttons & XInputButtons.RightShoulder) != 0); - state.SetButton(Buttons.LeftStick, (xstate.GamePad.Buttons & XInputButtons.LeftThumb) != 0); - state.SetButton(Buttons.RightStick, (xstate.GamePad.Buttons & XInputButtons.RightThumb) != 0); - state.SetButton(Buttons.DPadDown, (xstate.GamePad.Buttons & XInputButtons.DPadDown) != 0); - state.SetButton(Buttons.DPadUp, (xstate.GamePad.Buttons & XInputButtons.DPadUp) != 0); - state.SetButton(Buttons.DPadLeft, (xstate.GamePad.Buttons & XInputButtons.DPadLeft) != 0); - state.SetButton(Buttons.DPadRight, (xstate.GamePad.Buttons & XInputButtons.DPadRight) != 0); + 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) { - throw new NotImplementedException(); + 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) @@ -78,6 +93,58 @@ namespace OpenTK.Platform.Windows #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 { @@ -85,12 +152,12 @@ namespace OpenTK.Platform.Windows DeviceNotConnected } - enum XInputType + enum XInputDeviceType : byte { GamePad } - enum XInputSubType + enum XInputDeviceSubType : byte { Unknown = 0, GamePad = 1, @@ -107,10 +174,10 @@ namespace OpenTK.Platform.Windows enum XInputCapabilities { - Ffb = 0x0001, + ForceFeedback = 0x0001, Wireless = 0x0002, Voice = 0x0004, - Pmd = 0x0008, + PluginModules = 0x0008, NoNavigation = 0x0010, } @@ -197,8 +264,8 @@ namespace OpenTK.Platform.Windows struct XInputDeviceCapabilities { - public byte Type; - public byte SubType; + public XInputDeviceType Type; + public XInputDeviceSubType SubType; public short Flags; public XInputGamePad GamePad; public XInputVibration Vibration; @@ -210,90 +277,124 @@ namespace OpenTK.Platform.Windows public XInputBatteryLevel Level; } - static class XInput910 + class XInput : IDisposable { - const string dll = "XINPUT9_1_0"; + 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] - [DllImport(dll, EntryPoint = "XInputGetCapabilities", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode GetCapabilities( + internal delegate XInputErrorCode XInputGetCapabilities( XInputUserIndex dwUserIndex, XInputCapabilitiesFlags dwFlags, - ref XInputDeviceCapabilities pCapabilities); + out XInputDeviceCapabilities pCapabilities); [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputGetState", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode GetState + internal delegate XInputErrorCode XInputGetState ( XInputUserIndex dwUserIndex, out XInputState pState ); [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputSetState", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode SetState + 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 } - static class XInput13 + #endregion + + #region IDisposable Members + + public void Dispose() { - const string dll = "XINPUT1_3"; - - [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputGetCapabilities", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode GetCapabilities( - XInputUserIndex dwUserIndex, - XInputCapabilitiesFlags dwFlags, - ref XInputDeviceCapabilities pCapabilities); - - [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputGetState", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode GetState - ( - XInputUserIndex dwUserIndex, - out XInputState pState - ); - - [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputSetState", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode SetState - ( - XInputUserIndex dwUserIndex, - ref XInputVibration pVibration - ); + Dispose(true); + GC.SuppressFinalize(this); } - static class XInput14 + void Dispose(bool manual) { - const string dll = "XINPUT1_4"; - - [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputGetCapabilities", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode GetCapabilities( - XInputUserIndex dwUserIndex, - XInputCapabilitiesFlags dwFlags, - ref XInputDeviceCapabilities pCapabilities); - - [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputGetState", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode GetState - ( - XInputUserIndex dwUserIndex, - out XInputState pState - ); - - [SuppressUnmanagedCodeSecurity] - [DllImport(dll, EntryPoint = "XInputSetState", ExactSpelling = true, SetLastError = false)] - public static extern XInputErrorCode SetState - ( - XInputUserIndex dwUserIndex, - ref XInputVibration pVibration - ); + 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 } } From dfd65540614149acc0d0ee52e0e22915a806174f Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 12:48:15 +0100 Subject: [PATCH 58/69] Added internal GamePadMapping class --- Source/OpenTK/Input/GamePadMapping.cs | 39 +++++++++++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 3 ++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Source/OpenTK/Input/GamePadMapping.cs diff --git a/Source/OpenTK/Input/GamePadMapping.cs b/Source/OpenTK/Input/GamePadMapping.cs new file mode 100644 index 00000000..9ddca29f --- /dev/null +++ b/Source/OpenTK/Input/GamePadMapping.cs @@ -0,0 +1,39 @@ +#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.Text; + +namespace OpenTK.Input +{ + class GamePadMapping + { + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 01e5c793..be7ac936 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -133,6 +133,7 @@ + @@ -748,7 +749,7 @@ - + From 58b67d31e3e6243f767c47952a1ae9739912b06e Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 12:52:57 +0100 Subject: [PATCH 59/69] No reason to comment out #region License --- Source/OpenTK/Input/GamePadCapabilities.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs index 2db7b612..7fbe8055 100644 --- a/Source/OpenTK/Input/GamePadCapabilities.cs +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -1,4 +1,4 @@ -// #region License +#region License // // GamePadCapabilities.cs // @@ -25,7 +25,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// #endregion +#endregion using System; From 5c73a3ea74f72cc618192fd73cbc890204e1321c Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 14:21:37 +0100 Subject: [PATCH 60/69] Implements JoystickState and Capabilities setters --- Source/OpenTK/Input/JoystickCapabilities.cs | 34 ++++++++--- Source/OpenTK/Input/JoystickState.cs | 67 +++++++++++++++++++-- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/Source/OpenTK/Input/JoystickCapabilities.cs b/Source/OpenTK/Input/JoystickCapabilities.cs index 5265d209..b25fa69b 100644 --- a/Source/OpenTK/Input/JoystickCapabilities.cs +++ b/Source/OpenTK/Input/JoystickCapabilities.cs @@ -38,20 +38,35 @@ namespace OpenTK.Input byte axis_count; byte button_count; byte dpad_count; - byte trackball_count; + bool is_connected; + + #region Constructors + + public 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 public int AxisCount { get { return axis_count; } - set { axis_count = (byte)value; } } public int ButtonCount { get { return button_count; } - set { button_count = (byte)value; } } #endregion @@ -61,15 +76,14 @@ namespace OpenTK.Input int DPadCount { get { return dpad_count; } - set { dpad_count = (byte)value; } - } - - int TrackballCount - { - get { return trackball_count; } - set { trackball_count = (byte)value; } } #endregion + + public bool IsConnected + { + get { return is_connected; } + } + } } diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index 28775e69..8a233df6 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -36,10 +36,18 @@ namespace OpenTK.Input { public struct JoystickState { - const int MaxAxes = 10; // JoystickAxis defines 10 axes - const float ConversionFactor = 1.0f / short.MaxValue; + // 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 + 1); + unsafe fixed short axes[MaxAxes]; - JoystickButton buttons; + int buttons; + bool is_connected; + + #region Public Members public float GetAxis(JoystickAxis axis) { @@ -66,14 +74,63 @@ namespace OpenTK.Input return value; } + public ButtonState GetButton(JoystickButton button) + { + return (buttons & (1 << (int)button)) != 0 ? ButtonState.Pressed : ButtonState.Released; + } + public bool IsButtonDown(JoystickButton button) { - return (buttons & button) != 0; + return (buttons & (1 << (int)button)) != 0; } public bool IsButtonUp(JoystickButton button) { - return (buttons & button) == 0; + return (buttons & (1 << (int)button)) == 0; } + + public bool IsConnected + { + get { return is_connected; } + } + + #endregion + + #region Internal Members + + 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; + } + + #endregion } } From b9242c006b2e55fa293135d88fc00aef0056f9b0 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 14:22:03 +0100 Subject: [PATCH 61/69] Added MappedGamePadDriver skeleton implementation --- Source/OpenTK/OpenTK.csproj | 1 + Source/OpenTK/Platform/MappedGamePadDriver.cs | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 Source/OpenTK/Platform/MappedGamePadDriver.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index be7ac936..a378e483 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -164,6 +164,7 @@ + diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs new file mode 100644 index 00000000..346be196 --- /dev/null +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -0,0 +1,83 @@ +#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 + { + public GamePadState GetState(int index) + { + JoystickState joy = Joystick.GetState(index); + GamePadState pad = new GamePadState(); + if (joy.IsConnected) + { + GamePadMapping mapping = new GamePadMapping();//GamePadMapping.Lookup() + // Todo: implement mapping + } + return pad; + } + + public GamePadCapabilities GetCapabilities(int index) + { + JoystickCapabilities joy = Joystick.GetCapabilities(index); + GamePadCapabilities pad = new GamePadCapabilities(); + if (joy.IsConnected) + { + // Todo: implement mapping + } + return pad; + } + + public string GetName(int index) + { + throw new NotImplementedException(); + } + } +} From 062962aeb2b45c852550cd670d85193529dd4749 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 14:24:29 +0100 Subject: [PATCH 62/69] WinMMJoystick implements IJoystickDriver2 WinMM is optimized for general joystick use, not for the canonical GamePad layout. Instead of exposing IGamePadDriver directly, it should expose IJoystickDriver2 and use a mapping driver to get GamePad support. --- .../OpenTK/Platform/Windows/WinMMJoystick.cs | 131 ++++++++---------- 1 file changed, 59 insertions(+), 72 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index 1821b136..882ce5f0 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 @@ -242,6 +242,64 @@ 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 + { + 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; + UnsafeNativeMethods.joyGetPosEx(index, ref info); + + state.SetAxis(JoystickAxis.Axis0, (short)info.XPos); + state.SetAxis(JoystickAxis.Axis1, (short)info.YPos); + state.SetAxis(JoystickAxis.Axis2, (short)info.ZPos); + state.SetAxis(JoystickAxis.Axis3, (short)info.RPos); + state.SetAxis(JoystickAxis.Axis4, (short)info.ZPos); + state.SetAxis(JoystickAxis.Axis5, (short)info.RPos); + + for (int i = 0; i < 16; i++) + { + state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0); + } + } + else + { + Debug.Print("[Win] Invalid WinMM joystick device {0}", index); + } + + return state; + } + + #endregion + #region IDisposable public void Dispose() @@ -432,76 +490,5 @@ namespace OpenTK.Platform.Windows } #endregion - - #region IGamePadDriver Members - - public GamePadCapabilities GetCapabilities(int index) - { - GamePadCapabilities gpcaps = new GamePadCapabilities(); - - if (IsValid(index)) - { - JoyCaps caps; - JoystickError result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes); - if (result == JoystickError.NoError) - { - //gpcaps.AxisCount = caps.NumAxes; - //gpcaps.ButtonCount = caps.NumButtons; - //if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0) - // gpcaps.DPadCount++; - } - } - else - { - Debug.Print("[Win] Invalid WinMM joystick device {0}", index); - } - - return gpcaps; - } - - public GamePadState GetState(int index) - { - GamePadState state = new GamePadState(); - - if (IsValid(index)) - { - JoyInfoEx info = new JoyInfoEx(); - info.Size = JoyInfoEx.SizeInBytes; - info.Flags = JoystickFlags.All; - UnsafeNativeMethods.joyGetPosEx(index, ref info); - - state.SetAxis(GamePadAxes.LeftX, (short)info.XPos); - state.SetAxis(GamePadAxes.LeftY, (short)info.YPos); - state.SetAxis(GamePadAxes.RightX, (short)info.ZPos); - state.SetAxis(GamePadAxes.RightY, (short)info.RPos); - //state.SetAxis(GamePadAxis.RightX, (short)info.ZPos); - //state.SetAxis(GamePadAxis.RightY, (short)info.RPos); - - state.SetButton(Buttons.A, (info.Buttons & 1 << 0) != 0); - state.SetButton(Buttons.B, (info.Buttons & 1 << 1) != 0); - state.SetButton(Buttons.X, (info.Buttons & 1 << 2) != 0); - state.SetButton(Buttons.Y, (info.Buttons & 1 << 3) != 0); - state.SetButton(Buttons.LeftShoulder, (info.Buttons & 1 << 4) != 0); - state.SetButton(Buttons.RightShoulder, (info.Buttons & 1 << 5) != 0); - state.SetButton(Buttons.Back, (info.Buttons & 1 << 6) != 0); - state.SetButton(Buttons.Start, (info.Buttons & 1 << 7) != 0); - state.SetButton(Buttons.LeftStick, (info.Buttons & 1 << 8) != 0); - state.SetButton(Buttons.RightStick, (info.Buttons & 1 << 9) != 0); - state.SetButton(Buttons.BigButton, (info.Buttons & 1 << 10) != 0); - } - else - { - Debug.Print("[Win] Invalid WinMM joystick device {0}", index); - } - - return state; - } - - public string GetName(int index) - { - throw new NotImplementedException(); - } - - #endregion } } From 42e6a96a4340f0c7ac27fff7cfd23d65cc849587 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 14:24:42 +0100 Subject: [PATCH 63/69] Connected XInput driver --- Source/OpenTK/Platform/Windows/WinRawInput.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinRawInput.cs b/Source/OpenTK/Platform/Windows/WinRawInput.cs index 04025efd..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); } @@ -187,12 +197,12 @@ namespace OpenTK.Platform.Windows public override IGamePadDriver GamePadDriver { - get { return joystick_driver; } + get { return gamepad_driver; } } public override IJoystickDriver2 JoystickDriver { - get { throw new NotImplementedException(); } + get { return joystick_driver; } } #endregion From 7bab950cc025c5a7afbeae2bbc3c99312c76b15f Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 15:01:10 +0100 Subject: [PATCH 64/69] Implemented structural equality --- Source/OpenTK/Input/JoystickCapabilities.cs | 39 ++++++++++- Source/OpenTK/Input/JoystickState.cs | 73 ++++++++++++++++++--- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Input/JoystickCapabilities.cs b/Source/OpenTK/Input/JoystickCapabilities.cs index b25fa69b..80036211 100644 --- a/Source/OpenTK/Input/JoystickCapabilities.cs +++ b/Source/OpenTK/Input/JoystickCapabilities.cs @@ -33,7 +33,7 @@ using System.Text; namespace OpenTK.Input { - public struct JoystickCapabilities + public struct JoystickCapabilities : IEquatable { byte axis_count; byte button_count; @@ -69,6 +69,33 @@ namespace OpenTK.Input get { return button_count; } } + public bool IsConnected + { + get { return is_connected; } + } + + public override string ToString() + { + return String.Format( + "{{Axes: {0}; Buttons: {1}; IsConnected: {2}}}", + AxisCount, ButtonCount, IsConnected); + } + + public override int GetHashCode() + { + return + AxisCount.GetHashCode() ^ + ButtonCount.GetHashCode() ^ + IsConnected.GetHashCode(); + } + + public override bool Equals(object obj) + { + return + obj is JoystickCapabilities && + Equals((JoystickCapabilities)obj); + } + #endregion #region Private Members @@ -80,10 +107,16 @@ namespace OpenTK.Input #endregion - public bool IsConnected + #region IEquatable Members + + public bool Equals(JoystickCapabilities other) { - get { return is_connected; } + return + AxisCount == other.AxisCount && + ButtonCount == other.ButtonCount && + IsConnected == other.IsConnected; } + #endregion } } diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index 8a233df6..1b2a1299 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -34,7 +34,7 @@ using System.Text; namespace OpenTK.Input { - public struct JoystickState + public struct JoystickState : IEquatable { // If we ever add more values to JoystickAxis or JoystickButton // then we'll need to increase these limits. @@ -59,13 +59,7 @@ namespace OpenTK.Input float value = 0.0f; if (axis >= 0 && axis < MaxAxes) { - unsafe - { - fixed (short* paxis = axes) - { - value = *(paxis + axis) * ConversionFactor; - } - } + value = GetAxisUnsafe(axis) * ConversionFactor; } else { @@ -94,6 +88,38 @@ namespace OpenTK.Input get { return is_connected; } } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < MaxAxes; i++) + { + sb.Append(" "); + sb.Append(GetAxis(i)); + } + return String.Format( + "{{Axes:{0}; Buttons: {1}; IsConnected: {2}}}", + sb.ToString(), + Convert.ToString((int)buttons, 2), + IsConnected); + } + + public override int GetHashCode() + { + int hash = buttons.GetHashCode() ^ IsConnected.GetHashCode(); + for (int i = 0; i < MaxAxes; i++) + { + hash ^= GetAxisUnsafe(i).GetHashCode(); + } + return hash; + } + + public override bool Equals(object obj) + { + return + obj is JoystickState && + Equals((JoystickState)obj); + } + #endregion #region Internal Members @@ -132,5 +158,36 @@ namespace OpenTK.Input } #endregion + + #region Private Members + + short GetAxisUnsafe(int index) + { + unsafe + { + fixed (short* paxis = axes) + { + return *(paxis + index); + } + } + } + + #endregion + + #region IEquatable Members + + 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 } } From 97b8710339fb4c48f74e5caa0fee0a410e4cfa5e Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 15:01:33 +0100 Subject: [PATCH 65/69] Added state output for OpenTK.Input.Joystick --- .../Examples/OpenTK/Test/GameWindowStates.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 8359a552..aa9d27d2 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -228,14 +228,31 @@ namespace Examples.Tests 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); - DrawString(gfx, caps.ToString(), line++); - DrawString(gfx, state.ToString(), line++); + 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; } From 3c1404f0ac04faaae0ed64d220be1ea783478b9a Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 15:51:25 +0100 Subject: [PATCH 66/69] Improved WinMMJoystickDriver hotplugging behavior --- .../OpenTK/Platform/Windows/WinMMJoystick.cs | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index 882ce5f0..9f84c09e 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -133,6 +133,16 @@ 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(); @@ -258,6 +268,10 @@ namespace OpenTK.Platform.Windows // gpcaps.DPadCount++; return caps; } + else if (result == JoystickError.Unplugged) + { + UnplugJoystick(index); + } } else { @@ -276,18 +290,32 @@ namespace OpenTK.Platform.Windows JoyInfoEx info = new JoyInfoEx(); info.Size = JoyInfoEx.SizeInBytes; info.Flags = JoystickFlags.All; - UnsafeNativeMethods.joyGetPosEx(index, ref info); - state.SetAxis(JoystickAxis.Axis0, (short)info.XPos); - state.SetAxis(JoystickAxis.Axis1, (short)info.YPos); - state.SetAxis(JoystickAxis.Axis2, (short)info.ZPos); - state.SetAxis(JoystickAxis.Axis3, (short)info.RPos); - state.SetAxis(JoystickAxis.Axis4, (short)info.ZPos); - state.SetAxis(JoystickAxis.Axis5, (short)info.RPos); - - for (int i = 0; i < 16; i++) + JoystickError result = UnsafeNativeMethods.joyGetPosEx(index, ref info); + if (result == JoystickError.NoError) { - state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0); + state.SetAxis(JoystickAxis.Axis0, (short)info.XPos); + state.SetAxis(JoystickAxis.Axis1, (short)info.YPos); + state.SetAxis(JoystickAxis.Axis2, (short)info.ZPos); + state.SetAxis(JoystickAxis.Axis3, (short)info.RPos); + state.SetAxis(JoystickAxis.Axis4, (short)info.ZPos); + state.SetAxis(JoystickAxis.Axis5, (short)info.RPos); + + for (int i = 0; i < 16; i++) + { + state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0); + } + + state.SetIsConnected(true); + } + else if (result == JoystickError.Unplugged) + { + UnplugJoystick(index); + } + else + { + // Joystick not existent or other error. No need to spam the log + //Debug.Print("[Win] WinMM joyGetPosEx failed. Error: {0}", result); } } else @@ -441,9 +469,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 From 3c6298a1e6102cd6a54418c8cfc48a135b0721ce Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 16:42:48 +0100 Subject: [PATCH 67/69] Fixed WinMM offsets for IJoystickDevice2 --- .../OpenTK/Platform/Windows/WinMMJoystick.cs | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index 9f84c09e..fa84aba1 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -148,6 +148,12 @@ namespace OpenTK.Platform.Windows 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 @@ -294,29 +300,30 @@ namespace OpenTK.Platform.Windows JoystickError result = UnsafeNativeMethods.joyGetPosEx(index, ref info); if (result == JoystickError.NoError) { - state.SetAxis(JoystickAxis.Axis0, (short)info.XPos); - state.SetAxis(JoystickAxis.Axis1, (short)info.YPos); - state.SetAxis(JoystickAxis.Axis2, (short)info.ZPos); - state.SetAxis(JoystickAxis.Axis3, (short)info.RPos); - state.SetAxis(JoystickAxis.Axis4, (short)info.ZPos); - state.SetAxis(JoystickAxis.Axis5, (short)info.RPos); - - for (int i = 0; i < 16; i++) + JoyCaps caps; + result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes); + if (result == JoystickError.NoError) { - state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0); - } + 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)); - state.SetIsConnected(true); + for (int i = 0; i < 16; i++) + { + state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0); + } + + state.SetIsConnected(true); + } } - else if (result == JoystickError.Unplugged) + + if (result == JoystickError.Unplugged) { UnplugJoystick(index); } - else - { - // Joystick not existent or other error. No need to spam the log - //Debug.Print("[Win] WinMM joyGetPosEx failed. Error: {0}", result); - } } else { From 0a71bbe065b0dd7cf1e71cc25f7dd19dc9881b4d Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 24 Dec 2013 17:06:39 +0100 Subject: [PATCH 68/69] Improved ToString implementation --- Source/OpenTK/Input/GamePadButtons.cs | 14 +------------- Source/OpenTK/Input/GamePadThumbSticks.cs | 4 ++-- Source/OpenTK/Input/GamePadTriggers.cs | 2 +- Source/OpenTK/Input/Joystick.cs | 5 +++++ Source/OpenTK/Input/JoystickState.cs | 6 +++--- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs index aeb97b59..c13a9f0c 100644 --- a/Source/OpenTK/Input/GamePadButtons.cs +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -109,19 +109,7 @@ namespace OpenTK.Input public override string ToString() { - return String.Format( - "{{{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}}}", - A == ButtonState.Pressed ? "A" : String.Empty, - B == ButtonState.Pressed ? "B" : String.Empty, - X == ButtonState.Pressed ? "X" : String.Empty, - Y == ButtonState.Pressed ? "Y" : String.Empty, - LeftShoulder == ButtonState.Pressed ? "L" : String.Empty, - RightShoulder == ButtonState.Pressed ? "R" : String.Empty, - Back == ButtonState.Pressed ? " Back" : String.Empty, - Start == ButtonState.Pressed ? " Start" : String.Empty, - BigButton == ButtonState.Pressed ? " Big" : String.Empty, - LeftStick == ButtonState.Pressed ? " LStick" : String.Empty, - RightStick == ButtonState.Pressed ? " RStick" : String.Empty); + return Convert.ToString((int)buttons, 2).PadLeft(10, '0'); } public override int GetHashCode() diff --git a/Source/OpenTK/Input/GamePadThumbSticks.cs b/Source/OpenTK/Input/GamePadThumbSticks.cs index 59eb4ee3..63838c6f 100644 --- a/Source/OpenTK/Input/GamePadThumbSticks.cs +++ b/Source/OpenTK/Input/GamePadThumbSticks.cs @@ -73,8 +73,8 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{Left: {0}; Right: {1}}}", - Left, Right); + "{{Left: ({0:f4}; {1:f4}); Right: ({2:f4}; {3:f4})}}", + Left.X, Left.Y, Right.X, Right.Y); } public override int GetHashCode() diff --git a/Source/OpenTK/Input/GamePadTriggers.cs b/Source/OpenTK/Input/GamePadTriggers.cs index ecff49c7..0e505d81 100644 --- a/Source/OpenTK/Input/GamePadTriggers.cs +++ b/Source/OpenTK/Input/GamePadTriggers.cs @@ -69,7 +69,7 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{Left: {0}; Right: {1}}}", + "{{Left: {0:f2}; Right: {1:f2}}}", Left, Right); } diff --git a/Source/OpenTK/Input/Joystick.cs b/Source/OpenTK/Input/Joystick.cs index 0458abed..378b11f4 100644 --- a/Source/OpenTK/Input/Joystick.cs +++ b/Source/OpenTK/Input/Joystick.cs @@ -47,5 +47,10 @@ namespace OpenTK.Input { return implementation.GetState(index); } + + //public string GetName(int index) + //{ + // return implementation.GetName(index); + //} } } diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index 1b2a1299..e14ac2d2 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -41,7 +41,7 @@ namespace OpenTK.Input internal const int MaxAxes = 10; internal const int MaxButtons = 32; - const float ConversionFactor = 1.0f / (short.MaxValue + 1); + const float ConversionFactor = 1.0f / (short.MaxValue + 0.5f); unsafe fixed short axes[MaxAxes]; int buttons; @@ -94,12 +94,12 @@ namespace OpenTK.Input for (int i = 0; i < MaxAxes; i++) { sb.Append(" "); - sb.Append(GetAxis(i)); + sb.Append(String.Format("{0:f4}", GetAxis(i))); } return String.Format( "{{Axes:{0}; Buttons: {1}; IsConnected: {2}}}", sb.ToString(), - Convert.ToString((int)buttons, 2), + Convert.ToString((int)buttons, 2).PadLeft(16, '0'), IsConnected); } From bd1fb1883629c370ee3c4de70020bda858423aec Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 24 Dec 2013 17:16:16 +0100 Subject: [PATCH 69/69] Use IGamePadDriver through MappedGamePadDriver --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 30 +++++------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 613d1c64..0967afef 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, IJoystickDriver2 + 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; @@ -291,10 +293,9 @@ namespace OpenTK.Platform.MacOS public IMouseDriver2 MouseDriver { get { return this; } } public IKeyboardDriver2 KeyboardDriver { get { return this; } } - public IGamePadDriver GamePadDriver { get { return this; } } + public IGamePadDriver GamePadDriver { get { return mapped_gamepad; } } public IJoystickDriver2 JoystickDriver { get { return this; } } - #endregion #region IMouseDriver2 Members @@ -368,35 +369,16 @@ namespace OpenTK.Platform.MacOS #endregion - #region IGamePadDriver Members - - public GamePadState GetState(int index) - { - return new GamePadState(); - } - - public GamePadCapabilities GetCapabilities(int index) - { - return new GamePadCapabilities(); - } - - public string GetName(int index) - { - throw new NotImplementedException(); - } - - #endregion - #region IJoystickDriver2 Members JoystickState IJoystickDriver2.GetState(int index) { - throw new NotImplementedException(); + return new JoystickState(); } JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) { - throw new NotImplementedException(); + return new JoystickCapabilities(); } #endregion