From 427b3641a01cb57256a6a0e71159e27b29b52517 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 18 Dec 2013 15:50:59 +0100 Subject: [PATCH 001/245] 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 002/245] 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 003/245] 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 004/245] 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 005/245] 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 006/245] 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 4aa2eae2e56e212b2bf543040e0dfd0b92d0479b Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sat, 21 Dec 2013 22:41:10 +0100 Subject: [PATCH 007/245] 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 a7ae4bb038ff3a2d95f75114a658ed549ef699ec Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sat, 21 Dec 2013 22:41:35 +0100 Subject: [PATCH 008/245] 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 1723be8a8b52532174085214145e73e5399f76aa Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sat, 21 Dec 2013 22:43:35 +0100 Subject: [PATCH 009/245] 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 956bbe6491b3d3d51fbbe1a146673bc4f354cf24 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sat, 21 Dec 2013 23:35:55 +0100 Subject: [PATCH 010/245] 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 a2744719d5191218d44aa10c6d8dec30a210d422 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 09:20:40 +0100 Subject: [PATCH 011/245] 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 0a46e20029ee70d75d0008c1709fd52d3fcf56d5 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 10:35:05 +0100 Subject: [PATCH 012/245] 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 30cd9cb7f84afd2ccc3a437b2de299788ba15ec0 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 11:10:05 +0100 Subject: [PATCH 013/245] 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 234c15e9c9d0352357ec612c7fcff9401a4d446b Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 17 Dec 2013 16:38:56 +0100 Subject: [PATCH 014/245] 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 015/245] 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 016/245] 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 017/245] 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 018/245] 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 019/245] 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 020/245] 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 021/245] 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 022/245] 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 023/245] 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 024/245] 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 025/245] 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 026/245] 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 027/245] 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 028/245] 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 029/245] 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 030/245] 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 031/245] 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 032/245] 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 033/245] 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 034/245] 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 035/245] 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 036/245] 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 037/245] 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 2d7734c915dffbf34fc4efddb9977c3a7fdd698e Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 22 Dec 2013 15:40:01 +0100 Subject: [PATCH 038/245] 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 7692243cd2250c9ed374a918144b3c4d04f5ebb4 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 15:40:01 +0100 Subject: [PATCH 039/245] 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 040/245] 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 041/245] 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 042/245] 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 043/245] 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 044/245] 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 045/245] 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 046/245] 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 047/245] 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 048/245] 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 049/245] 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 050/245] 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 051/245] 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 052/245] 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 053/245] 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 054/245] 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 055/245] 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 056/245] 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 057/245] 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 c6a21a2239d3b6d7bd637e9bc8f3444701d09c86 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 19:32:16 +0100 Subject: [PATCH 058/245] Implemented KeyDown & KeyUp events on OpenTK/Carbon --- .../OpenTK/Platform/MacOS/CarbonGLNative.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index b285473b..0a8324f0 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -74,6 +74,8 @@ namespace OpenTK.Platform.MacOS new Dictionary(new IntPtrEqualityComparer()); KeyPressEventArgs mKeyPressArgs = new KeyPressEventArgs((char)0); + OpenTK.Input.KeyboardKeyEventArgs mKeyDownArgs = new OpenTK.Input.KeyboardKeyEventArgs(); + OpenTK.Input.KeyboardKeyEventArgs mKeyUpArgs = new OpenTK.Input.KeyboardKeyEventArgs(); bool mMouseIn = false; bool mIsActive = false; @@ -377,12 +379,25 @@ namespace OpenTK.Platform.MacOS { case KeyboardEventKind.RawKeyRepeat: if (InputDriver.Keyboard[0].KeyRepeat) - goto case KeyboardEventKind.RawKeyDown; + { + // Repeat KeyPress events until KeyUp + if (!Char.IsControl(mKeyPressArgs.KeyChar)) + { + OnKeyPress(mKeyPressArgs); + } + } break; case KeyboardEventKind.RawKeyDown: Keymap.TryGetValue(code, out key); + // Legacy keyboard API InputDriver.Keyboard[0].SetKey(key, (uint)code, true); + + // Raise KeyDown for new keyboard API + mKeyDownArgs.Key = key; + KeyDown(this, mKeyDownArgs); + + // Raise KeyPress for new keyboard API if (!Char.IsControl(mKeyPressArgs.KeyChar)) { OnKeyPress(mKeyPressArgs); @@ -391,7 +406,12 @@ namespace OpenTK.Platform.MacOS case KeyboardEventKind.RawKeyUp: Keymap.TryGetValue(code, out key); + // Legacy keyboard API InputDriver.Keyboard[0].SetKey(key, (uint)code, false); + + // Raise KeyUp for new keyboard API + mKeyUpArgs.Key = key; + KeyUp(this, mKeyUpArgs); return OSStatus.NoError; case KeyboardEventKind.RawKeyModifiersChanged: From c13d80d6d8b7f31f61281a70b4f8b56b09d7b1b7 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 20:21:02 +0100 Subject: [PATCH 059/245] Fixed GameWindow.{Bounds, Location, Size, X, Y} setters on OpenTK/Carbon --- .../MacOS/CarbonBindings/CarbonAPI.cs | 8 +++ .../OpenTK/Platform/MacOS/CarbonGLNative.cs | 61 +++++++++---------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs index 83c41a43..407563f4 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs @@ -64,6 +64,11 @@ namespace OpenTK.Platform.MacOS.Carbon short bottom; short right; + internal Rect(int left, int top, int width, int height) + : this((short)left, (short)top, (short)width, (short)height) + { + } + internal Rect(short _left, short _top, short _width, short _height) { top = _top; @@ -530,6 +535,9 @@ namespace OpenTK.Platform.MacOS.Carbon return retval; } + [DllImport(carbon)] + internal static extern OSStatus SetWindowBounds(IntPtr Windows, WindowRegionCode WindowRegionCode, ref Rect globalBounds); + //[DllImport(carbon)] //internal static extern void MoveWindow(IntPtr window, short hGlobal, short vGlobal, bool front); diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index 0a8324f0..a9cf8f81 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -660,52 +660,51 @@ namespace OpenTK.Platform.MacOS } - Rect GetRegion() + Rect GetClientSize() { Rect retval = API.GetWindowBounds(window.Handle, WindowRegionCode.ContentRegion); - return retval; } - void SetLocation(short x, short y) - { - if (windowState == WindowState.Fullscreen) - return; - - API.MoveWindow(window.Handle, x, y, false); - } - - void SetSize(short width, short height) - { - if (WindowState == WindowState.Fullscreen) - return; - - // The bounds of the window should be the size specified, but - // API.SizeWindow sets the content region size. So - // we reduce the size to get the correct bounds. - width -= (short)(bounds.Width - clientRectangle.Width); - height -= (short)(bounds.Height - clientRectangle.Height); - - API.SizeWindow(window.Handle, width, height, true); - } - void SetClientSize(short width, short height) { if (WindowState == WindowState.Fullscreen) return; - - API.SizeWindow(window.Handle, width, height, true); + + Rect new_bounds = new Rect(Bounds.X, Bounds.Y, width, height); + API.SetWindowBounds(window.Handle, WindowRegionCode.ContentRegion, ref new_bounds); + LoadSize(); + } + + void SetLocation(short x, short y) + { + if (windowState == WindowState.Fullscreen) + return; + + Rect new_bounds = new Rect(x, y, Bounds.Width, Bounds.Height); + API.SetWindowBounds(window.Handle, WindowRegionCode.StructureRegion, ref new_bounds); + LoadSize(); + } + + void SetSize(short width, short height) + { + if (WindowState == WindowState.Fullscreen) + return; + + Rect new_bounds = new Rect(Bounds.X, Bounds.Y, width, height); + API.SetWindowBounds(window.Handle, WindowRegionCode.StructureRegion, ref new_bounds); + LoadSize(); } private void LoadSize() { if (WindowState == WindowState.Fullscreen) return; - + Rect r = API.GetWindowBounds(window.Handle, WindowRegionCode.StructureRegion); bounds = new Rectangle(r.X, r.Y, r.Width, r.Height); - - r = API.GetWindowBounds(window.Handle, WindowRegionCode.GlobalPortRegion); + + r = API.GetWindowBounds(window.Handle, WindowRegionCode.ContentRegion); clientRectangle = new Rectangle(0, 0, r.Width, r.Height); } @@ -891,13 +890,13 @@ namespace OpenTK.Platform.MacOS public int X { - get { return ClientRectangle.X; } + get { return Bounds.X; } set { Location = new Point(value, Y); } } public int Y { - get { return ClientRectangle.Y; } + get { return Bounds.Y; } set { Location = new Point(X, value); } } From f7e2c2ea7c8b19aac50a6c86c58e21b95d664a2c Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 20:30:58 +0100 Subject: [PATCH 060/245] 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 1189b33ed59bc150e169e4279fccc405919f219c Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 20:40:27 +0100 Subject: [PATCH 061/245] Fixed OpenTK/Carbon key repeat behavior to match the other backends --- Source/OpenTK/Platform/MacOS/CarbonGLNative.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index a9cf8f81..c6453f7b 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -379,13 +379,7 @@ namespace OpenTK.Platform.MacOS { case KeyboardEventKind.RawKeyRepeat: if (InputDriver.Keyboard[0].KeyRepeat) - { - // Repeat KeyPress events until KeyUp - if (!Char.IsControl(mKeyPressArgs.KeyChar)) - { - OnKeyPress(mKeyPressArgs); - } - } + goto case KeyboardEventKind.RawKeyDown; break; case KeyboardEventKind.RawKeyDown: From 95270c877df70fb5b5a6519534f98d7951382092 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 21:42:16 +0100 Subject: [PATCH 062/245] Added Compositing and FrameworkScaled flags --- .../MacOS/CarbonBindings/CarbonAPI.cs | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs index 407563f4..530ea688 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs @@ -241,6 +241,7 @@ namespace OpenTK.Platform.MacOS.Carbon WindowClickProxyIconRgn = 38, WindowClose = 72, WindowClosed = 73, + WindowPaint = 1013, } internal enum MouseEventKind : int { @@ -388,7 +389,35 @@ namespace OpenTK.Platform.MacOS.Carbon SideTitlebar = (1u << 5), /* window wants a titlebar on the side (floating window class only)*/ NoUpdates = (1u << 16), /* this window receives no update events*/ NoActivates = (1u << 17), /* this window receives no activate events*/ - NoBuffering = (1u << 20), /* this window is not buffered (Mac OS X only)*/ + + /// + /// This window uses composited drawing. This means that the entire + /// window is comprised of HIViews, and can be treated thusly. This + /// attribute must be specified at window creation, and cannot be + /// changed later with ChangeWindows. In 64-bit mode, all windows must + /// be compositing, and you must always specify this attribute when + /// creating a window from code or designing a window in Interface + /// Builder. Available on Mac OS X 10.2 and later. + /// + Compositing = (1u << 19), + + /// + /// This window's context should be scaled to match the display scale + /// factor. This attribute can only be used when + /// kHIWindowBitCompositing is also enabled. When this attribute is + /// enabled, you may not draw with QuickDraw in the window. If this + /// attribute is enabled and if the scale factor is something other + /// than 1.0, the window's scale mode will be + /// kHIWindowScaleModeFrameworkScaled. You may only specify this + /// attribute at window creation time. Available for all windows in + /// Mac OS X 10.4 and later. + /// + FrameworkScaled = (1u << 20), + + /// + /// This window has the standard Carbon window event handler + /// installed. Available for all windows. + /// StandardHandler = (1u << 25), InWindowMenu = (1u << 27), LiveResize = (1u << 28), @@ -1014,6 +1043,12 @@ namespace OpenTK.Platform.MacOS.Carbon return retval; } + + //[DllImport(carbon)] + //static extern OSStatus HIWindowCreate(WindowClass class, int[] attributes, + // ref WindowDefSpec defSpec, HICoordinateSpace space, ref HIRect bounds, + // out IntPtr window); + #region --- SetWindowTitle --- [DllImport(carbon)] From d7fefe495ef1d1271ddf7dab4c912d14a2034912 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 21:43:17 +0100 Subject: [PATCH 063/245] Removed unused constructors --- Source/OpenTK/Platform/MacOS/CarbonGLNative.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index c6453f7b..88511fb7 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -108,18 +108,6 @@ namespace OpenTK.Platform.MacOS Application.Initialize(); } - CarbonGLNative() : this(WindowClass.Document, - WindowAttributes.StandardDocument | WindowAttributes.StandardHandler | - WindowAttributes.InWindowMenu | WindowAttributes.LiveResize) - { - } - - CarbonGLNative(WindowClass @class, WindowAttributes attrib) - { - mWindowClass = @class; - mWindowAttrib = attrib; - } - public CarbonGLNative(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { From 95c3d00aa27a3c8700caef58e80dc2ed3e5c66a5 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 23 Dec 2013 22:00:10 +0100 Subject: [PATCH 064/245] 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 065/245] 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 066/245] 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 067/245] 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 068/245] 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 069/245] 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 070/245] 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 071/245] 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 072/245] 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 073/245] 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 074/245] 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 075/245] 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 076/245] 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 077/245] 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 078/245] 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 079/245] 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 080/245] 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 081/245] 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 082/245] 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 From 022e5c845c07d3e6ac198f970068243dfaf48c6a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 18 Dec 2013 15:50:59 +0100 Subject: [PATCH 083/245] 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 d9985fc5714e8ba74a72d39bac90aedc7cf66926 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 18 Dec 2013 17:16:29 +0100 Subject: [PATCH 084/245] 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 ee65f81f5614e13ad98da982e3863156694eefbe Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 10:42:12 +0100 Subject: [PATCH 085/245] 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 484af186738856b689afbecd76840c2f6c355c0f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 16:27:26 +0100 Subject: [PATCH 086/245] 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 ecd04a3cad181552f59d57c1cd1ccd48b2e95529 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 16:27:57 +0100 Subject: [PATCH 087/245] 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 2bf024a912e6aae1d69d1caee372102402a323ba Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 19 Dec 2013 16:28:20 +0100 Subject: [PATCH 088/245] 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 1acf8a807b374c6d5e24c354a34a5085f5f85f9c Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:01:04 +0100 Subject: [PATCH 089/245] 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 0c10f29bd8e5b509209b987b11305dd3c87591d4 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:07:30 +0100 Subject: [PATCH 090/245] 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 18f99c2f635dd092d019996345d0f0513778b463 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:07:40 +0100 Subject: [PATCH 091/245] 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 91d248ad29aa316f390261dd39561ffa104d8277 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:32:18 +0100 Subject: [PATCH 092/245] 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 76a35c0b918b2f5a1b1543f0832777a40c65d3fc Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:32:24 +0100 Subject: [PATCH 093/245] 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 1ba5fd438057dac4eb52adca735c4f8245106cac Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:34:34 +0100 Subject: [PATCH 094/245] 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 4a13415fc67b502494f80c0e1ca5b256f816ab71 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 22:47:50 +0100 Subject: [PATCH 095/245] 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 3660509deefe760969bd3888248f102a40c67ae0 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 23:12:53 +0100 Subject: [PATCH 096/245] 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 a72d70c3d01be0be043ce0ee959761c373a0d691 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 23:13:21 +0100 Subject: [PATCH 097/245] 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 1d61bd9dd2d14814c9f3cc93a35dc238400796c9 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 23:54:55 +0100 Subject: [PATCH 098/245] 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 2f1a81da2c57d85e04bd5e1a304cb056cff8ccc6 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 23:55:28 +0100 Subject: [PATCH 099/245] 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 31ce400a7e206e41564e35227570d8ff90cdeac6 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Sun, 22 Dec 2013 23:55:46 +0100 Subject: [PATCH 100/245] 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 44351a03c4249689c6c8573244d0861154a01552 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 00:17:13 +0100 Subject: [PATCH 101/245] 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 0c9a67da411310f3f7f158a503d881ae8c0d04bd Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 01:29:12 +0100 Subject: [PATCH 102/245] 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 1adc3f77331bf1c01f7b00638bb64b22d7b49319 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 01:49:49 +0100 Subject: [PATCH 103/245] 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 0875cbd9285ed51c12948fc418c5baf6d6b23ea1 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 01:50:13 +0100 Subject: [PATCH 104/245] 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 5d88a8daf4879d139c3e0608ab0e60d38df9aaaa Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 01:50:25 +0100 Subject: [PATCH 105/245] 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 8f7eebb58d3213c15a683447698b800e75a08cee Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 23 Dec 2013 19:19:41 +0100 Subject: [PATCH 106/245] 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 dd648a836262ded283bc1d4a5c89c17d56e5c3d0 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 20:30:58 +0100 Subject: [PATCH 107/245] 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 ea3c9ffe8599aeab49e73d0881ca1e4bddd56637 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Mon, 23 Dec 2013 22:00:10 +0100 Subject: [PATCH 108/245] 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 ddc52ce13524f2c1fd45c34a2241877cc6639ef8 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 00:15:28 +0100 Subject: [PATCH 109/245] 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 f459647613aee230dd16fc5aa52db5560ac290f4 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 00:15:43 +0100 Subject: [PATCH 110/245] 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 b62f5993d486027d9f4ac4672c522d526adf05e7 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 01:36:44 +0100 Subject: [PATCH 111/245] 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 e2d86fdf5221b43c90b1d2fe79a1896e5f956f7a Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 01:37:34 +0100 Subject: [PATCH 112/245] 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 7e5307bd4aa5cfd3c0ab3ac3f1460270354bc86a Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 03:16:32 +0100 Subject: [PATCH 113/245] 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 2839db587e408e14fe47a974e25609bf2073cb07 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 12:47:09 +0100 Subject: [PATCH 114/245] 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 52daef4b0d7f3650041abfa62aae6d6688d1b2cd Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 12:48:15 +0100 Subject: [PATCH 115/245] 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 91b54cfbf3dcae47a1c90af2e9729c873bd2c65f Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 12:52:57 +0100 Subject: [PATCH 116/245] 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 02fb6bf2f92da5e38332e3bef7dcbabcb87a289d Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 14:21:37 +0100 Subject: [PATCH 117/245] 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 165aa5bde62d490513d5e341d31d94e8421b0a98 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 14:22:03 +0100 Subject: [PATCH 118/245] 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 15c01d0d5c4249d13f6e2fe835c839daea3cbf23 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 14:24:29 +0100 Subject: [PATCH 119/245] 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 890d56ae63c978789211096facaafe853f3daea5 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 14:24:42 +0100 Subject: [PATCH 120/245] 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 82a2c9113cb33ec08bd97fa1ccb9397885ebd6d3 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 15:01:10 +0100 Subject: [PATCH 121/245] 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 d33d0c7387cb2e9b13d34d842b36b30a154629d6 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 15:01:33 +0100 Subject: [PATCH 122/245] 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 f3cb578587d614281f7801549318f02524d7d63b Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 15:51:25 +0100 Subject: [PATCH 123/245] 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 9a90772ceff5779ff0eccd93187d51d59d1be016 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 16:42:48 +0100 Subject: [PATCH 124/245] 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 7fd7b8c7a255ce6c4a2a18883a715e1f732f7c97 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Tue, 24 Dec 2013 17:06:39 +0100 Subject: [PATCH 125/245] 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 1eb807bb64aee69204674cd0328313bef3574394 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 24 Dec 2013 17:16:16 +0100 Subject: [PATCH 126/245] 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 From 6fc679c4ba830eedfc53e6d70295a062a8991e2a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 24 Dec 2013 19:14:35 +0100 Subject: [PATCH 127/245] Removed duplicate button types --- Source/OpenTK/Input/Buttons.cs | 54 ++-------------------------------- 1 file changed, 3 insertions(+), 51 deletions(-) diff --git a/Source/OpenTK/Input/Buttons.cs b/Source/OpenTK/Input/Buttons.cs index 739a8fcd..5d3889e7 100644 --- a/Source/OpenTK/Input/Buttons.cs +++ b/Source/OpenTK/Input/Buttons.cs @@ -27,6 +27,9 @@ using System; namespace OpenTK.Input { + /// + /// Enumerates available buttons for a canonical GamePad device. + /// public enum Buttons { /// @@ -158,56 +161,5 @@ namespace OpenTK.Input /// 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, } } From c51c4934df348442818a9906eebcdb2ea46dbc2b Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 24 Dec 2013 19:14:54 +0100 Subject: [PATCH 128/245] Added GameControllerGetBind APIs --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 55 ++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 163d393b..5aa6dad4 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -137,7 +137,28 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetAxis", ExactSpelling = true)] public static extern short GameControllerGetAxis(IntPtr gamecontroller, GameControllerAxis axis); - /// > + /// + /// Gets the SDL joystick layer binding for the specified game controller axis + /// + /// Pointer to a game controller instance returned by GameControllerOpen. + /// A value from the GameControllerAxis enumeration + /// A GameControllerButtonBind instance describing the specified binding + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForAxis", ExactSpelling = true)] + public static extern GameControllerButtonBind GameControllerGetBindForAxis(IntPtr gamecontroller, GameControllerAxis axis); + + /// + /// Gets the SDL joystick layer binding for the specified game controller button + /// + /// Pointer to a game controller instance returned by GameControllerOpen. + /// A value from the GameControllerButton enumeration + /// A GameControllerButtonBind instance describing the specified binding + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForButton", ExactSpelling = true)] + public static extern GameControllerButtonBind GameControllerGetBindForButton( + IntPtr gamecontroller, GameControllerButton button); + + /// /// Gets the current state of a button on a game controller. /// /// A game controller handle previously opened with GameControllerOpen. @@ -147,6 +168,15 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetButton", ExactSpelling = true)] public static extern bool GameControllerGetButton(IntPtr gamecontroller, GameControllerButton button); + /// + /// Retrieve the joystick handle that corresponds to the specified game controller. + /// + /// A game controller handle previously opened with GameControllerOpen. + /// A handle to a joystick, or IntPtr.Zero in case of error. The pointer is owned by the callee. Use SDL.GetError to retrieve error information + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetJoystick", ExactSpelling = true)] + public static extern IntPtr GameControllerGetJoystick(IntPtr gamecontroller); + /// /// Opens a game controller for use. /// @@ -587,6 +617,14 @@ namespace OpenTK.Platform.SDL2 Max } + enum GameControllerBindType : byte + { + None = 0, + Button, + Axis, + Hat + } + [Flags] enum HatPosition : byte { @@ -1277,6 +1315,21 @@ namespace OpenTK.Platform.SDL2 #endif } + [StructLayout(LayoutKind.Explicit)] + struct GameControllerButtonBind + { + [FieldOffset(0)] + public GameControllerBindType BindType; + [FieldOffset(4)] + public Button Button; + [FieldOffset(4)] + public GameControllerAxis Axis; + [FieldOffset(4)] + public int Hat; + [FieldOffset(8)] + public int HatMask; + } + struct JoyAxisEvent { public EventType Type; From b4b8bc1665f189a3ddc63de4f167ff6f5b5833e2 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 24 Dec 2013 19:15:23 +0100 Subject: [PATCH 129/245] Implemented SDL IJoystickDriver2 and IGamePadDriver --- .../Platform/SDL2/Sdl2JoystickDriver.cs | 116 ++++++++++++++++-- 1 file changed, 106 insertions(+), 10 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index d5caa2c1..7e5cd955 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -48,6 +48,7 @@ namespace OpenTK.Platform.SDL2 { public IntPtr Handle { get; private set; } public GamePadState State { get; set; } + public GamePadCapabilities Capabilities { get; set; } public Sdl2GamePad(IntPtr handle) { @@ -114,6 +115,53 @@ namespace OpenTK.Platform.SDL2 return controllers.ContainsKey(id); } + GamePadAxes TranslateAxes(IntPtr gamecontroller) + { + GamePadAxes axes = 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftX) ? GamePadAxes.LeftX : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftY) ? GamePadAxes.LeftY : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightX) ? GamePadAxes.RightX : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightY) ? GamePadAxes.RightY : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerLeft) ? GamePadAxes.LeftTrigger : 0; + axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerRight) ? GamePadAxes.RightTrigger : 0; + return axes; + } + + Buttons TranslateButtons(IntPtr gamecontroller) + { + Buttons buttons = 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.A) ? Buttons.A : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.B) ? Buttons.B : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.X) ? Buttons.X : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.Y) ? Buttons.Y : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.START) ? Buttons.Start : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.BACK) ? Buttons.Back : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSHOULDER) ? Buttons.LeftShoulder : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSHOULDER) ? Buttons.RightShoulder : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSTICK) ? Buttons.LeftStick : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSTICK) ? Buttons.RightStick : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.GUIDE) ? Buttons.BigButton : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_DOWN) ? Buttons.DPadDown : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_UP) ? Buttons.DPadUp : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_LEFT) ? Buttons.DPadLeft : 0; + buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_RIGHT) ? Buttons.DPadRight : 0; + return buttons; + } + + bool IsAxisBind(IntPtr gamecontroller, GameControllerAxis axis) + { + GameControllerButtonBind bind = + SDL.GameControllerGetBindForAxis(gamecontroller, axis); + return bind.BindType == GameControllerBindType.Axis; + } + + bool IsButtonBind(IntPtr gamecontroller, GameControllerButton button) + { + GameControllerButtonBind bind = + SDL.GameControllerGetBindForButton(gamecontroller, button); + return bind.BindType == GameControllerBindType.Axis; + } + #endregion #region Public Members @@ -232,11 +280,27 @@ namespace OpenTK.Platform.SDL2 if (handle != IntPtr.Zero) { 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; + + IntPtr joystick = SDL.GameControllerGetJoystick(handle); + if (joystick != IntPtr.Zero) + { + SDL.JoystickNumAxes(joystick); + pad.Capabilities = new GamePadCapabilities( + GamePadType.GamePad, + TranslateAxes(joystick), + TranslateButtons(joystick), + true); + pad.State.SetConnected(true); + + // Check whether the device has ever been connected before + if (!controllers.ContainsKey(id)) + controllers.Add(id, null); + controllers[id] = pad; + } + else + { + Debug.Print("[SDL2] Failed to retrieve joystick from game controller. Error: {0}", SDL.GetError()); + } } break; @@ -302,16 +366,19 @@ namespace OpenTK.Platform.SDL2 public GamePadCapabilities GetCapabilities(int index) { + if (IsControllerValid(index)) + { + return controllers[index].Capabilities; + } return new GamePadCapabilities(); } public GamePadState GetState(int index) { - if (joysticks.Count >= index) + if (IsControllerValid(index)) { - + return controllers[index].State; } - return new GamePadState(); } @@ -326,12 +393,41 @@ namespace OpenTK.Platform.SDL2 JoystickState IJoystickDriver2.GetState(int index) { - throw new NotImplementedException(); + JoystickState state = new JoystickState(); + if (IsJoystickValid(index)) + { + JoystickDevice joystick = + (JoystickDevice)joysticks[sdl_joyid_to_joysticks[index]]; + + for (int i = 0; i < joystick.Axis.Count; i++) + { + state.SetAxis(JoystickAxis.Axis0 + i, (short)(joystick.Axis[i] * short.MaxValue + 0.5f)); + } + + for (int i = 0; i < joystick.Button.Count; i++) + { + state.SetButton(JoystickButton.Button0 + i, joystick.Button[i]); + } + + state.SetIsConnected(true); + } + + return state; } JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) { - throw new NotImplementedException(); + if (IsJoystickValid(index)) + { + JoystickDevice joystick = + (JoystickDevice)joysticks[sdl_joyid_to_joysticks[index]]; + + return new JoystickCapabilities( + joystick.Axis.Count, + joystick.Button.Count, + true); + } + return new JoystickCapabilities(); } #endregion From f7fbf38c43ee0543172f59391403e6ed79533196 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 25 Dec 2013 01:47:27 +0100 Subject: [PATCH 130/245] IsButtonBind should check for buttons, not axes --- Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 7e5cd955..5d014d0b 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -159,7 +159,7 @@ namespace OpenTK.Platform.SDL2 { GameControllerButtonBind bind = SDL.GameControllerGetBindForButton(gamecontroller, button); - return bind.BindType == GameControllerBindType.Axis; + return bind.BindType == GameControllerBindType.Button; } #endregion From 83039f8740fc2725288bd73554b61f2f6e3ccc53 Mon Sep 17 00:00:00 2001 From: parallels Date: Wed, 25 Dec 2013 11:11:29 +0100 Subject: [PATCH 131/245] Ignore *.pidb files from MonoDevelop 2.x --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 22578d3d..ee5d815a 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ Binaries/ *.vspscc *.vssscc .builds +*.pidb # Visual C++ cache files ipch/ From ffdf881cea72d86f08187f68467689cf16845d64 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 25 Dec 2013 11:14:30 +0100 Subject: [PATCH 132/245] Do not raise KeyPress for control characters --- Source/OpenTK/Platform/X11/X11GLNative.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 0e4ef49f..12460425 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -805,8 +805,11 @@ namespace OpenTK.Platform.X11 { for (int i = 0; i < status; i++) { - KPEventArgs.KeyChar = chars[i]; - key_press(this, KPEventArgs); + if (!Char.IsControl(chars[i])) + { + KPEventArgs.KeyChar = chars[i]; + key_press(this, KPEventArgs); + } } } break; From 4d27b6ee558897e3861a519553fcc3b8da9b32ea Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 25 Dec 2013 11:46:02 +0100 Subject: [PATCH 133/245] Implemented X11 KeyDown/KeyUp events and reduced code duplication --- Source/OpenTK/Platform/X11/X11GLNative.cs | 60 ++++++++++++++++------- Source/OpenTK/Platform/X11/X11Input.cs | 43 +++++++++------- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 12460425..eb611394 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -59,6 +59,7 @@ namespace OpenTK.Platform.X11 // Legacy input support X11Input driver; + KeyboardDevice keyboard; MouseDevice mouse; // Window manager hints for fullscreen windows. @@ -119,6 +120,8 @@ namespace OpenTK.Platform.X11 readonly byte[] ascii = new byte[16]; readonly char[] chars = new char[16]; readonly KeyPressEventArgs KPEventArgs = new KeyPressEventArgs('\0'); + readonly KeyboardKeyEventArgs KeyDownEventArgs = new KeyboardKeyEventArgs(); + readonly KeyboardKeyEventArgs KeyUpEventArgs = new KeyboardKeyEventArgs(); readonly IntPtr EmptyCursor; @@ -209,6 +212,7 @@ namespace OpenTK.Platform.X11 RefreshWindowBounds(ref e); driver = new X11Input(window); + keyboard = driver.Keyboard[0]; mouse = driver.Mouse[0]; EmptyCursor = CreateEmptyCursor(window); @@ -256,7 +260,7 @@ namespace OpenTK.Platform.X11 #endregion #region Private Members - + #region private void RegisterAtoms() /// @@ -795,32 +799,50 @@ namespace OpenTK.Platform.X11 break; case XEventName.KeyPress: - driver.ProcessEvent(ref e); - int status = 0; - status = Functions.XLookupString(ref e.KeyEvent, ascii, ascii.Length, null, IntPtr.Zero); - Encoding.Default.GetChars(ascii, 0, status, chars, 0); - - EventHandler key_press = KeyPress; - if (key_press != null) + case XEventName.KeyRelease: + bool pressed = e.type == XEventName.KeyPress; + Key key; + if (driver.TranslateKey(ref e.KeyEvent, out key)) { - for (int i = 0; i < status; i++) + if (pressed) { - if (!Char.IsControl(chars[i])) + // Raise KeyDown event + KeyDownEventArgs.Key = key; + KeyDownEventArgs.ScanCode = (uint)e.KeyEvent.keycode; + KeyDown(this, KeyDownEventArgs); + } + else + { + // Raise KeyUp event + KeyUpEventArgs.Key = key; + KeyUpEventArgs.ScanCode = (uint)e.KeyEvent.keycode; + KeyUp(this, KeyDownEventArgs); + } + + // Update legacy GameWindow.Keyboard API: + keyboard.SetKey(key, (uint)e.KeyEvent.keycode, pressed); + + if (pressed) + { + // Translate XKeyPress to characters and + // raise KeyPress events + int status = 0; + status = Functions.XLookupString( + ref e.KeyEvent, ascii, ascii.Length, null, IntPtr.Zero); + Encoding.Default.GetChars(ascii, 0, status, chars, 0); + + for (int i = 0; i < status; i++) { - KPEventArgs.KeyChar = chars[i]; - key_press(this, KPEventArgs); + if (!Char.IsControl(chars[i])) + { + KPEventArgs.KeyChar = chars[i]; + KeyPress(this, KPEventArgs); + } } } } break; - case XEventName.KeyRelease: - // Todo: raise KeyPress event. Use code from - // http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/X11Keyboard.cs?view=markup - - driver.ProcessEvent(ref e); - break; - case XEventName.MotionNotify: { // Try to detect and ignore events from XWarpPointer, below. diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs index a9193a40..6f3bab0b 100644 --- a/Source/OpenTK/Platform/X11/X11Input.cs +++ b/Source/OpenTK/Platform/X11/X11Input.cs @@ -148,29 +148,38 @@ namespace OpenTK.Platform.X11 #endif #endregion + #region TranslateKey + + internal bool TranslateKey(ref XKeyEvent e, out Key key) + { + XKey keysym = (XKey)API.LookupKeysym(ref e, 0); + XKey keysym2 = (XKey)API.LookupKeysym(ref e, 1); + key = Key.Unknown; + + if (keymap.ContainsKey(keysym)) + { + key = keymap[keysym]; + } + else if (keymap.ContainsKey(keysym2)) + { + key = keymap[keysym2]; + } + else + { + Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.keycode, (XKey)keysym, (XKey)keysym2); + } + + return key != Key.Unknown; + } + + #endregion + #region internal void ProcessEvent(ref XEvent e) internal void ProcessEvent(ref XEvent e) { switch (e.type) { - case XEventName.KeyPress: - case XEventName.KeyRelease: - bool pressed = e.type == XEventName.KeyPress; - XKey keysym = (XKey)API.LookupKeysym(ref e.KeyEvent, 0); - XKey keysym2 = (XKey)API.LookupKeysym(ref e.KeyEvent, 1); - Key key = Key.Unknown; - - if (keymap.ContainsKey(keysym)) - key = keymap[keysym]; - else if (keymap.ContainsKey(keysym2)) - key = keymap[keysym2]; - else - Debug.Print("KeyCode {0} (Keysym: {1}, {2}) not mapped.", e.KeyEvent.keycode, (XKey)keysym, (XKey)keysym2); - - keyboard.SetKey(key, (uint)e.KeyEvent.keycode, pressed); - break; - case XEventName.ButtonPress: if (e.ButtonEvent.button == 1) mouse[OpenTK.Input.MouseButton.Left] = true; else if (e.ButtonEvent.button == 2) mouse[OpenTK.Input.MouseButton.Middle] = true; From 6c6e09aae6a26c6cb2499cf4a05faf03eef1abd3 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 27 Dec 2013 11:07:38 +0100 Subject: [PATCH 134/245] [X11] Allow resizing with fixed borders Windows can now be resized programmatically, even when they have WindowBorder.Fixed. All resizing logic is now consolidated in the Bounds property, and ConfigureNotify messages are now handled correctly depending on their source (StructureNotify or SubStructureNotify.) --- Source/OpenTK/Platform/X11/X11GLNative.cs | 88 +++++++++++++++++------ 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index eb611394..4bc1178f 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -314,6 +314,11 @@ namespace OpenTK.Platform.X11 #endregion #region SetWindowMinMax + + void SetWindowMinMax(int min_width, int min_height, int max_width, int max_height) + { + SetWindowMinMax((short)min_width, (short)min_height, (short)max_width, (short)max_height); + } void SetWindowMinMax(short min_width, short min_height, short max_width, short max_height) { @@ -671,9 +676,30 @@ namespace OpenTK.Platform.X11 { RefreshWindowBorders(); + // For whatever reason, the x/y coordinates + // of a configure event are global to the + // root window when it is a send_event but + // local when it is a regular event. + // I don't know who designed this, but this is + // utter nonsense. + int x, y; + IntPtr unused; + if (!e.ConfigureEvent.send_event) + { + Functions.XTranslateCoordinates(window.Display, + window.Handle, window.RootWindow, + 0, 0, out x, out y, out unused); + } + else + { + x = e.ConfigureEvent.x; + y = e.ConfigureEvent.y; + } + Point new_location = new Point( - e.ConfigureEvent.x - border_left, - e.ConfigureEvent.y - border_top); + x - border_left, + y - border_top); + if (Location != new_location) { bounds.Location = new_location; @@ -692,6 +718,8 @@ namespace OpenTK.Platform.X11 Resize(this, EventArgs.Empty); } + + Debug.Print("[X11] Window bounds changed: {0}", bounds); } static IntPtr CreateEmptyCursor(X11WindowInfo window) @@ -951,17 +979,44 @@ namespace OpenTK.Platform.X11 public Rectangle Bounds { - get { return bounds; } + get + { + return bounds; + } set { + bool is_location_changed = bounds.Location != value.Location; + bool is_size_changed = bounds.Size != value.Size; + + int x = value.X; + int y = value.Y; + int width = value.Width - border_left - border_right; + int height = value.Height - border_top - border_bottom; + + if (WindowBorder != WindowBorder.Resizable) + { + SetWindowMinMax(width, height, width, height); + } + using (new XLock(window.Display)) { - Functions.XMoveResizeWindow(window.Display, window.Handle, - value.X, - value.Y, - value.Width - border_left - border_right, - value.Height - border_top - border_bottom); + if (is_location_changed && is_size_changed) + { + Functions.XMoveResizeWindow(window.Display, window.Handle, + x, y, width, height); + } + else if (is_location_changed) + { + Functions.XMoveWindow(window.Display, window.Handle, + x, y); + } + else if (is_size_changed) + { + Functions.XResizeWindow(window.Display, window.Handle, + width, height); + } } + ProcessEvents(); } } @@ -975,11 +1030,7 @@ namespace OpenTK.Platform.X11 get { return Bounds.Location; } set { - using (new XLock(window.Display)) - { - Functions.XMoveWindow(window.Display, window.Handle, value.X, value.Y); - } - ProcessEvents(); + Bounds = new Rectangle(value, Bounds.Size); } } @@ -992,16 +1043,7 @@ namespace OpenTK.Platform.X11 get { return Bounds.Size; } set { - int width = value.Width - border_left - border_right; - int height = value.Height - border_top - border_bottom; - width = width <= 0 ? 1 : width; - height = height <= 0 ? 1 : height; - - using (new XLock(window.Display)) - { - Functions.XResizeWindow(window.Display, window.Handle, width, height); - } - ProcessEvents(); + Bounds = new Rectangle(Bounds.Location, value); } } From ab8796c9428b800743fdb725e423140f032d4fcb Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 27 Dec 2013 11:10:41 +0100 Subject: [PATCH 135/245] [Input] Do not crash with scancodes > 255 --- Source/OpenTK/Input/KeyboardDevice.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/OpenTK/Input/KeyboardDevice.cs b/Source/OpenTK/Input/KeyboardDevice.cs index d21ca5a7..7150c874 100644 --- a/Source/OpenTK/Input/KeyboardDevice.cs +++ b/Source/OpenTK/Input/KeyboardDevice.cs @@ -201,6 +201,10 @@ namespace OpenTK.Input { if (keys[(int)key] != state || KeyRepeat) { + // limit scancode to 8bits, otherwise the assignment + // below will crash randomly + scancode &= 0xff; + keys[(int)key] = scancodes[scancode] = state; if (state && KeyDown != null) From 88f7cd68f528195e0da14f0eb4a8c6a6a329de13 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 27 Dec 2013 13:31:51 +0200 Subject: [PATCH 136/245] [X11] Fixed border size on Gnome 3 The _NET_FRAME_EXTENTS atom is implemented differently by different window managers, when window decorations are hidden with Motif. Unity returns a 0 size, while Gnome 3 returns the previous size. This patch removes that ambiguity: when decorations are hidden, border size becomes zero. This should work everywhere, unless some window manager decides to troll us by decorating the window when we explicitly request no decorations. Sigh... --- Source/OpenTK/Platform/X11/X11GLNative.cs | 87 ++++++++++++++--------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index 4bc1178f..0336b8e6 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -626,46 +626,63 @@ namespace OpenTK.Platform.X11 bool RefreshWindowBorders() { - IntPtr atom, nitems, bytes_after, prop = IntPtr.Zero; - int format; bool borders_changed = false; - using (new XLock(window.Display)) + if (IsWindowBorderHidden) { - Functions.XGetWindowProperty(window.Display, window.Handle, - _atom_net_frame_extents, IntPtr.Zero, new IntPtr(16), false, - (IntPtr)Atom.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop); + borders_changed = + border_left != 0 || + border_right != 0 || + border_top != 0 || + border_bottom != 0; + + border_left = 0; + border_right = 0; + border_top = 0; + border_bottom = 0; } - - if ((prop != IntPtr.Zero)) + else { - if ((long)nitems == 4) - { - int new_border_left = Marshal.ReadIntPtr(prop, 0).ToInt32(); - int new_border_right = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32(); - int new_border_top = Marshal.ReadIntPtr(prop, IntPtr.Size * 2).ToInt32(); - int new_border_bottom = Marshal.ReadIntPtr(prop, IntPtr.Size * 3).ToInt32(); - - borders_changed = - new_border_left != border_left || - new_border_right != border_right || - new_border_top != border_top || - new_border_bottom != border_bottom; - - border_left = new_border_left; - border_right = new_border_right; - border_top = new_border_top; - border_bottom = new_border_bottom; - - //Debug.WriteLine(border_left); - //Debug.WriteLine(border_right); - //Debug.WriteLine(border_top); - //Debug.WriteLine(border_bottom); - } - + IntPtr atom, nitems, bytes_after, prop = IntPtr.Zero; + int format; + using (new XLock(window.Display)) { - Functions.XFree(prop); + Functions.XGetWindowProperty(window.Display, window.Handle, + _atom_net_frame_extents, IntPtr.Zero, new IntPtr(16), false, + (IntPtr)Atom.XA_CARDINAL, out atom, out format, out nitems, out bytes_after, ref prop); + } + + if ((prop != IntPtr.Zero)) + { + if ((long)nitems == 4) + { + int new_border_left = Marshal.ReadIntPtr(prop, 0).ToInt32(); + int new_border_right = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32(); + int new_border_top = Marshal.ReadIntPtr(prop, IntPtr.Size * 2).ToInt32(); + int new_border_bottom = Marshal.ReadIntPtr(prop, IntPtr.Size * 3).ToInt32(); + + borders_changed = + new_border_left != border_left || + new_border_right != border_right || + new_border_top != border_top || + new_border_bottom != border_bottom; + + border_left = new_border_left; + border_right = new_border_right; + border_top = new_border_top; + border_bottom = new_border_bottom; + + //Debug.WriteLine(border_left); + //Debug.WriteLine(border_right); + //Debug.WriteLine(border_top); + //Debug.WriteLine(border_bottom); + } + + using (new XLock(window.Display)) + { + Functions.XFree(prop); + } } } @@ -718,8 +735,8 @@ namespace OpenTK.Platform.X11 Resize(this, EventArgs.Empty); } - - Debug.Print("[X11] Window bounds changed: {0}", bounds); + + //Debug.Print("[X11] Window bounds changed: {0}", bounds); } static IntPtr CreateEmptyCursor(X11WindowInfo window) From 0d1df413938aa2821d3cacd921d6be3a99bd97b6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 27 Dec 2013 14:01:21 +0200 Subject: [PATCH 137/245] [X11] Improve GraphicsMode fallback (fixes issue #23) When the user requests a GraphicsMode that is not directly supported by the GPU/drivers, we should relax the requested parameters until we find a supported mode. An exception should only be thrown when there is no usable mode. This makes the X11 backend match the behavior of Windows. The SDL/X11 backend works a little bit differently, in that it falls back to the a default mode directly if the requested mode is not available. There is nothing we can do about that. --- Source/OpenTK/Platform/X11/X11GraphicsMode.cs | 73 ++++++++++++++++--- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11GraphicsMode.cs b/Source/OpenTK/Platform/X11/X11GraphicsMode.cs index 789281bf..1c019036 100644 --- a/Source/OpenTK/Platform/X11/X11GraphicsMode.cs +++ b/Source/OpenTK/Platform/X11/X11GraphicsMode.cs @@ -39,19 +39,68 @@ namespace OpenTK.Platform.X11 // The actual GraphicsMode that will be selected. IntPtr visual = IntPtr.Zero; IntPtr display = API.DefaultDisplay; - - // Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig. - // This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual. - visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo); - - if (visual == IntPtr.Zero) - visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo); - - if (visual == IntPtr.Zero) - throw new GraphicsModeException("Requested GraphicsMode not available."); - + + do + { + // Try to select a visual using Glx.ChooseFBConfig and Glx.GetVisualFromFBConfig. + // This is only supported on GLX 1.3 - if it fails, fall back to Glx.ChooseVisual. + visual = SelectVisualUsingFBConfig(color, depth, stencil, samples, accum, buffers, stereo); + + if (visual == IntPtr.Zero) + visual = SelectVisualUsingChooseVisual(color, depth, stencil, samples, accum, buffers, stereo); + + if (visual == IntPtr.Zero) + { + // Relax parameters and retry + if (stereo) + { + stereo = false; + continue; + } + + if (accum != 0) + { + accum = 0; + continue; + } + + if (samples > 0) + { + samples = Math.Max(samples - 2, 0); + continue; + } + + if (stencil != 0) + { + stencil = 0; + continue; + } + + if (depth != 0) + { + depth = 0; + continue; + } + + if (color != 24) + { + color = 24; + continue; + } + + if (buffers != 0) + { + buffers = 0; + continue; + } + + throw new GraphicsModeException("Requested GraphicsMode not available."); + } + } + while (visual == IntPtr.Zero); + XVisualInfo info = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo)); - + // See what we *really* got: int r, g, b, a; Glx.GetConfig(display, ref info, GLXAttribute.ALPHA_SIZE, out a); From b79d951e62615f87fbad5de55be39417d44d1d68 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:04:22 +0100 Subject: [PATCH 138/245] [Mac] Use Cgl.GetCurrentContext instead of Agl CGL appears to work with both AGL and NSOpenGL contexts, whereas AGL is limited to AGL contexts. This allows us to be more flexible in terms of implementation (i.e. we can use Cgl.GetCurrentContext to retrieve a handle to a context created through SDL, which uses NSOpenGL internally.) --- Source/OpenTK/Platform/MacOS/MacOSFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs index ad540311..97473c23 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs @@ -70,7 +70,7 @@ namespace OpenTK.Platform.MacOS { return (GraphicsContext.GetCurrentContextDelegate)delegate { - return new ContextHandle(Agl.aglGetCurrentContext()); + return new ContextHandle(Cgl.GetCurrentContext()); }; } From 91719770790d8c7b6de1d1502788c9482d7248be Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:04:46 +0100 Subject: [PATCH 139/245] [SDL2] Added missing [SuppressUnamangedCodeSecurity] attribute --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 9bf07558..40ec3700 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -334,6 +334,7 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetAttribute", ExactSpelling = true)] public static extern int GetAttribute(ContextAttribute attr, out int value); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)] public static extern IntPtr GetCurrentContext(); From cf76aa608566157d862fc95b11ef8f30cb4092a0 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:06:33 +0100 Subject: [PATCH 140/245] [Mac] Moved GetAddress implementation to its own class The functions defined in libdl.dylib are useful in more places than just AglContext. Moving them to their own class ensures we can access these from wherever we might need them. --- Source/OpenTK/Platform/MacOS/AglContext.cs | 54 +------------ Source/OpenTK/Platform/MacOS/NS.cs | 92 ++++++++++++++++++++++ 2 files changed, 95 insertions(+), 51 deletions(-) create mode 100644 Source/OpenTK/Platform/MacOS/NS.cs diff --git a/Source/OpenTK/Platform/MacOS/AglContext.cs b/Source/OpenTK/Platform/MacOS/AglContext.cs index 65bdd0af..d1de6664 100644 --- a/Source/OpenTK/Platform/MacOS/AglContext.cs +++ b/Source/OpenTK/Platform/MacOS/AglContext.cs @@ -464,64 +464,16 @@ namespace OpenTK.Platform.MacOS #region IGraphicsContextInternal Members - private const string Library = "libdl.dylib"; - - [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] - private static extern bool NSIsSymbolNameDefined(string s); - [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] - private static extern bool NSIsSymbolNameDefined(IntPtr s); - [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] - private static extern IntPtr NSLookupAndBindSymbol(string s); - [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] - private static extern IntPtr NSLookupAndBindSymbol(IntPtr s); - [DllImport(Library, EntryPoint = "NSAddressOfSymbol")] - private static extern IntPtr NSAddressOfSymbol(IntPtr symbol); - public override IntPtr GetAddress(string function) { - // Instead of allocating and combining strings in managed memory - // we do that directly in unmanaged memory. This way, we avoid - // 2 string allocations every time this function is called. - - // must add a '_' prefix and null-terminate the function name, - // hence we allocate +2 bytes - IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2); - try - { - Marshal.WriteByte(ptr, (byte)'_'); - for (int i = 0; i < function.Length; i++) - { - Marshal.WriteByte(ptr, i + 1, (byte)function[i]); - } - Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate - - IntPtr symbol = IntPtr.Zero; - if (NSIsSymbolNameDefined(ptr)) - { - symbol = NSLookupAndBindSymbol(ptr); - if (symbol != IntPtr.Zero) - symbol = NSAddressOfSymbol(symbol); - } - return symbol; - } - finally - { - Marshal.FreeHGlobal(ptr); - } + return NS.GetAddress(function); } public override IntPtr GetAddress(IntPtr function) { - if (!NSIsSymbolNameDefined(function)) - return IntPtr.Zero; - - IntPtr symbol = NSLookupAndBindSymbol(function); - if (symbol != IntPtr.Zero) - symbol = NSAddressOfSymbol(symbol); - - return symbol; + return NS.GetAddress(function); } - + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/NS.cs b/Source/OpenTK/Platform/MacOS/NS.cs new file mode 100644 index 00000000..945a7e25 --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/NS.cs @@ -0,0 +1,92 @@ +#region License +// +// NS.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.Runtime.InteropServices; + +namespace OpenTK.Platform.MacOS +{ + + internal class NS + { + const string Library = "libdl.dylib"; + + [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] + static extern bool NSIsSymbolNameDefined(string s); + [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] + static extern bool NSIsSymbolNameDefined(IntPtr s); + [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] + static extern IntPtr NSLookupAndBindSymbol(string s); + [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] + static extern IntPtr NSLookupAndBindSymbol(IntPtr s); + [DllImport(Library, EntryPoint = "NSAddressOfSymbol")] + static extern IntPtr NSAddressOfSymbol(IntPtr symbol); + + public static IntPtr GetAddress(string function) + { + // Instead of allocating and combining strings in managed memory + // we do that directly in unmanaged memory. This way, we avoid + // 2 string allocations every time this function is called. + + // must add a '_' prefix and null-terminate the function name, + // hence we allocate +2 bytes + IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2); + try + { + Marshal.WriteByte(ptr, (byte)'_'); + for (int i = 0; i < function.Length; i++) + { + Marshal.WriteByte(ptr, i + 1, (byte)function[i]); + } + Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate + + IntPtr symbol = GetAddress(ptr); + return symbol; + } + finally + { + Marshal.FreeHGlobal(ptr); + } + } + + public static IntPtr GetAddress(IntPtr function) + { + IntPtr symbol = IntPtr.Zero; + if (NSIsSymbolNameDefined(function)) + { + symbol = NSLookupAndBindSymbol(function); + if (symbol != IntPtr.Zero) + symbol = NSAddressOfSymbol(symbol); + } + return symbol; + } + } +} + From 8bcbb06f8eb9f89bbe35183646a102df6711339b Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:08:24 +0100 Subject: [PATCH 141/245] [Platform] Added internal CreateGetAddress() function CreateGetAddress() constructs a GraphicsContext.GetAddressDelegate that is suitable for the current platform. This can be used when combining OpenTK with an OpenGL context created through a third-party toolkit. --- Source/OpenTK/Platform/Utilities.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Source/OpenTK/Platform/Utilities.cs b/Source/OpenTK/Platform/Utilities.cs index 4ed38158..67c92a78 100644 --- a/Source/OpenTK/Platform/Utilities.cs +++ b/Source/OpenTK/Platform/Utilities.cs @@ -170,6 +170,32 @@ namespace OpenTK.Platform #endregion + #region CreateGetAddress + + internal static GraphicsContext.GetAddressDelegate CreateGetAddress() + { + GraphicsContext.GetAddressDelegate loader = null; + if (Configuration.RunningOnWindows) + { + loader = Platform.Windows.Wgl.GetProcAddress; + } + else if (Configuration.RunningOnX11) + { + loader = Platform.X11.Glx.GetProcAddress; + } + else if (Configuration.RunningOnMacOS) + { + loader = Platform.MacOS.NS.GetAddress; + } + else + { + throw new PlatformNotSupportedException(); + } + return loader; + } + + #endregion + #region --- Creating a Graphics Context --- /// From 5cebaccfca3c7f109610d6af0b335a97ac5cbe7e Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:09:59 +0100 Subject: [PATCH 142/245] [Dummy] Improved thread-safety; added entry-point loader DummyGLContext will now attempt to load OpenGL and OpenGL ES entry points when a suitable OpenGL context is current on the calling thread. This allows OpenTK to be used on contexts created through third-party toolkits. --- .../OpenTK/Platform/Dummy/DummyGLContext.cs | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/Source/OpenTK/Platform/Dummy/DummyGLContext.cs b/Source/OpenTK/Platform/Dummy/DummyGLContext.cs index 05853f79..bb6f1a2a 100644 --- a/Source/OpenTK/Platform/Dummy/DummyGLContext.cs +++ b/Source/OpenTK/Platform/Dummy/DummyGLContext.cs @@ -22,7 +22,8 @@ namespace OpenTK.Platform.Dummy /// internal sealed class DummyGLContext : DesktopGraphicsContext { - // This mode is not real. To receive a real mode we'd have to create a temporary context, which is not desirable! + readonly GraphicsContext.GetAddressDelegate Loader; + bool vsync; int swap_interval; static int handle_count; @@ -31,13 +32,20 @@ namespace OpenTK.Platform.Dummy #region --- Constructors --- public DummyGLContext() - : this(new ContextHandle(new IntPtr(++handle_count))) { + Handle = new ContextHandle( + new IntPtr(Interlocked.Increment( + ref handle_count))); } - - public DummyGLContext(ContextHandle handle) + + public DummyGLContext(ContextHandle handle, GraphicsContext.GetAddressDelegate loader) + : this() { - Handle = handle; + if (handle != ContextHandle.Zero) + { + Handle = handle; + } + Loader = loader; Mode = new GraphicsMode(new IntPtr(2), 32, 16, 0, 0, 0, 2, false); } @@ -45,15 +53,6 @@ namespace OpenTK.Platform.Dummy #region --- IGraphicsContext Members --- - public void CreateContext(bool direct, IGraphicsContext source) - { - if (Handle == ContextHandle.Zero) - { - ++handle_count; - Handle = new ContextHandle((IntPtr)handle_count); - } - } - public override void SwapBuffers() { } public override void MakeCurrent(IWindowInfo info) @@ -81,9 +80,15 @@ namespace OpenTK.Platform.Dummy get { return current_thread != null && current_thread == Thread.CurrentThread; } } - public override IntPtr GetAddress(string function) { return IntPtr.Zero; } + public override IntPtr GetAddress(string function) + { + return Loader(function); + } - public override IntPtr GetAddress(IntPtr function) { return IntPtr.Zero; } + public override IntPtr GetAddress(IntPtr function) + { + return IntPtr.Zero; + } public override int SwapInterval { @@ -101,7 +106,14 @@ namespace OpenTK.Platform.Dummy { } public override void LoadAll() - { } + { + new OpenTK.Graphics.OpenGL.GL().LoadEntryPoints(); + new OpenTK.Graphics.OpenGL4.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES10.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES11.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES20.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES30.GL().LoadEntryPoints(); + } #endregion From 04e6d9335a77a495a34f3ab168c849a918b518ae Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:14:59 +0100 Subject: [PATCH 143/245] [Graphics] Improved support for external OpenGL contexts When combining OpenTK with a third-party OpenGL toolkit, it is now possible to implement a suitable GetAddress() and GetCurrentContext() implementation in terms of the third-party toolkit. If no implementation is specified, then OpenTK will try to guess the most suitable implementation within its own platform backends. If no custom implementation is defined, and if no suitable implementation can be found, then OpenTK will throw a PlatformNotSupportedException. If a suitable implementation is found or defined, then OpenTK will attempt to load OpenGL entry points using that implementation. In this case third-party toolkit remains solely responsible for managing its context via its MakeCurrent(), SwapBuffers(), etc implementations. --- Source/OpenTK/Graphics/GraphicsContext.cs | 120 ++++++++++++---------- 1 file changed, 68 insertions(+), 52 deletions(-) diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs index 6c0b93f6..1fd39b2d 100644 --- a/Source/OpenTK/Graphics/GraphicsContext.cs +++ b/Source/OpenTK/Graphics/GraphicsContext.cs @@ -39,14 +39,13 @@ namespace OpenTK.Graphics /// public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal { + public delegate IntPtr GetAddressDelegate(string function); + public delegate ContextHandle GetCurrentContextDelegate(); + #region --- Fields --- IGraphicsContext implementation; // The actual render context implementation for the underlying platform. bool disposed; - // Indicates that this context was created through external means, e.g. Tao.Sdl or GLWidget#. - // In this case, We'll assume that the external program will manage the lifetime of this - // context - we'll not destroy it manually. - readonly bool IsExternal; bool check_errors = true; // Cache for the context handle. We need this for RemoveContext() // in case the user does not call Dispose(). When this happens, @@ -67,17 +66,6 @@ namespace OpenTK.Graphics #region --- Constructors --- - // Necessary to allow creation of dummy GraphicsContexts (see CreateDummyContext static method). - GraphicsContext(ContextHandle handle) - { - implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle); - - lock (SyncRoot) - { - AddContext(this); - } - } - /// /// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window. /// @@ -163,12 +151,65 @@ namespace OpenTK.Graphics } } + /// + /// Initializes a new instance of the class using + /// an external context handle that was created by a third-party library. + /// + /// + /// A valid, unique handle for an external OpenGL context, or ContextHandle.Zero to use the current context. + /// It is an error to specify a handle that has been created through OpenTK or that has been passed to OpenTK before. + /// + /// + /// A GetAddressDelegate instance that accepts the name of an OpenGL function and returns + /// a valid function pointer, or IntPtr.Zero if that function is not supported. This delegate should be + /// implemented using the same toolkit that created the OpenGL context (i.e. if the context was created with + /// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetProcAddress() to retrieve function + /// pointers.) + /// + /// + /// A GetCurrentContextDelegate instance that returns the handle of the current OpenGL context, + /// or IntPtr.Zero if no context is current on the calling thread. This delegate should be implemented + /// using the same toolkit that created the OpenGL context (i.e. if the context was created with + /// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetCurrentContext() to retrieve + /// the current context.) + /// + public GraphicsContext(ContextHandle handle, GetAddressDelegate getAddress, GetCurrentContextDelegate getCurrent) + { + if (getAddress == null || getCurrent == null) + throw new ArgumentNullException(); + + lock (SyncRoot) + { + // Replace a zero-handle by the current context, if any + if (handle == ContextHandle.Zero) + { + handle = getCurrent(); + } + + // Make sure this handle corresponds to a valid, unique OpenGL context + if (handle == ContextHandle.Zero) + { + throw new GraphicsContextMissingException(); + } + else if (available_contexts.ContainsKey(handle)) + { + throw new InvalidOperationException("Context handle has already been added"); + } + + // We have a valid handle for an external OpenGL context, wrap it into a + // DummyGLContext instance. + implementation = new Platform.Dummy.DummyGLContext(handle, getAddress); + GetCurrentContext = getCurrent ?? GetCurrentContext; + AddContext(this); + } + implementation.LoadAll(); + } + /// /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK. /// /// The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK. - /// The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo. - /// Occurs if handle is identical to a context already registered with OpenTK. + /// This parameter is reserved. public GraphicsContext(ContextHandle handle, IWindowInfo window) : this(handle, window, null, 1, 0, GraphicsContextFlags.Default) { } @@ -177,40 +218,14 @@ namespace OpenTK.Graphics /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK. /// /// The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK. - /// The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo. - /// A different context that shares resources with this instance, if any. - /// Pass null if the context is not shared or if this is the first GraphicsContext instruct you construct. - /// The major version of the context (e.g. "2" for "2.1"). - /// The minor version of the context (e.g. "1" for "2.1"). - /// A bitwise combination of that describe this context. - /// Occurs if handle is identical to a context already registered with OpenTK. + /// This parameter is reserved. + /// This parameter is reserved. + /// This parameter is reserved. + /// This parameter is reserved. + /// This parameter is reserved.. public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags) - : this(handle) - { - lock (SyncRoot) - { - IsExternal = true; - - if (handle == ContextHandle.Zero) - { - implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle); - } - else if (available_contexts.ContainsKey(handle)) - { - throw new GraphicsContextException("Context already exists."); - } - else - { - switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded) - { - case false: implementation = Factory.Default.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break; - case true: implementation = Factory.Embedded.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break; - } - } - - (this as IGraphicsContextInternal).LoadAll(); - } - } + : this(handle, Platform.Utilities.CreateGetAddress(), Factory.Default.CreateGetCurrentGraphicsContext()) + { } #endregion @@ -309,6 +324,7 @@ namespace OpenTK.Graphics /// Instances created by this method will not be functional. Instance methods will have no effect. /// This method requires that a context is current on the calling thread. /// + [Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")] public static GraphicsContext CreateDummyContext() { ContextHandle handle = GetCurrentContext(); @@ -326,12 +342,13 @@ namespace OpenTK.Graphics /// /// Instances created by this method will not be functional. Instance methods will have no effect. /// + [Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")] public static GraphicsContext CreateDummyContext(ContextHandle handle) { if (handle == ContextHandle.Zero) throw new ArgumentOutOfRangeException("handle"); - return new GraphicsContext(handle); + return new GraphicsContext(handle, (IWindowInfo)null); } #endregion @@ -352,7 +369,6 @@ namespace OpenTK.Graphics #region public static IGraphicsContext CurrentContext - internal delegate ContextHandle GetCurrentContextDelegate(); internal static GetCurrentContextDelegate GetCurrentContext; /// From 3bad2eefdc59c9b89c0556ba438c054d54d95bb3 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:17:56 +0100 Subject: [PATCH 144/245] [Examples] Added ExternalContext test This test uses SDL2 to create a window and an OpenGL context. It then uses OpenTK to render into the external SDL2 context. If everything is working correctly, a black window should appear and gradually turn white before disappearing. --- .../Examples/OpenTK/Test/ExternalContext.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 Source/Examples/OpenTK/Test/ExternalContext.cs diff --git a/Source/Examples/OpenTK/Test/ExternalContext.cs b/Source/Examples/OpenTK/Test/ExternalContext.cs new file mode 100644 index 00000000..cdb6d156 --- /dev/null +++ b/Source/Examples/OpenTK/Test/ExternalContext.cs @@ -0,0 +1,81 @@ +// This code was written for the OpenTK library and has been released +// to the Public Domain. +// It is provided "as is" without express or implied warranty of any kind. + +using System; +using System.Runtime.InteropServices; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.OpenGL; + +namespace Examples.Tests +{ + [Example("External Context Test", ExampleCategory.OpenTK, "OpenGL")] + class ExternalContext + { + public static void Main() + { + using (Toolkit.Init(new ToolkitOptions { Backend = PlatformBackend.PreferNative })) + { + var window = Sdl2.CreateWindow("Test", 0, 0, 640, 480, WindowFlags.AllowHighDpi | WindowFlags.OpenGL); + var context = Sdl2.CreateContext(window); + Sdl2.MakeCurrent(window, context); + + using (var dummy = new GraphicsContext(new ContextHandle(context), OpenTK.Platform.Utilities.CreateDummyWindowInfo())) + { + for (int i = 0; i < 100; i++) + { + Sdl2.PumpEvents(); + GL.ClearColor(i / 100.0f, i / 100.0f, i / 100.0f, i / 100.0f); + GL.Clear(ClearBufferMask.ColorBufferBit); + + Sdl2.SwapWindow(window); + } + + Sdl2.DestroyWindow(window); + } + } + } + } + + #region SDL2 bindings + + public enum WindowFlags + { + Default = 0, + OpenGL = 0x00000002, + AllowHighDpi = 0x00002000, + } + + static class Sdl2 + { + const string lib = "SDL2.dll"; + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)] + public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_CreateContext", ExactSpelling = true)] + public static extern IntPtr CreateContext(IntPtr window); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow", ExactSpelling = true)] + public static extern void DestroyWindow(IntPtr window); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)] + public static extern IntPtr GetCurrentContext(); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetProcAddress", ExactSpelling = true)] + public static extern IntPtr GetAddress(string name); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_MakeCurrent", ExactSpelling = true)] + public static extern int MakeCurrent(IntPtr window, IntPtr context); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_PumpEvents", ExactSpelling = true)] + public static extern void PumpEvents(); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_SwapWindow", ExactSpelling = true)] + public static extern void SwapWindow(IntPtr window); + } + + #endregion + +} From 3dbf575765cf90c4b3660ae8405e600291691a4f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 28 Dec 2013 01:23:39 +0100 Subject: [PATCH 145/245] [Build] Added new files to the relevant csproj scripts --- Source/Examples/OpenTK.Examples.csproj | 1 + Source/OpenTK/OpenTK.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/Source/Examples/OpenTK.Examples.csproj b/Source/Examples/OpenTK.Examples.csproj index 4871f240..c78290e6 100644 --- a/Source/Examples/OpenTK.Examples.csproj +++ b/Source/Examples/OpenTK.Examples.csproj @@ -565,6 +565,7 @@ Dependencies\x64\libSDL2.dylib + diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 7bddfcb0..92404b7f 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -777,6 +777,7 @@ + From 4005710b999e2d108ffcecd6183f97d9ce043815 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 09:23:33 +0100 Subject: [PATCH 146/245] [X11] Fixed current resolution detection. --- Source/OpenTK/Platform/X11/X11DisplayDevice.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/X11/X11DisplayDevice.cs b/Source/OpenTK/Platform/X11/X11DisplayDevice.cs index 86c28ab4..7f9e1ea3 100644 --- a/Source/OpenTK/Platform/X11/X11DisplayDevice.cs +++ b/Source/OpenTK/Platform/X11/X11DisplayDevice.cs @@ -221,7 +221,20 @@ namespace OpenTK.Platform.X11 int current_resolution_index = Functions.XRRConfigCurrentConfiguration(screen_config, out current_rotation); if (dev.Bounds == Rectangle.Empty) - dev.Bounds = new Rectangle(0, 0, available_res[current_resolution_index].Width, available_res[current_resolution_index].Height); + { + // We have added depths.Length copies of each resolution + // Adjust the return value of XRRGetScreenInfo to retrieve the correct resolution + int index = current_resolution_index * depths.Length; + + // Make sure we are within the bounds of the available_res array + if (index >= available_res.Count) + { + // If not, use the return value of XRRGetScreenInfo directly + index = current_resolution_index; + } + DisplayResolution current_resolution = available_res[index]; + dev.Bounds = new Rectangle(0, 0, current_resolution.Width, current_resolution.Height); + } dev.BitsPerPixel = current_depth; dev.RefreshRate = current_refresh_rate; dev.AvailableResolutions = available_res; From 0b8db7c40b99382f0bd4b7666f4b3fa266cc74ad Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 10:25:17 +0100 Subject: [PATCH 147/245] [X11] Removed unused variable --- Source/OpenTK/Platform/X11/X11Input.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs index 6f3bab0b..a8393beb 100644 --- a/Source/OpenTK/Platform/X11/X11Input.cs +++ b/Source/OpenTK/Platform/X11/X11Input.cs @@ -78,7 +78,6 @@ namespace OpenTK.Platform.X11 Marshal.PtrToStructure(keysym_ptr, keysyms); API.Free(keysym_ptr); - KeyboardDevice kb = new KeyboardDevice(); keyboard.Description = "Default X11 keyboard"; keyboard.NumberOfKeys = lastKeyCode - firstKeyCode + 1; keyboard.DeviceID = IntPtr.Zero; From e997ddf9c636bb773b84d5146dab03330105a6c8 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 13:39:43 +0100 Subject: [PATCH 148/245] [Input] Fixed GamePadState.SetAxis() implementation GamePadState.SetAxis() receives a GamePadAxes enumeration, which is a bitmask of the axes we wish to set. SetAxis now correctly decodes the bitmask to apply the values we are interested in. --- Source/OpenTK/Input/GamePadState.cs | 40 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index bd196982..76db81aa 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -111,26 +111,36 @@ namespace OpenTK.Input internal void SetAxis(GamePadAxes axis, short value) { - switch (axis) + if ((axis & GamePadAxes.LeftX) != 0) { - case GamePadAxes.LeftX: - left_stick_x = value; - break; + left_stick_x = value; + } - case GamePadAxes.LeftY: - left_stick_y = value; - break; + if ((axis & GamePadAxes.LeftY) != 0) + { + left_stick_y = value; + } - case GamePadAxes.RightX: - right_stick_x = value; - break; + if ((axis & GamePadAxes.RightX) != 0) + { + right_stick_x = value; + } - case GamePadAxes.RightY: - right_stick_y = value; - break; + if ((axis & GamePadAxes.RightY) != 0) + { + right_stick_y = value; + } - default: - throw new ArgumentOutOfRangeException("axis"); + if ((axis & GamePadAxes.LeftTrigger) != 0) + { + // Adjust from [-32768, 32767] to [0, 255] + left_trigger = (byte)((ev.Value - short.MinValue) >> 8); + } + + if ((axis & GamePadAxes.RightTrigger) != 0) + { + // Adjust from [-32768, 32767] to [0, 255] + right_trigger = (byte)((ev.Value - short.MinValue) >> 8); } } From 1b0a72472e76aaac99b4c9966d8faf285bf7c1ba Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 13:42:37 +0100 Subject: [PATCH 149/245] [SDL] Use TranslateAxis() to decode ControllerAxisEvent SDL GameControllerAxis and GamePadAxes are not interchangeable. The driver will now correctly interpret incoming SDL messages and update the GamePadState for the relevant axis. --- .../Platform/SDL2/Sdl2JoystickDriver.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 5d014d0b..6c08c77e 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -115,7 +115,7 @@ namespace OpenTK.Platform.SDL2 return controllers.ContainsKey(id); } - GamePadAxes TranslateAxes(IntPtr gamecontroller) + GamePadAxes GetBoundAxes(IntPtr gamecontroller) { GamePadAxes axes = 0; axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftX) ? GamePadAxes.LeftX : 0; @@ -127,7 +127,7 @@ namespace OpenTK.Platform.SDL2 return axes; } - Buttons TranslateButtons(IntPtr gamecontroller) + Buttons GetBoundButtons(IntPtr gamecontroller) { Buttons buttons = 0; buttons |= IsButtonBind(gamecontroller, GameControllerButton.A) ? Buttons.A : 0; @@ -162,6 +162,33 @@ namespace OpenTK.Platform.SDL2 return bind.BindType == GameControllerBindType.Button; } + GamePadAxes TranslateAxis(GameControllerAxis axis) + { + switch (axis) + { + case GameControllerAxis.LeftX: + return GamePadAxes.LeftX; + + case GameControllerAxis.LeftY: + return GamePadAxes.LeftY; + + case GameControllerAxis.RightX: + return GamePadAxes.RightX; + + case GameControllerAxis.RightY: + return GamePadAxes.RightY; + + case GameControllerAxis.TriggerLeft: + return GamePadAxes.LeftTrigger; + + case GameControllerAxis.TriggerRight: + return GamePadAxes.RightTrigger; + + default: + throw new ArgumentOutOfRangeException( + String.Format("[SDL] Unknown axis {0}", axis)); + } + } #endregion #region Public Members @@ -287,8 +314,8 @@ namespace OpenTK.Platform.SDL2 SDL.JoystickNumAxes(joystick); pad.Capabilities = new GamePadCapabilities( GamePadType.GamePad, - TranslateAxes(joystick), - TranslateButtons(joystick), + GetBoundAxes(joystick), + GetBoundButtons(joystick), true); pad.State.SetConnected(true); @@ -322,7 +349,7 @@ namespace OpenTK.Platform.SDL2 int id = ev.Which; if (IsControllerValid(id)) { - controllers[id].State.SetAxis((GamePadAxes)ev.Axis, ev.Value); + controllers[id].State.SetAxis(TranslateAxis(ev.Axis), ev.Value); } else { From 064a45e4a7356fc5de3ea093cc102d849c6d675c Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 13:49:26 +0100 Subject: [PATCH 150/245] [Input] Fixed incorrect variable name --- Source/OpenTK/Input/GamePadState.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index 76db81aa..096d3daf 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -134,13 +134,13 @@ namespace OpenTK.Input if ((axis & GamePadAxes.LeftTrigger) != 0) { // Adjust from [-32768, 32767] to [0, 255] - left_trigger = (byte)((ev.Value - short.MinValue) >> 8); + left_trigger = (byte)((value - short.MinValue) >> 8); } if ((axis & GamePadAxes.RightTrigger) != 0) { // Adjust from [-32768, 32767] to [0, 255] - right_trigger = (byte)((ev.Value - short.MinValue) >> 8); + right_trigger = (byte)((value - short.MinValue) >> 8); } } From a4366e52f52fadd9f865ec2314aad4cee10c247e Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 14:56:22 +0100 Subject: [PATCH 151/245] [Input] Added missing closing brace in ToString() message --- 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 096d3daf..179762b3 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -75,7 +75,7 @@ namespace OpenTK.Input public override string ToString() { return String.Format( - "{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}", + "{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}}}", ThumbSticks, Buttons, DPad, IsConnected); } From 52b8762593f1d8b58f9933df3a53df7985cf50fd Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 14:57:28 +0100 Subject: [PATCH 152/245] [SDL2] Fixed mapping of instance ids to device ids --- .../Platform/SDL2/Sdl2JoystickDriver.cs | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 6c08c77e..1d616027 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -56,9 +56,12 @@ namespace OpenTK.Platform.SDL2 } } + int last_controllers_instance = 0; + readonly List joysticks = new List(4); readonly Dictionary sdl_joyid_to_joysticks = new Dictionary(); - readonly Dictionary controllers = new Dictionary(); + readonly List controllers = new List(4); + readonly Dictionary sdl_instanceid_to_controllers = new Dictionary(); IList joysticks_readonly; bool disposed; @@ -112,7 +115,12 @@ namespace OpenTK.Platform.SDL2 bool IsControllerValid(int id) { - return controllers.ContainsKey(id); + return id >= 0 && id < controllers.Count; + } + + bool IsControllerInstanceValid(int instance_id) + { + return sdl_instanceid_to_controllers.ContainsKey(instance_id); } GamePadAxes GetBoundAxes(IntPtr gamecontroller) @@ -306,12 +314,20 @@ namespace OpenTK.Platform.SDL2 IntPtr handle = SDL.GameControllerOpen(id); if (handle != IntPtr.Zero) { + // The id variable here corresponds to a device_id between 0 and Sdl.NumJoysticks(). + // It is only used in the ADDED event. All other events use an instance_id which increases + // monotonically in each ADDED event. + // The idea is that device_id refers to the n-th connected joystick, whereas instance_id + // refers to the actual hardware device behind the n-th joystick. + // Yes, it's confusing. + int device_id = id; + int instance_id = last_controllers_instance++; + Sdl2GamePad pad = new Sdl2GamePad(handle); IntPtr joystick = SDL.GameControllerGetJoystick(handle); if (joystick != IntPtr.Zero) { - SDL.JoystickNumAxes(joystick); pad.Capabilities = new GamePadCapabilities( GamePadType.GamePad, GetBoundAxes(joystick), @@ -319,10 +335,17 @@ namespace OpenTK.Platform.SDL2 true); pad.State.SetConnected(true); - // Check whether the device has ever been connected before - if (!controllers.ContainsKey(id)) - controllers.Add(id, null); - controllers[id] = pad; + // Connect this device and add the relevant device index + if (controllers.Count <= id) + { + controllers.Add(pad); + } + else + { + controllers[device_id] = pad; + } + + sdl_instanceid_to_controllers.Add(instance_id, device_id); } else { @@ -332,11 +355,15 @@ namespace OpenTK.Platform.SDL2 break; case EventType.CONTROLLERDEVICEREMOVED: - if (IsControllerValid(id)) { - controllers[id].State.SetConnected(false); + int instance_id = id; + if (IsControllerInstanceValid(id)) + { + controllers[id].State.SetConnected(false); + sdl_instanceid_to_controllers.Remove(instance_id); + } + break; } - break; case EventType.CONTROLLERDEVICEREMAPPED: // Todo: what should we do in this case? @@ -346,27 +373,29 @@ namespace OpenTK.Platform.SDL2 public void ProcessControllerEvent(ControllerAxisEvent ev) { - int id = ev.Which; - if (IsControllerValid(id)) + int instance_id = ev.Which; + if (IsControllerInstanceValid(instance_id)) { - controllers[id].State.SetAxis(TranslateAxis(ev.Axis), ev.Value); + int id = sdl_instanceid_to_controllers[instance_id]; + controllers[id].State.SetAxis(TranslateAxis(ev.Axis), ev.Value); } else { - Debug.Print("[SDL2] Invalid game controller handle {0} in {1}", id, ev.Type); + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type); } } public void ProcessControllerEvent(ControllerButtonEvent ev) { - int id = ev.Which; - if (IsControllerValid(id)) + int instance_id = ev.Which; + if (IsControllerInstanceValid(instance_id)) { + int id = sdl_instanceid_to_controllers[instance_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); + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type); } } From 6faa58aac3cf06660bdce3467bae26dab8194427 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 30 Dec 2013 15:24:48 +0100 Subject: [PATCH 153/245] [SDL2] Fixed joystick hotplugging SDL2 uses a weird system of device ids and instance ids to report joystick events, where the ADDED event uses a device id and the rest use instance ids. The SDL2 joystick driver is now fixed to correctly distinguish between the two, which fixes hotplugging support for joystick devices. --- .../Platform/SDL2/Sdl2JoystickDriver.cs | 88 +++++++++++++------ 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 1d616027..e284cc59 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -56,10 +56,11 @@ namespace OpenTK.Platform.SDL2 } } + int last_joystick_instance = 0; int last_controllers_instance = 0; readonly List joysticks = new List(4); - readonly Dictionary sdl_joyid_to_joysticks = new Dictionary(); + readonly Dictionary sdl_instanceid_to_joysticks = new Dictionary(); readonly List controllers = new List(4); readonly Dictionary sdl_instanceid_to_controllers = new Dictionary(); @@ -113,6 +114,11 @@ namespace OpenTK.Platform.SDL2 return id >= 0 && id < joysticks.Count; } + bool IsJoystickInstanceValid(int instance_id) + { + return sdl_instanceid_to_joysticks.ContainsKey(instance_id); + } + bool IsControllerValid(int id) { return id >= 0 && id < controllers.Count; @@ -217,28 +223,41 @@ namespace OpenTK.Platform.SDL2 IntPtr handle = SDL.JoystickOpen(id); if (handle != IntPtr.Zero) { + int device_id = id; + int instance_id = last_joystick_instance++; + JoystickDevice joystick = OpenJoystick(id); if (joystick != null) { joystick.Details.IsConnected = true; - if (!sdl_joyid_to_joysticks.ContainsKey(id)) + if (device_id < joysticks.Count) { - sdl_joyid_to_joysticks.Add(id, joysticks.Count); - joysticks.Add(null); + joysticks[device_id] = joystick; } - int index = sdl_joyid_to_joysticks[id]; - joysticks[index] = joystick; + else + { + joysticks.Add(joystick); + } + + sdl_instanceid_to_joysticks.Add(instance_id, device_id); } } } break; case EventType.JOYDEVICEREMOVED: + if (IsJoystickInstanceValid(id)) { - int index = sdl_joyid_to_joysticks[id]; - JoystickDevice joystick = (JoystickDevice)joysticks[index]; + int instance_id = sdl_instanceid_to_joysticks[id]; + sdl_instanceid_to_joysticks.Remove(id); + + JoystickDevice joystick = (JoystickDevice)joysticks[instance_id]; joystick.Details.IsConnected = false; } + else + { + Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type); + } break; } } @@ -246,9 +265,9 @@ namespace OpenTK.Platform.SDL2 public void ProcessJoystickEvent(JoyAxisEvent ev) { int id = ev.Which; - if (IsJoystickValid(id)) + if (IsJoystickInstanceValid(id)) { - int index = sdl_joyid_to_joysticks[id]; + int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; float value = ev.Value * RangeMultiplier; joystick.SetAxis((JoystickAxis)ev.Axis, value); @@ -262,9 +281,9 @@ namespace OpenTK.Platform.SDL2 public void ProcessJoystickEvent(JoyBallEvent ev) { int id = ev.Which; - if (IsJoystickValid(id)) + if (IsJoystickInstanceValid(id)) { - int index = sdl_joyid_to_joysticks[id]; + int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; // Todo: does it make sense to support balls? } @@ -277,9 +296,9 @@ namespace OpenTK.Platform.SDL2 public void ProcessJoystickEvent(JoyButtonEvent ev) { int id = ev.Which; - if (IsJoystickValid(id)) + if (IsJoystickInstanceValid(id)) { - int index = sdl_joyid_to_joysticks[id]; + int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed); } @@ -292,9 +311,9 @@ namespace OpenTK.Platform.SDL2 public void ProcessJoystickEvent(JoyHatEvent ev) { int id = ev.Which; - if (IsJoystickValid(id)) + if (IsJoystickInstanceValid(id)) { - int index = sdl_joyid_to_joysticks[id]; + int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; // Todo: map hat to an extra axis } @@ -307,6 +326,11 @@ namespace OpenTK.Platform.SDL2 public void ProcessControllerEvent(ControllerDeviceEvent ev) { int id = ev.Which; + if (id < 0) + { + Debug.Print("[SDL2] Invalid controller id {0} in {1}", id, ev.Type); + return; + } switch (ev.Type) { @@ -355,18 +379,26 @@ namespace OpenTK.Platform.SDL2 break; case EventType.CONTROLLERDEVICEREMOVED: + if (IsControllerInstanceValid(id)) { - int instance_id = id; - if (IsControllerInstanceValid(id)) - { - controllers[id].State.SetConnected(false); - sdl_instanceid_to_controllers.Remove(instance_id); - } - break; + controllers[id].State.SetConnected(false); + sdl_instanceid_to_controllers.Remove(id); } + else + { + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type); + } + break; case EventType.CONTROLLERDEVICEREMAPPED: - // Todo: what should we do in this case? + if (IsControllerInstanceValid(id)) + { + // Todo: what should we do in this case? + } + else + { + Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type); + } break; } } @@ -453,7 +485,7 @@ namespace OpenTK.Platform.SDL2 if (IsJoystickValid(index)) { JoystickDevice joystick = - (JoystickDevice)joysticks[sdl_joyid_to_joysticks[index]]; + (JoystickDevice)joysticks[index]; for (int i = 0; i < joystick.Axis.Count; i++) { @@ -465,7 +497,7 @@ namespace OpenTK.Platform.SDL2 state.SetButton(JoystickButton.Button0 + i, joystick.Button[i]); } - state.SetIsConnected(true); + state.SetIsConnected(joystick.Details.IsConnected); } return state; @@ -476,12 +508,12 @@ namespace OpenTK.Platform.SDL2 if (IsJoystickValid(index)) { JoystickDevice joystick = - (JoystickDevice)joysticks[sdl_joyid_to_joysticks[index]]; + (JoystickDevice)joysticks[index]; return new JoystickCapabilities( joystick.Axis.Count, joystick.Button.Count, - true); + joystick.Details.IsConnected); } return new JoystickCapabilities(); } From 2d303a6884187895af2ec8a6e82f7dcbe458b25f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 16:07:06 +0100 Subject: [PATCH 154/245] [Input] Buttons should have [Flags] attribute --- Source/OpenTK/Input/Buttons.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Input/Buttons.cs b/Source/OpenTK/Input/Buttons.cs index 5d3889e7..878c738f 100644 --- a/Source/OpenTK/Input/Buttons.cs +++ b/Source/OpenTK/Input/Buttons.cs @@ -30,8 +30,9 @@ namespace OpenTK.Input /// /// Enumerates available buttons for a canonical GamePad device. /// - public enum Buttons - { + [Flags] + public enum Buttons + { /// /// DPad up direction button /// From 9beb396c9ed489dbf8784ffa8dfd32cf7c987212 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 17:08:42 +0100 Subject: [PATCH 155/245] [SDL2] Explicitly enable joystick and gamepad events --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 55 +++++++++++++------ .../OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 3 + 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 5aa6dad4..5d0cde57 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -116,22 +116,11 @@ 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); + #region GameContoller - /// - /// 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_GameControllerEventState", ExactSpelling = true)] + public static extern EventState GameControllerEventState(EventState state); [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetAxis", ExactSpelling = true)] @@ -177,6 +166,27 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetJoystick", ExactSpelling = true)] public static extern IntPtr GameControllerGetJoystick(IntPtr gamecontroller); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetCurrentDisplayMode", ExactSpelling = true)] + public static extern int GetCurrentDisplayMode(int displayIndex, out DisplayMode mode); + + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerName", ExactSpelling = true)] + static extern IntPtr GameControllerNameInternal(IntPtr gamecontroller); + + /// + /// Return the name for an openend game controller instance. + /// + /// The name of the game controller name. + /// Pointer to a game controller instance returned by GameControllerOpen. + public static string GameControllerName(IntPtr gamecontroller) + { + unsafe + { + return new string((sbyte*)GameControllerNameInternal(gamecontroller)); + } + } + /// /// Opens a game controller for use. /// @@ -189,9 +199,7 @@ namespace OpenTK.Platform.SDL2 [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); + #endregion [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetDisplayBounds", ExactSpelling = true)] @@ -280,6 +288,10 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickClose", ExactSpelling = true)] public static extern void JoystickClose(IntPtr joystick); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickEventState", ExactSpelling = true)] + public static extern EventState JoystickEventState(EventState enabled); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetAxis", ExactSpelling = true)] public static extern short JoystickGetAxis(IntPtr joystick, int axis); @@ -545,6 +557,13 @@ namespace OpenTK.Platform.SDL2 ES = 0x0004 } + enum EventState + { + Query = -1, + Ignore = 0, + Enable = 1 + } + enum EventType { FIRSTEVENT = 0, diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index 023af9a2..f5df4654 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -55,6 +55,9 @@ namespace OpenTK.Platform.SDL2 { lock (SDL.Sync) { + SDL.GameControllerEventState(EventState.Enable); + SDL.JoystickEventState(EventState.Enable); + EventFilterDelegate = Marshal.GetFunctionPointerForDelegate(EventFilterDelegate_GCUnsafe); driver_handle = new IntPtr(count++); DriverHandles.Add(driver_handle, this); From 43ef78f222fa90185290989610d0107f3ed5cf40 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 17:09:20 +0100 Subject: [PATCH 156/245] [SDL2] Fix issue where changes to mutable struct were lost --- Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index e284cc59..c40562e5 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -47,8 +47,8 @@ namespace OpenTK.Platform.SDL2 class Sdl2GamePad { public IntPtr Handle { get; private set; } - public GamePadState State { get; set; } - public GamePadCapabilities Capabilities { get; set; } + public GamePadState State; + public GamePadCapabilities Capabilities; public Sdl2GamePad(IntPtr handle) { From 9c8a5f5028c299b228e05cad586919ca4db5ca59 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 17:15:49 +0100 Subject: [PATCH 157/245] [SDL2] Fixed GameControllerButton to Buttons translation --- .../Platform/SDL2/Sdl2JoystickDriver.cs | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index c40562e5..70f08ca9 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -203,6 +203,61 @@ namespace OpenTK.Platform.SDL2 String.Format("[SDL] Unknown axis {0}", axis)); } } + + Buttons TranslateButton(GameControllerButton button) + { + switch (button) + { + case GameControllerButton.A: + return Buttons.A; + + case GameControllerButton.B: + return Buttons.B; + + case GameControllerButton.X: + return Buttons.X; + + case GameControllerButton.Y: + return Buttons.Y; + + case GameControllerButton.LEFTSHOULDER: + return Buttons.LeftShoulder; + + case GameControllerButton.RIGHTSHOULDER: + return Buttons.RightShoulder; + + case GameControllerButton.LEFTSTICK: + return Buttons.LeftStick; + + case GameControllerButton.RIGHTSTICK: + return Buttons.RightStick; + + case GameControllerButton.DPAD_UP: + return Buttons.DPadUp; + + case GameControllerButton.DPAD_DOWN: + return Buttons.DPadDown; + + case GameControllerButton.DPAD_LEFT: + return Buttons.DPadLeft; + + case GameControllerButton.DPAD_RIGHT: + return Buttons.DPadRight; + + case GameControllerButton.BACK: + return Buttons.Back; + + case GameControllerButton.START: + return Buttons.Start; + + case GameControllerButton.GUIDE: + return Buttons.BigButton; + + default: + Debug.Print("[SDL2] Unknown button {0}", button); + return 0; + } + } #endregion #region Public Members @@ -423,7 +478,7 @@ namespace OpenTK.Platform.SDL2 if (IsControllerInstanceValid(instance_id)) { int id = sdl_instanceid_to_controllers[instance_id]; - controllers[id].State.SetButton((Buttons)ev.Button, ev.State == State.Pressed); + controllers[id].State.SetButton(TranslateButton(ev.Button), ev.State == State.Pressed); } else { From d4348c50837157cc62d27ca37a5c80076f0ba325 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 30 Dec 2013 17:22:45 +0100 Subject: [PATCH 158/245] [SDL2] Fixed joystick/controller hotplugging support --- Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 70f08ca9..b0786d02 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -303,11 +303,13 @@ namespace OpenTK.Platform.SDL2 case EventType.JOYDEVICEREMOVED: if (IsJoystickInstanceValid(id)) { - int instance_id = sdl_instanceid_to_joysticks[id]; - sdl_instanceid_to_joysticks.Remove(id); + int instance_id = id; + int device_id = sdl_instanceid_to_joysticks[instance_id]; - JoystickDevice joystick = (JoystickDevice)joysticks[instance_id]; + JoystickDevice joystick = (JoystickDevice)joysticks[device_id]; joystick.Details.IsConnected = false; + + sdl_instanceid_to_joysticks.Remove(instance_id); } else { @@ -436,8 +438,11 @@ namespace OpenTK.Platform.SDL2 case EventType.CONTROLLERDEVICEREMOVED: if (IsControllerInstanceValid(id)) { - controllers[id].State.SetConnected(false); - sdl_instanceid_to_controllers.Remove(id); + int instance_id = id; + int device_id = sdl_instanceid_to_controllers[instance_id]; + + controllers[device_id].State.SetConnected(false); + sdl_instanceid_to_controllers.Remove(device_id); } else { From 7d4d116ce9efd280e4d21533f85998203c3221ad Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 31 Dec 2013 01:01:52 +0100 Subject: [PATCH 159/245] [Input] Renamed GamePadMapping to GamePadMap --- Source/OpenTK/Input/GamePadMap.cs | 148 ++++++++++++++++++++++++++ Source/OpenTK/Input/GamePadMapping.cs | 39 ------- Source/OpenTK/OpenTK.csproj | 2 +- 3 files changed, 149 insertions(+), 40 deletions(-) create mode 100644 Source/OpenTK/Input/GamePadMap.cs delete mode 100644 Source/OpenTK/Input/GamePadMapping.cs diff --git a/Source/OpenTK/Input/GamePadMap.cs b/Source/OpenTK/Input/GamePadMap.cs new file mode 100644 index 00000000..c67ca8c2 --- /dev/null +++ b/Source/OpenTK/Input/GamePadMap.cs @@ -0,0 +1,148 @@ +#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 +{ + enum MapType + { + Axis = 0, + Button = 1 + } + + class MapItem + { + MapType map_type; + Nullable map_button; + Nullable map_axis; + + public MapItem(JoystickAxis axis) + { + Type = MapType.Axis; + Axis = axis; + } + + public MapItem(JoystickButton button) + { + Type = MapType.Button; + Button = button; + } + + public MapType Type + { + get { return map_type; } + private set { map_type = value; } + } + + public Nullable Axis + { + get { return map_axis; } + private set { map_axis = value; } + } + + public Nullable Button + { + get { return map_button; } + private set { map_button = value; } + } + } + + class GamePadMap + { + static readonly GamePadMap default_map; + + MapItem a, b, x, y; + MapItem lshoulder, rshoulder; + MapItem lstick, rstick; + MapItem start, back; + MapItem home; + MapItem axis_lx, axis_ly; + MapItem axis_rx, axis_ry; + MapItem ltrigger, rtrigger; + MapItem dpad_u, dpad_d; + MapItem dpad_l, dpad_r; + + public MapItem A { get { return a; } set { a = value; } } + public MapItem B { get { return b; } set { b = value; } } + public MapItem X { get { return x; } set { x = value; } } + public MapItem Y { get { return y; } set { y = value; } } + public MapItem LeftShoulder { get { return lshoulder; } set { lshoulder = value; } } + public MapItem RightShoulder { get { return rshoulder; } set { rshoulder = value; } } + public MapItem LeftStick { get { return lstick; } set { lstick = value; } } + public MapItem RightStick { get { return rstick; } set { rstick = value; } } + public MapItem Start { get { return start; } set { start = value; } } + public MapItem Back { get { return back; } set { back = value; } } + public MapItem BigButton { get { return home; } set { home = value; } } + public MapItem LeftAxisX { get { return axis_lx; } set { axis_lx = value; } } + public MapItem LeftAxisY { get { return axis_ly; } set { axis_ly = value; } } + public MapItem RightAxisX { get { return axis_rx; } set { axis_rx = value; } } + public MapItem RightAxisY { get { return axis_ry; } set { axis_ry = value; } } + public MapItem LeftTrigger { get { return ltrigger; } set { ltrigger = value; } } + public MapItem RightTrigger { get { return rtrigger; } set { rtrigger = value; } } + public MapItem DPadUp { get { return dpad_u; } set { dpad_u = value; } } + public MapItem DPadDown { get { return dpad_d; } set { dpad_d = value; } } + public MapItem DPadLeft { get { return dpad_l; } set { dpad_l = value; } } + public MapItem DPadRight { get { return dpad_r; } set { dpad_r = value; } } + + static GamePadMap() + { + GamePadMap map = new GamePadMap(); + map.A = new MapItem(JoystickButton.Button0); + map.B = new MapItem(JoystickButton.Button1); + map.X = new MapItem(JoystickButton.Button2); + map.Y = new MapItem(JoystickButton.Button3); + map.LeftShoulder = new MapItem(JoystickButton.Button4); + map.RightShoulder = new MapItem(JoystickButton.Button5); + map.LeftStick = new MapItem(JoystickButton.Button6); + map.RightStick = new MapItem(JoystickButton.Button7); + map.Start = new MapItem(JoystickButton.Button8); + map.Back = new MapItem(JoystickButton.Button9); + map.BigButton = new MapItem(JoystickButton.Button10); + map.DPadUp = new MapItem(JoystickButton.Button11); + map.DPadDown = new MapItem(JoystickButton.Button12); + map.DPadLeft = new MapItem(JoystickButton.Button13); + map.DPadRight = new MapItem(JoystickButton.Button14); + map.LeftAxisX = new MapItem(JoystickAxis.Axis0); + map.LeftAxisY = new MapItem(JoystickAxis.Axis1); + map.RightAxisX = new MapItem(JoystickAxis.Axis2); + map.RightAxisY = new MapItem(JoystickAxis.Axis3); + map.LeftTrigger = new MapItem(JoystickAxis.Axis4); + map.RightTrigger = new MapItem(JoystickAxis.Axis5); + default_map = map; + } + + public static GamePadMap Default + { + get { return default_map; } + } + } +} diff --git a/Source/OpenTK/Input/GamePadMapping.cs b/Source/OpenTK/Input/GamePadMapping.cs deleted file mode 100644 index 9ddca29f..00000000 --- a/Source/OpenTK/Input/GamePadMapping.cs +++ /dev/null @@ -1,39 +0,0 @@ -#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 a378e483..af108d7a 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -133,7 +133,6 @@ - @@ -792,6 +791,7 @@ + From ef7f31099d61416ff5aafd6c9cd83ee3d5a3ffb4 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 31 Dec 2013 01:02:07 +0100 Subject: [PATCH 160/245] [Input] Removed unnecessary #region --- Source/OpenTK/Input/JoystickButton.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/OpenTK/Input/JoystickButton.cs b/Source/OpenTK/Input/JoystickButton.cs index cf3a9ca6..e8daf6ed 100644 --- a/Source/OpenTK/Input/JoystickButton.cs +++ b/Source/OpenTK/Input/JoystickButton.cs @@ -33,8 +33,6 @@ using System.Text; namespace OpenTK.Input { - #region JoystickButton - /// /// Defines available JoystickDevice buttons. /// @@ -73,6 +71,4 @@ namespace OpenTK.Input /// The sixteenth button of the JoystickDevice. Button15, } - - #endregion } From 1687518ef5f419a916cfb8b582a512912c24b41d Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 31 Dec 2013 11:46:40 +0100 Subject: [PATCH 161/245] [SDL2] Add compile-time check for SDL2 GameController vs MappedGamePadDriver --- .../OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 2 + .../Platform/SDL2/Sdl2JoystickDriver.cs | 40 ++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index f5df4654..5e88fd6d 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -126,6 +126,7 @@ namespace OpenTK.Platform.SDL2 driver.joystick_driver.ProcessJoystickEvent(ev.JoyHat); break; +#if USE_SDL2_GAMECONTROLLER case EventType.CONTROLLERDEVICEADDED: case EventType.CONTROLLERDEVICEREMOVED: driver.joystick_driver.ProcessControllerEvent(ev.ControllerDevice); @@ -139,6 +140,7 @@ namespace OpenTK.Platform.SDL2 case EventType.CONTROLLERBUTTONUP: driver.joystick_driver.ProcessControllerEvent(ev.ControllerButton); break; +#endif } } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index b0786d02..01c72b82 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -35,6 +35,8 @@ namespace OpenTK.Platform.SDL2 class Sdl2JoystickDriver : IJoystickDriver, IJoystickDriver2, IGamePadDriver, IDisposable { const float RangeMultiplier = 1.0f / 32768.0f; + readonly MappedGamePadDriver gamepad_driver = new MappedGamePadDriver(); + bool disposed; struct Sdl2JoystickDetails { @@ -44,6 +46,15 @@ namespace OpenTK.Platform.SDL2 public bool IsConnected { get; set; } } + // For IJoystickDriver2 implementation + int last_joystick_instance = 0; + readonly List joysticks = new List(4); + readonly Dictionary sdl_instanceid_to_joysticks = new Dictionary(); + + // For IJoystickDriver implementation + IList joysticks_readonly; + +#if USE_SDL2_GAMECONTROLLER class Sdl2GamePad { public IntPtr Handle { get; private set; } @@ -56,16 +67,11 @@ namespace OpenTK.Platform.SDL2 } } - int last_joystick_instance = 0; int last_controllers_instance = 0; - - readonly List joysticks = new List(4); - readonly Dictionary sdl_instanceid_to_joysticks = new Dictionary(); readonly List controllers = new List(4); readonly Dictionary sdl_instanceid_to_controllers = new Dictionary(); +#endif - IList joysticks_readonly; - bool disposed; public Sdl2JoystickDriver() { @@ -119,6 +125,7 @@ namespace OpenTK.Platform.SDL2 return sdl_instanceid_to_joysticks.ContainsKey(instance_id); } +#if USE_SDL2_GAMECONTROLLER bool IsControllerValid(int id) { return id >= 0 && id < controllers.Count; @@ -258,6 +265,8 @@ namespace OpenTK.Platform.SDL2 return 0; } } +#endif + #endregion #region Public Members @@ -380,6 +389,7 @@ namespace OpenTK.Platform.SDL2 } } +#if USE_SDL2_GAMECONTROLLER public void ProcessControllerEvent(ControllerDeviceEvent ev) { int id = ev.Which; @@ -490,6 +500,7 @@ namespace OpenTK.Platform.SDL2 Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type); } } +#endif #endregion @@ -512,6 +523,7 @@ namespace OpenTK.Platform.SDL2 #region IGamePadDriver Members +#if USE_SDL2_GAMECONTOLLER public GamePadCapabilities GetCapabilities(int index) { if (IsControllerValid(index)) @@ -534,6 +546,22 @@ namespace OpenTK.Platform.SDL2 { return String.Empty; } +#else + public GamePadCapabilities GetCapabilities(int index) + { + return gamepad_driver.GetCapabilities(index); + } + + public GamePadState GetState(int index) + { + return gamepad_driver.GetState(index); + } + + public string GetName(int index) + { + return gamepad_driver.GetName(index); + } +#endif #endregion From cd143af60a7ff875fc3ebf38d6d2b35ed0b12adf Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 31 Dec 2013 14:08:28 +0100 Subject: [PATCH 162/245] [SDL2] Added JoystickGetGUID method --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 5d0cde57..8ad25480 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -300,6 +300,10 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetButton", ExactSpelling = true)] public static extern byte JoystickGetButton(IntPtr joystick, int button); + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetGuid", ExactSpelling = true)] + public static extern JoystickGuid JoystickGetGUID(IntPtr joystick); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickName", ExactSpelling = true)] static extern IntPtr JoystickNameInternal(IntPtr joystick); @@ -1404,6 +1408,26 @@ namespace OpenTK.Platform.SDL2 byte padding2; } + struct JoystickGuid + { + unsafe fixed byte data[16]; + + public Guid ToGuid() + { + byte[] bytes = new byte[16]; + + unsafe + { + fixed (byte* pdata = data) + { + Marshal.Copy(new IntPtr(pdata), bytes, 0, bytes.Length); + } + } + + return new Guid(bytes); + } + } + struct KeyboardEvent { public EventType Type; From b9a8e365dea405675ec79caa6e5089ae0010e535 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 31 Dec 2013 14:09:17 +0100 Subject: [PATCH 163/245] [Input] Added IJoystickDriver2.GetGuid() API --- Source/OpenTK/Input/IJoystickDriver2.cs | 1 + Source/OpenTK/Input/Joystick.cs | 5 +++++ Source/OpenTK/Platform/MacOS/HIDInput.cs | 5 +++++ .../OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 17 ++++++++++++++++- Source/OpenTK/Platform/Windows/WinMMJoystick.cs | 12 ++++++++++++ Source/OpenTK/Platform/X11/X11Joystick.cs | 5 +++++ 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Input/IJoystickDriver2.cs b/Source/OpenTK/Input/IJoystickDriver2.cs index 1359d1de..8e536aaf 100644 --- a/Source/OpenTK/Input/IJoystickDriver2.cs +++ b/Source/OpenTK/Input/IJoystickDriver2.cs @@ -37,5 +37,6 @@ namespace OpenTK.Input { JoystickState GetState(int index); JoystickCapabilities GetCapabilities(int index); + Guid GetGuid(int index); } } diff --git a/Source/OpenTK/Input/Joystick.cs b/Source/OpenTK/Input/Joystick.cs index 378b11f4..2b10cfb9 100644 --- a/Source/OpenTK/Input/Joystick.cs +++ b/Source/OpenTK/Input/Joystick.cs @@ -48,6 +48,11 @@ namespace OpenTK.Input return implementation.GetState(index); } + internal static Guid GetGuid(int index) + { + return implementation.GetGuid(index); + } + //public string GetName(int index) //{ // return implementation.GetName(index); diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 0967afef..9090e703 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -381,6 +381,11 @@ namespace OpenTK.Platform.MacOS return new JoystickCapabilities(); } + Guid IJoystickDriver2.GetGuid(int index) + { + return new Guid(); + } + #endregion #region NativeMethods diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 01c72b82..25b6b253 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -38,9 +38,10 @@ namespace OpenTK.Platform.SDL2 readonly MappedGamePadDriver gamepad_driver = new MappedGamePadDriver(); bool disposed; - struct Sdl2JoystickDetails + class Sdl2JoystickDetails { public IntPtr Handle { get; set; } + public Guid Guid { get; set; } public int HatCount { get; set; } public int BallCount { get; set; } public bool IsConnected { get; set; } @@ -99,6 +100,7 @@ namespace OpenTK.Platform.SDL2 joystick = new JoystickDevice(id, num_axes, num_buttons); joystick.Description = SDL.JoystickName(handle); joystick.Details.Handle = handle; + joystick.Details.Guid = SDL.JoystickGetGUID(handle).ToGuid(); joystick.Details.HatCount = num_hats; joystick.Details.BallCount = num_balls; @@ -606,6 +608,19 @@ namespace OpenTK.Platform.SDL2 return new JoystickCapabilities(); } + Guid IJoystickDriver2.GetGuid(int index) + { + Guid guid = new Guid(); + if (IsJoystickValid(index)) + { + JoystickDevice joystick = + (JoystickDevice)joysticks[index]; + + return joystick.Details.Guid; + } + return guid; + } + #endregion #region IDisposable Members diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs index fa84aba1..313abdd9 100644 --- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs +++ b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs @@ -333,6 +333,18 @@ namespace OpenTK.Platform.Windows return state; } + public Guid GetGuid(int index) + { + Guid guid = new Guid(); + + if (IsValid(index)) + { + // Todo: implement WinMM Guid retrieval + } + + return guid; + } + #endregion #region IDisposable diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index 36fac9f2..5f76dd54 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -290,6 +290,11 @@ namespace OpenTK.Platform.X11 throw new NotImplementedException(); } + Guid IJoystickDriver2.GetGuid(int index) + { + throw new NotImplementedException(); + } + #endregion } } From 5e75fac056286b1ff742205a7e8535e14f4846e1 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:06:47 +0100 Subject: [PATCH 164/245] [Input] Added configuration db for GamePads The database is based on the SDL bindings found at https://hg.libsdl.org/SDL/file/b744b3f8754b/src/joystick/SDL_gamecontrol lerdb.h --- .../Input/GamePadConfigurationDatabase.cs | 110 ++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 1 + 2 files changed, 111 insertions(+) create mode 100644 Source/OpenTK/Input/GamePadConfigurationDatabase.cs diff --git a/Source/OpenTK/Input/GamePadConfigurationDatabase.cs b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs new file mode 100644 index 00000000..95205993 --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs @@ -0,0 +1,110 @@ +#region License +// +// GamePadConfigurationDatabase.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; + +namespace OpenTK.Input +{ + static class GamePadConfigurationDatabase + { + internal static readonly Dictionary Configurations = new Dictionary(); + + static GamePadConfigurationDatabase() + { + // Configuration database copied from SDL + + #region License + // Simple DirectMedia Layer + // Copyright (C) 1997-2013 Sam Lantinga + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + #endregion + + // Default configuration, used when no suitable configuration is available + Add("00000000000000000000000000000000,Unknown Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + + // Windows + Add("341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); + Add("6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); // Guide button doesn't seem to be sent in DInput mode. + Add("88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,"); + Add("4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); + Add("25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,"); + Add("xinput00000000000000000000000000,X360 Controller,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,"); + + // Mac OS X + Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); + Add("6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* Guide button doesn't seem to be sent in DInput mode. */ + Add("6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,"); + Add("6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* This includes F710 in DInput mode and the "Logitech Cordless RumblePad 2"); at the very least. */ + Add("4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); + Add("5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,"); + + // Linux + Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); + Add("03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,"); + Add("030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + Add("030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); /* Guide button doesn't seem to be sent in DInput mode. */ + Add("030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); + Add("03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + Add("030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); + } + + static void Add(string config) + { + Configurations.Add( + new Guid(config.Substring(0, 32)), + config); + } + } +} + diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index af108d7a..ec3d8aff 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -792,6 +792,7 @@ + From f0fbb26efed299b5c01ee4eb87f0413380023e1c Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:07:21 +0100 Subject: [PATCH 165/245] [Input] Added ability to parse SDL configuration db strings --- Source/OpenTK/Input/GamePadMap.cs | 195 +++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Input/GamePadMap.cs b/Source/OpenTK/Input/GamePadMap.cs index c67ca8c2..c9cfaf1e 100644 --- a/Source/OpenTK/Input/GamePadMap.cs +++ b/Source/OpenTK/Input/GamePadMap.cs @@ -29,14 +29,16 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; namespace OpenTK.Input { enum MapType { - Axis = 0, - Button = 1 + Unmapped = 0, + Axis, + Button } class MapItem @@ -45,6 +47,10 @@ namespace OpenTK.Input Nullable map_button; Nullable map_axis; + public MapItem() + { + } + public MapItem(JoystickAxis axis) { Type = MapType.Axis; @@ -80,6 +86,8 @@ namespace OpenTK.Input { static readonly GamePadMap default_map; + Guid guid; + string name; MapItem a, b, x, y; MapItem lshoulder, rshoulder; MapItem lstick, rstick; @@ -91,6 +99,8 @@ namespace OpenTK.Input MapItem dpad_u, dpad_d; MapItem dpad_l, dpad_r; + Guid Guid { get { return guid; } set { guid = value; } } + string Name { get { return name; } set { name = value; } } public MapItem A { get { return a; } set { a = value; } } public MapItem B { get { return b; } set { b = value; } } public MapItem X { get { return x; } set { x = value; } } @@ -113,6 +123,14 @@ namespace OpenTK.Input public MapItem DPadLeft { get { return dpad_l; } set { dpad_l = value; } } public MapItem DPadRight { get { return dpad_r; } set { dpad_r = value; } } + public GamePadMap() + { + } + + public GamePadMap(string configuration) + { + } + static GamePadMap() { GamePadMap map = new GamePadMap(); @@ -144,5 +162,178 @@ namespace OpenTK.Input { get { return default_map; } } + + public static GamePadMap GetConfiguration(Guid guid) + { + GamePadMap map = GamePadMap.Default; + if (GamePadConfigurationDatabase.Configurations.ContainsKey(guid)) + { + map = ParseConfiguration(GamePadConfigurationDatabase.Configurations[guid]); + } + return map; + } + + // Parses a GamePad configuration string. The string + // follows the rules for SDL2 GameController, outlined here: + // http://wiki.libsdl.org/SDL_GameControllerAddMapping + static GamePadMap ParseConfiguration(string configuration) + { + if (String.IsNullOrEmpty(configuration)) + { + throw new ArgumentNullException(); + } + + // The mapping string has the format "GUID,name,config" + // - GUID is a unigue identifier returned by Joystick.GetGuid() + // - name is a human-readable name for the controller + // - config is a comma-separated list of configurations as follows: + // - [gamepad axis or button name]:[joystick axis, button or hat number] + string[] items = configuration.Split(','); + if (items.Length < 3) + { + throw new ArgumentException(); + } + + GamePadMap map = new GamePadMap(); + map.Guid = new Guid(items[0]); + map.Name = items[1]; + for (int i = 2; i < items.Length; i++) + { + string[] config = items[i].Split(':'); + MapItem map_item = ParseItem(config[1]); + switch (config[0]) + { + case "a": + map.A = map_item; + break; + + case "b": + map.B = map_item; + break; + + case "x": + map.X = map_item; + break; + + case "y": + map.Y = map_item; + break; + + case "start": + map.Start = map_item; + break; + + case "back": + map.Back = map_item; + break; + + case "guide": + map.BigButton = map_item; + break; + + case "dpup": + map.DPadUp = map_item; + break; + + case "dpdown": + map.DPadDown = map_item; + break; + + case "dpleft": + map.DPadLeft = map_item; + break; + + case "dpright": + map.DPadRight = map_item; + break; + + case "leftshoulder": + map.LeftShoulder = map_item; + break; + + case "rightshoulder": + map.RightShoulder = map_item; + break; + + case "leftstick": + map.LeftStick = map_item; + break; + + case "rightstick": + map.RightStick = map_item; + break; + + case "leftx": + map.LeftAxisX = map_item; + break; + + case "lefty": + map.LeftAxisY = map_item; + break; + + case "rightx": + map.RightAxisX = map_item; + break; + + case "righty": + map.RightAxisY = map_item; + break; + + case "lefttrigger": + map.LeftTrigger = map_item; + break; + + case "righttrigger": + map.RightTrigger = map_item; + break; + + default: + Debug.Print("[Input] Invalid GamePad configuration name: {0}", items[i]); + break; + } + } + + return map; + } + + static MapItem ParseItem(string item) + { + if (String.IsNullOrEmpty(item)) + { + return new MapItem(); + } + + switch (item[0]) + { + case 'a': + return new MapItem(ParseAxis(item)); + + case 'b': + return new MapItem(ParseButton(item)); + + case 'h': + throw new NotImplementedException(); + //return new MapItem(ParseHat(item)); + + default: + throw new InvalidOperationException("[Input] Invalid GamePad configuration value"); + } + } + + static JoystickAxis ParseAxis(string item) + { + // item is in the format "a#" where # a zero-based integer number + JoystickAxis axis = JoystickAxis.Axis0; + int id = Int32.Parse(item.Substring(1)); + return axis + id; + } + + static JoystickButton ParseButton(string item) + { + // item is in the format "b#" where # a zero-based integer nubmer + JoystickButton button = JoystickButton.Button0; + int id = Int32.Parse(item.Substring(1)); + return button + id; + } } } From a0dad7f6981cdb57b2b6632e8c640239a8356883 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:13:20 +0100 Subject: [PATCH 166/245] [Input] Implemented MappedGamePadDriver.GetCapabilities() --- Source/OpenTK/Platform/MappedGamePadDriver.cs | 76 ++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index 346be196..dbac6149 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -52,13 +52,18 @@ namespace OpenTK.Platform /// class MappedGamePadDriver : IGamePadDriver { + readonly Dictionary configurations = + new Dictionary(); + public GamePadState GetState(int index) { JoystickState joy = Joystick.GetState(index); GamePadState pad = new GamePadState(); if (joy.IsConnected) { - GamePadMapping mapping = new GamePadMapping();//GamePadMapping.Lookup() + GamePadMap map = GetConfiguration(Joystick.GetGuid(index)); + + // Todo: implement mapping } return pad; @@ -67,10 +72,19 @@ namespace OpenTK.Platform public GamePadCapabilities GetCapabilities(int index) { JoystickCapabilities joy = Joystick.GetCapabilities(index); - GamePadCapabilities pad = new GamePadCapabilities(); + GamePadCapabilities pad; if (joy.IsConnected) { - // Todo: implement mapping + GamePadMap map = GetConfiguration(Joystick.GetGuid(index)); + pad = new GamePadCapabilities( + GamePadType.GamePad, // Todo: detect different types + TranslateAxes(map), + TranslateButtons(map), + true); + } + else + { + pad = new GamePadCapabilities(); } return pad; } @@ -79,5 +93,61 @@ namespace OpenTK.Platform { throw new NotImplementedException(); } + + #region Private Members + + GamePadMap GetConfiguration(Guid guid) + { + if (!configurations.ContainsKey(guid)) + { + GamePadMap map = GamePadMap.GetConfiguration(guid); + configurations.Add(guid, map); + } + return configurations[guid]; + } + + bool IsMapped(MapItem item) + { + return item.Type != MapType.Unmapped; + } + + GamePadAxes TranslateAxes(GamePadMap map) + { + GamePadAxes axes = 0; + axes |= IsMapped(map.LeftAxisX) ? GamePadAxes.LeftX : 0; + axes |= IsMapped(map.LeftAxisY) ? GamePadAxes.LeftY : 0; + axes |= IsMapped(map.RightAxisX) ? GamePadAxes.RightX : 0; + axes |= IsMapped(map.RightAxisY) ? GamePadAxes.RightY : 0; + axes |= IsMapped(map.LeftTrigger) ? GamePadAxes.LeftTrigger : 0; + axes |= IsMapped(map.RightTrigger) ? GamePadAxes.RightTrigger : 0; + return axes; + } + + Buttons TranslateButtons(GamePadMap map) + { + Buttons buttons = 0; + buttons |= IsMapped(map.A) ? Buttons.A : 0; + buttons |= IsMapped(map.B) ? Buttons.B : 0; + buttons |= IsMapped(map.X) ? Buttons.X : 0; + buttons |= IsMapped(map.Y) ? Buttons.Y : 0; + buttons |= IsMapped(map.Start) ? Buttons.Start : 0; + buttons |= IsMapped(map.Back) ? Buttons.Back : 0; + buttons |= IsMapped(map.BigButton) ? Buttons.BigButton : 0; + buttons |= IsMapped(map.LeftShoulder) ? Buttons.LeftShoulder : 0; + buttons |= IsMapped(map.RightShoulder) ? Buttons.RightShoulder : 0; + buttons |= IsMapped(map.LeftStick) ? Buttons.LeftStick : 0; + buttons |= IsMapped(map.RightStick) ? Buttons.RightStick : 0; + return buttons; + } + +// bool TranslateDPad(GamePadMap map) +// { +// pad.HasDPadDownButton = IsMapped(map.DPadDown); +// pad.HasDPadUpButton = IsMapped(map.DPadUp); +// pad.HasDPadLeftButton = IsMapped(map.DPadLeft); +// pad.HasDPadRightButton = IsMapped(map.DPadRight); +// } + + #endregion } } From b056a50e73c8744a23c899d9c6840a88babbd46c Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:33:08 +0100 Subject: [PATCH 167/245] [Input] Added xml documentation for GamePadCapabilities --- Source/OpenTK/Input/GamePadCapabilities.cs | 170 ++++++++++++++++++++- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs index 7fbe8055..269081e8 100644 --- a/Source/OpenTK/Input/GamePadCapabilities.cs +++ b/Source/OpenTK/Input/GamePadCapabilities.cs @@ -31,7 +31,9 @@ using System; namespace OpenTK.Input { - + /// + /// Describes the capabilities of a GamePad input device. + /// public struct GamePadCapabilities : IEquatable { Buttons buttons; @@ -54,146 +56,285 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets a value describing the type of a input device. + /// This value depends on the connected device and the drivers in use. If IsConnected + /// is false, then this value will be GamePadType.Unknown. + /// + /// The GamePadType of the connected input device. public GamePadType GamePadType { get { return (GamePadType)gamepad_type; } } + /// + /// Gets a value describing whether this GamePad has + /// an up digital pad button. + /// + /// true if this instance has an up digital pad button; otherwise, false. public bool HasDPadUpButton { get { return (buttons & Buttons.DPadUp) != 0; } } - public bool HasDPadLeftButton - { - get { return (buttons & Buttons.DPadLeft) != 0; } - } - + /// + /// Gets a value describing whether this GamePad has + /// a down digital pad button. + /// + /// true if this instance has a down digital pad button; otherwise, false. public bool HasDPadDownButton { get { return (buttons & Buttons.DPadDown) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a left digital pad button. + /// + /// true if this instance has a left digital pad button; otherwise, false. + public bool HasDPadLeftButton + { + get { return (buttons & Buttons.DPadLeft) != 0; } + } + + /// + /// Gets a value describing whether this GamePad has + /// a right digital pad button. + /// + /// true if this instance has a right digital pad button; otherwise, false. public bool HasDPadRightButton { get { return (buttons & Buttons.DPadRight) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// an A button. + /// + /// true if this instance has an A button; otherwise, false. public bool HasAButton { get { return (buttons & Buttons.A) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a B button. + /// + /// true if this instance has a B button; otherwise, false. public bool HasBButton { get { return (buttons & Buttons.B) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a X button. + /// + /// true if this instance has a X button; otherwise, false. public bool HasXButton { get { return (buttons & Buttons.X) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a Y button. + /// + /// true if this instance has a Y button; otherwise, false. public bool HasYButton { get { return (buttons & Buttons.Y) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a left stick button. + /// + /// true if this instance has a left stick button; otherwise, false. public bool HasLeftStickButton { get { return (buttons & Buttons.LeftStick) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a right stick button. + /// + /// true if this instance has a right stick button; otherwise, false. public bool HasRightStickButton { get { return (buttons & Buttons.RightStick) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a left shoulder button. + /// + /// true if this instance has a left shoulder button; otherwise, false. public bool HasLeftShoulderButton { get { return (buttons & Buttons.LeftShoulder) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a right shoulder button. + /// + /// true if this instance has a right shoulder button; otherwise, false. public bool HasRightShoulderButton { get { return (buttons & Buttons.RightShoulder) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a back button. + /// + /// true if this instance has a back button; otherwise, false. public bool HasBackButton { get { return (buttons & Buttons.Back) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a big button. (also known as "guide" or "home" button). + /// + /// true if this instance has a big button; otherwise, false. public bool HasBigButton { get { return (buttons & Buttons.BigButton) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a start button. + /// + /// true if this instance has a start button; otherwise, false. public bool HasStartButton { get { return (buttons & Buttons.Start) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a left thumbstick with a x-axis. + /// + /// true if this instance has a left thumbstick with a x-axis; otherwise, false. public bool HasLeftXThumbStick { get { return (axes & GamePadAxes.LeftX) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a left thumbstick with a y-axis. + /// + /// true if this instance has a left thumbstick with a y-axis; otherwise, false. public bool HasLeftYThumbStick { get { return (axes & GamePadAxes.LeftY) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a right thumbstick with a x-axis. + /// + /// true if this instance has a right thumbstick with a x-axis; otherwise, false. public bool HasRightXThumbStick { get { return (axes & GamePadAxes.RightX) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a right thumbstick with a y-axis. + /// + /// true if this instance has a right thumbstick with a y-axis; otherwise, false. public bool HasRightYThumbStick { get { return (axes & GamePadAxes.RightY) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a left trigger. + /// + /// true if this instance has a left trigger; otherwise, false. public bool HasLeftTrigger { get { return (axes & GamePadAxes.LeftTrigger) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a right trigger. + /// + /// true if this instance has a right trigger; otherwise, false. public bool HasRightTrigger { get { return (axes & GamePadAxes.RightTrigger) != 0; } } + /// + /// Gets a value describing whether this GamePad has + /// a low-frequency vibration motor. + /// + /// true if this instance has a low-frequency vibration motor; otherwise, false. public bool HasLeftVibrationMotor { get { return false; } } + /// + /// Gets a value describing whether this GamePad has + /// a high-frequency vibration motor. + /// + /// true if this instance has a high frequency vibration motor; otherwise, false. public bool HasRightVibrationMotor { get { return false; } } + /// + /// Gets a value describing whether this GamePad has + /// a microphone input. + /// + /// true if this instance has a microphone input; otherwise, false. public bool HasVoiceSupport { get { return false; } } + /// + /// Gets a value describing whether this GamePad is + /// currently connected. + /// + /// true if this instance is currently connected; otherwise, false. public bool IsConnected { get { return is_connected; } } + /// A structure to test for equality. + /// A structure to test for equality. public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right) { return left.Equals(right); } + /// A structure to test for inequality. + /// A structure to test for inequality. public static bool operator !=(GamePadCapabilities left, GamePadCapabilities right) { return !left.Equals(right); } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return String.Format( @@ -204,6 +345,11 @@ namespace OpenTK.Input IsConnected); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return @@ -212,6 +358,12 @@ namespace OpenTK.Input gamepad_type.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -223,6 +375,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(GamePadCapabilities other) { return From c2c76f2ab2376351f81c75943e62de5dbf64ee6f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:33:57 +0100 Subject: [PATCH 168/245] [Input] Made the Name property public We can use the name property to implement a hypothetical GamePad.GetName() method in the future. --- Source/OpenTK/Input/GamePadMap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Input/GamePadMap.cs b/Source/OpenTK/Input/GamePadMap.cs index c9cfaf1e..cc96f79e 100644 --- a/Source/OpenTK/Input/GamePadMap.cs +++ b/Source/OpenTK/Input/GamePadMap.cs @@ -100,7 +100,7 @@ namespace OpenTK.Input MapItem dpad_l, dpad_r; Guid Guid { get { return guid; } set { guid = value; } } - string Name { get { return name; } set { name = value; } } + public string Name { get { return name; } set { name = value; } } public MapItem A { get { return a; } set { a = value; } } public MapItem B { get { return b; } set { b = value; } } public MapItem X { get { return x; } set { x = value; } } From dc1ffab78dbd7a0a23a1aecaf04f1e504c565c92 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:34:21 +0100 Subject: [PATCH 169/245] [Input] Implemented IGamePadDriver.GetName() --- Source/OpenTK/Platform/MappedGamePadDriver.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index dbac6149..8240d74c 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -91,7 +91,14 @@ namespace OpenTK.Platform public string GetName(int index) { - throw new NotImplementedException(); + JoystickCapabilities joy = Joystick.GetCapabilities(index); + string name = String.Empty; + if (joy.IsConnected) + { + GamePadMap map = GetConfiguration(Joystick.GetGuid(index)); + name = map.Name; + } + return name; } #region Private Members From fb57a9062d9dfbe2b2d1a54cd0fafb6bd45838b7 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 01:38:12 +0100 Subject: [PATCH 170/245] [Input] Renamed GamePadMap to GamePadConfiguration --- ...{GamePadMap.cs => GamePadConfiguration.cs} | 184 +++++++----------- .../OpenTK/Input/GamePadConfigurationItem.cs | 81 ++++++++ Source/OpenTK/OpenTK.csproj | 3 +- Source/OpenTK/Platform/MappedGamePadDriver.cs | 22 +-- 4 files changed, 162 insertions(+), 128 deletions(-) rename Source/OpenTK/Input/{GamePadMap.cs => GamePadConfiguration.cs} (56%) create mode 100644 Source/OpenTK/Input/GamePadConfigurationItem.cs diff --git a/Source/OpenTK/Input/GamePadMap.cs b/Source/OpenTK/Input/GamePadConfiguration.cs similarity index 56% rename from Source/OpenTK/Input/GamePadMap.cs rename to Source/OpenTK/Input/GamePadConfiguration.cs index cc96f79e..887ba6a1 100644 --- a/Source/OpenTK/Input/GamePadMap.cs +++ b/Source/OpenTK/Input/GamePadConfiguration.cs @@ -34,138 +34,90 @@ using System.Text; namespace OpenTK.Input { - enum MapType + class GamePadConfiguration { - Unmapped = 0, - Axis, - Button - } - - class MapItem - { - MapType map_type; - Nullable map_button; - Nullable map_axis; - - public MapItem() - { - } - - public MapItem(JoystickAxis axis) - { - Type = MapType.Axis; - Axis = axis; - } - - public MapItem(JoystickButton button) - { - Type = MapType.Button; - Button = button; - } - - public MapType Type - { - get { return map_type; } - private set { map_type = value; } - } - - public Nullable Axis - { - get { return map_axis; } - private set { map_axis = value; } - } - - public Nullable Button - { - get { return map_button; } - private set { map_button = value; } - } - } - - class GamePadMap - { - static readonly GamePadMap default_map; + static readonly GamePadConfiguration default_map; Guid guid; string name; - MapItem a, b, x, y; - MapItem lshoulder, rshoulder; - MapItem lstick, rstick; - MapItem start, back; - MapItem home; - MapItem axis_lx, axis_ly; - MapItem axis_rx, axis_ry; - MapItem ltrigger, rtrigger; - MapItem dpad_u, dpad_d; - MapItem dpad_l, dpad_r; + GamePadConfigurationItem a, b, x, y; + GamePadConfigurationItem lshoulder, rshoulder; + GamePadConfigurationItem lstick, rstick; + GamePadConfigurationItem start, back; + GamePadConfigurationItem home; + GamePadConfigurationItem axis_lx, axis_ly; + GamePadConfigurationItem axis_rx, axis_ry; + GamePadConfigurationItem ltrigger, rtrigger; + GamePadConfigurationItem dpad_u, dpad_d; + GamePadConfigurationItem dpad_l, dpad_r; Guid Guid { get { return guid; } set { guid = value; } } public string Name { get { return name; } set { name = value; } } - public MapItem A { get { return a; } set { a = value; } } - public MapItem B { get { return b; } set { b = value; } } - public MapItem X { get { return x; } set { x = value; } } - public MapItem Y { get { return y; } set { y = value; } } - public MapItem LeftShoulder { get { return lshoulder; } set { lshoulder = value; } } - public MapItem RightShoulder { get { return rshoulder; } set { rshoulder = value; } } - public MapItem LeftStick { get { return lstick; } set { lstick = value; } } - public MapItem RightStick { get { return rstick; } set { rstick = value; } } - public MapItem Start { get { return start; } set { start = value; } } - public MapItem Back { get { return back; } set { back = value; } } - public MapItem BigButton { get { return home; } set { home = value; } } - public MapItem LeftAxisX { get { return axis_lx; } set { axis_lx = value; } } - public MapItem LeftAxisY { get { return axis_ly; } set { axis_ly = value; } } - public MapItem RightAxisX { get { return axis_rx; } set { axis_rx = value; } } - public MapItem RightAxisY { get { return axis_ry; } set { axis_ry = value; } } - public MapItem LeftTrigger { get { return ltrigger; } set { ltrigger = value; } } - public MapItem RightTrigger { get { return rtrigger; } set { rtrigger = value; } } - public MapItem DPadUp { get { return dpad_u; } set { dpad_u = value; } } - public MapItem DPadDown { get { return dpad_d; } set { dpad_d = value; } } - public MapItem DPadLeft { get { return dpad_l; } set { dpad_l = value; } } - public MapItem DPadRight { get { return dpad_r; } set { dpad_r = value; } } + public GamePadConfigurationItem A { get { return a; } set { a = value; } } + public GamePadConfigurationItem B { get { return b; } set { b = value; } } + public GamePadConfigurationItem X { get { return x; } set { x = value; } } + public GamePadConfigurationItem Y { get { return y; } set { y = value; } } + public GamePadConfigurationItem LeftShoulder { get { return lshoulder; } set { lshoulder = value; } } + public GamePadConfigurationItem RightShoulder { get { return rshoulder; } set { rshoulder = value; } } + public GamePadConfigurationItem LeftStick { get { return lstick; } set { lstick = value; } } + public GamePadConfigurationItem RightStick { get { return rstick; } set { rstick = value; } } + public GamePadConfigurationItem Start { get { return start; } set { start = value; } } + public GamePadConfigurationItem Back { get { return back; } set { back = value; } } + public GamePadConfigurationItem BigButton { get { return home; } set { home = value; } } + public GamePadConfigurationItem LeftAxisX { get { return axis_lx; } set { axis_lx = value; } } + public GamePadConfigurationItem LeftAxisY { get { return axis_ly; } set { axis_ly = value; } } + public GamePadConfigurationItem RightAxisX { get { return axis_rx; } set { axis_rx = value; } } + public GamePadConfigurationItem RightAxisY { get { return axis_ry; } set { axis_ry = value; } } + public GamePadConfigurationItem LeftTrigger { get { return ltrigger; } set { ltrigger = value; } } + public GamePadConfigurationItem RightTrigger { get { return rtrigger; } set { rtrigger = value; } } + public GamePadConfigurationItem DPadUp { get { return dpad_u; } set { dpad_u = value; } } + public GamePadConfigurationItem DPadDown { get { return dpad_d; } set { dpad_d = value; } } + public GamePadConfigurationItem DPadLeft { get { return dpad_l; } set { dpad_l = value; } } + public GamePadConfigurationItem DPadRight { get { return dpad_r; } set { dpad_r = value; } } - public GamePadMap() + public GamePadConfiguration() { } - public GamePadMap(string configuration) + public GamePadConfiguration(string configuration) { } - static GamePadMap() + static GamePadConfiguration() { - GamePadMap map = new GamePadMap(); - map.A = new MapItem(JoystickButton.Button0); - map.B = new MapItem(JoystickButton.Button1); - map.X = new MapItem(JoystickButton.Button2); - map.Y = new MapItem(JoystickButton.Button3); - map.LeftShoulder = new MapItem(JoystickButton.Button4); - map.RightShoulder = new MapItem(JoystickButton.Button5); - map.LeftStick = new MapItem(JoystickButton.Button6); - map.RightStick = new MapItem(JoystickButton.Button7); - map.Start = new MapItem(JoystickButton.Button8); - map.Back = new MapItem(JoystickButton.Button9); - map.BigButton = new MapItem(JoystickButton.Button10); - map.DPadUp = new MapItem(JoystickButton.Button11); - map.DPadDown = new MapItem(JoystickButton.Button12); - map.DPadLeft = new MapItem(JoystickButton.Button13); - map.DPadRight = new MapItem(JoystickButton.Button14); - map.LeftAxisX = new MapItem(JoystickAxis.Axis0); - map.LeftAxisY = new MapItem(JoystickAxis.Axis1); - map.RightAxisX = new MapItem(JoystickAxis.Axis2); - map.RightAxisY = new MapItem(JoystickAxis.Axis3); - map.LeftTrigger = new MapItem(JoystickAxis.Axis4); - map.RightTrigger = new MapItem(JoystickAxis.Axis5); + GamePadConfiguration map = new GamePadConfiguration(); + map.A = new GamePadConfigurationItem(JoystickButton.Button0); + map.B = new GamePadConfigurationItem(JoystickButton.Button1); + map.X = new GamePadConfigurationItem(JoystickButton.Button2); + map.Y = new GamePadConfigurationItem(JoystickButton.Button3); + map.LeftShoulder = new GamePadConfigurationItem(JoystickButton.Button4); + map.RightShoulder = new GamePadConfigurationItem(JoystickButton.Button5); + map.LeftStick = new GamePadConfigurationItem(JoystickButton.Button6); + map.RightStick = new GamePadConfigurationItem(JoystickButton.Button7); + map.Start = new GamePadConfigurationItem(JoystickButton.Button8); + map.Back = new GamePadConfigurationItem(JoystickButton.Button9); + map.BigButton = new GamePadConfigurationItem(JoystickButton.Button10); + map.DPadUp = new GamePadConfigurationItem(JoystickButton.Button11); + map.DPadDown = new GamePadConfigurationItem(JoystickButton.Button12); + map.DPadLeft = new GamePadConfigurationItem(JoystickButton.Button13); + map.DPadRight = new GamePadConfigurationItem(JoystickButton.Button14); + map.LeftAxisX = new GamePadConfigurationItem(JoystickAxis.Axis0); + map.LeftAxisY = new GamePadConfigurationItem(JoystickAxis.Axis1); + map.RightAxisX = new GamePadConfigurationItem(JoystickAxis.Axis2); + map.RightAxisY = new GamePadConfigurationItem(JoystickAxis.Axis3); + map.LeftTrigger = new GamePadConfigurationItem(JoystickAxis.Axis4); + map.RightTrigger = new GamePadConfigurationItem(JoystickAxis.Axis5); default_map = map; } - public static GamePadMap Default + public static GamePadConfiguration Default { get { return default_map; } } - public static GamePadMap GetConfiguration(Guid guid) + public static GamePadConfiguration GetConfiguration(Guid guid) { - GamePadMap map = GamePadMap.Default; + GamePadConfiguration map = GamePadConfiguration.Default; if (GamePadConfigurationDatabase.Configurations.ContainsKey(guid)) { map = ParseConfiguration(GamePadConfigurationDatabase.Configurations[guid]); @@ -176,7 +128,7 @@ namespace OpenTK.Input // Parses a GamePad configuration string. The string // follows the rules for SDL2 GameController, outlined here: // http://wiki.libsdl.org/SDL_GameControllerAddMapping - static GamePadMap ParseConfiguration(string configuration) + static GamePadConfiguration ParseConfiguration(string configuration) { if (String.IsNullOrEmpty(configuration)) { @@ -194,13 +146,13 @@ namespace OpenTK.Input throw new ArgumentException(); } - GamePadMap map = new GamePadMap(); + GamePadConfiguration map = new GamePadConfiguration(); map.Guid = new Guid(items[0]); map.Name = items[1]; for (int i = 2; i < items.Length; i++) { string[] config = items[i].Split(':'); - MapItem map_item = ParseItem(config[1]); + GamePadConfigurationItem map_item = ParseItem(config[1]); switch (config[0]) { case "a": @@ -296,20 +248,20 @@ namespace OpenTK.Input return map; } - static MapItem ParseItem(string item) + static GamePadConfigurationItem ParseItem(string item) { if (String.IsNullOrEmpty(item)) { - return new MapItem(); + return new GamePadConfigurationItem(); } switch (item[0]) { case 'a': - return new MapItem(ParseAxis(item)); + return new GamePadConfigurationItem(ParseAxis(item)); case 'b': - return new MapItem(ParseButton(item)); + return new GamePadConfigurationItem(ParseButton(item)); case 'h': throw new NotImplementedException(); diff --git a/Source/OpenTK/Input/GamePadConfigurationItem.cs b/Source/OpenTK/Input/GamePadConfigurationItem.cs new file mode 100644 index 00000000..e9841074 --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationItem.cs @@ -0,0 +1,81 @@ +#region License +// +// GamePadConfigurationItem.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + enum ConfigurationType + { + Unmapped = 0, + Axis, + Button + } + + class GamePadConfigurationItem + { + ConfigurationType map_type; + Nullable map_button; + Nullable map_axis; + + public GamePadConfigurationItem() + { + } + + public GamePadConfigurationItem(JoystickAxis axis) + { + Type = ConfigurationType.Axis; + Axis = axis; + } + + public GamePadConfigurationItem(JoystickButton button) + { + Type = ConfigurationType.Button; + Button = button; + } + + public ConfigurationType Type + { + get { return map_type; } + private set { map_type = value; } + } + + public Nullable Axis + { + get { return map_axis; } + private set { map_axis = value; } + } + + public Nullable Button + { + get { return map_button; } + private set { map_button = value; } + } + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index ec3d8aff..13557288 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -791,8 +791,9 @@ - + + diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index 8240d74c..6240ec4a 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -52,8 +52,8 @@ namespace OpenTK.Platform /// class MappedGamePadDriver : IGamePadDriver { - readonly Dictionary configurations = - new Dictionary(); + readonly Dictionary configurations = + new Dictionary(); public GamePadState GetState(int index) { @@ -61,7 +61,7 @@ namespace OpenTK.Platform GamePadState pad = new GamePadState(); if (joy.IsConnected) { - GamePadMap map = GetConfiguration(Joystick.GetGuid(index)); + GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index)); // Todo: implement mapping @@ -75,7 +75,7 @@ namespace OpenTK.Platform GamePadCapabilities pad; if (joy.IsConnected) { - GamePadMap map = GetConfiguration(Joystick.GetGuid(index)); + GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index)); pad = new GamePadCapabilities( GamePadType.GamePad, // Todo: detect different types TranslateAxes(map), @@ -95,7 +95,7 @@ namespace OpenTK.Platform string name = String.Empty; if (joy.IsConnected) { - GamePadMap map = GetConfiguration(Joystick.GetGuid(index)); + GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index)); name = map.Name; } return name; @@ -103,22 +103,22 @@ namespace OpenTK.Platform #region Private Members - GamePadMap GetConfiguration(Guid guid) + GamePadConfiguration GetConfiguration(Guid guid) { if (!configurations.ContainsKey(guid)) { - GamePadMap map = GamePadMap.GetConfiguration(guid); + GamePadConfiguration map = GamePadConfiguration.GetConfiguration(guid); configurations.Add(guid, map); } return configurations[guid]; } - bool IsMapped(MapItem item) + bool IsMapped(GamePadConfigurationItem item) { - return item.Type != MapType.Unmapped; + return item.Type != ConfigurationType.Unmapped; } - GamePadAxes TranslateAxes(GamePadMap map) + GamePadAxes TranslateAxes(GamePadConfiguration map) { GamePadAxes axes = 0; axes |= IsMapped(map.LeftAxisX) ? GamePadAxes.LeftX : 0; @@ -130,7 +130,7 @@ namespace OpenTK.Platform return axes; } - Buttons TranslateButtons(GamePadMap map) + Buttons TranslateButtons(GamePadConfiguration map) { Buttons buttons = 0; buttons |= IsMapped(map.A) ? Buttons.A : 0; From a8fb977017657046eda6944061fd3a0b7a636732 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 02:20:25 +0100 Subject: [PATCH 171/245] [Input] Initialize JoystickDevice.Details in constructor --- Source/OpenTK/Input/JoystickDevice.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Input/JoystickDevice.cs b/Source/OpenTK/Input/JoystickDevice.cs index d02d5b4c..55a92bef 100644 --- a/Source/OpenTK/Input/JoystickDevice.cs +++ b/Source/OpenTK/Input/JoystickDevice.cs @@ -156,12 +156,13 @@ namespace OpenTK.Input // Provides platform-specific information about the relevant JoystickDevice. internal sealed class JoystickDevice : JoystickDevice + where TDetail : new() { internal JoystickDevice(int id, int axes, int buttons) : base(id, axes, buttons) { } - internal TDetail Details; + internal TDetail Details = new TDetail(); } #endregion From 9e4827bf67b877b6487c834805de4ac570c138fe Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 02:20:51 +0100 Subject: [PATCH 172/245] [SDL2] Fixed SDL_JoystickGetGUID capitalization --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 8ad25480..38ebef43 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -301,7 +301,7 @@ namespace OpenTK.Platform.SDL2 public static extern byte JoystickGetButton(IntPtr joystick, int button); [SuppressUnmanagedCodeSecurity] - [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetGuid", ExactSpelling = true)] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_JoystickGetGUID", ExactSpelling = true)] public static extern JoystickGuid JoystickGetGUID(IntPtr joystick); [SuppressUnmanagedCodeSecurity] From bf5d2a738d302004b0da9770647ee49b14eeeeb7 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 02:42:51 +0100 Subject: [PATCH 173/245] [Input] Made configuration database non-static --- .../Input/GamePadConfigurationDatabase.cs | 42 ++++++++++++++----- Source/OpenTK/Platform/MappedGamePadDriver.cs | 5 ++- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Input/GamePadConfigurationDatabase.cs b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs index 95205993..06d3b12e 100644 --- a/Source/OpenTK/Input/GamePadConfigurationDatabase.cs +++ b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs @@ -32,11 +32,11 @@ using System.Collections.Generic; namespace OpenTK.Input { - static class GamePadConfigurationDatabase + class GamePadConfigurationDatabase { - internal static readonly Dictionary Configurations = new Dictionary(); + readonly Dictionary Configurations = new Dictionary(); - static GamePadConfigurationDatabase() + internal GamePadConfigurationDatabase() { // Configuration database copied from SDL @@ -61,8 +61,10 @@ namespace OpenTK.Input // 3. This notice may not be removed or altered from any source distribution. #endregion - // Default configuration, used when no suitable configuration is available - Add("00000000000000000000000000000000,Unknown Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + // Default configuration, used when no suitable configuration is available. + // Add("00000000000000000000000000000000,Unknown Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); + // Valid for all xinput devices on Windows: + Add("00000000000000000000000000000000,X360 Controller,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,"); // Windows Add("341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); @@ -72,7 +74,6 @@ namespace OpenTK.Input Add("88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,"); Add("4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,"); Add("25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,"); - Add("xinput00000000000000000000000000,X360 Controller,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,"); // Mac OS X Add("0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,"); @@ -99,11 +100,32 @@ namespace OpenTK.Input Add("030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"); } - static void Add(string config) + internal void Add(string config) { - Configurations.Add( - new Guid(config.Substring(0, 32)), - config); + Guid guid = new Guid(config.Substring(0, 32)); + if (!Configurations.ContainsKey(guid)) + { + Configurations.Add(guid, config); + } + else + { + Configurations[guid] = config; + } + } + + internal string this[Guid guid] + { + get + { + if (Configurations.ContainsKey(guid)) + { + return Configurations[guid]; + } + else + { + return Configurations[new Guid()]; // default configuration + } + } } } } diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index 6240ec4a..97cb1fae 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -52,6 +52,8 @@ namespace OpenTK.Platform /// class MappedGamePadDriver : IGamePadDriver { + readonly GamePadConfigurationDatabase database = + new GamePadConfigurationDatabase(); readonly Dictionary configurations = new Dictionary(); @@ -107,7 +109,8 @@ namespace OpenTK.Platform { if (!configurations.ContainsKey(guid)) { - GamePadConfiguration map = GamePadConfiguration.GetConfiguration(guid); + string config = database[guid]; + GamePadConfiguration map = new GamePadConfiguration(config); configurations.Add(guid, map); } return configurations[guid]; From 0593ea62e69aa18a2d08df093c8c002f382384a3 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 18:36:09 +0100 Subject: [PATCH 174/245] [Input] Added Source and Target to GamePadConfigurationItem --- Source/OpenTK/Input/ConfigurationType.cs | 41 +++++++++++ .../OpenTK/Input/GamePadConfigurationItem.cs | 47 ++++-------- .../Input/GamePadConfigurationSource.cs | 72 +++++++++++++++++++ .../Input/GamePadConfigurationTarget.cs | 72 +++++++++++++++++++ Source/OpenTK/OpenTK.csproj | 3 + 5 files changed, 200 insertions(+), 35 deletions(-) create mode 100644 Source/OpenTK/Input/ConfigurationType.cs create mode 100644 Source/OpenTK/Input/GamePadConfigurationSource.cs create mode 100644 Source/OpenTK/Input/GamePadConfigurationTarget.cs diff --git a/Source/OpenTK/Input/ConfigurationType.cs b/Source/OpenTK/Input/ConfigurationType.cs new file mode 100644 index 00000000..0cbb649a --- /dev/null +++ b/Source/OpenTK/Input/ConfigurationType.cs @@ -0,0 +1,41 @@ +#region License +// +// ConfigurationType.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + enum ConfigurationType + { + Unmapped = 0, + Axis, + Button + } +} + diff --git a/Source/OpenTK/Input/GamePadConfigurationItem.cs b/Source/OpenTK/Input/GamePadConfigurationItem.cs index e9841074..c2dba9f5 100644 --- a/Source/OpenTK/Input/GamePadConfigurationItem.cs +++ b/Source/OpenTK/Input/GamePadConfigurationItem.cs @@ -31,51 +31,28 @@ using System; namespace OpenTK.Input { - enum ConfigurationType - { - Unmapped = 0, - Axis, - Button - } - class GamePadConfigurationItem { - ConfigurationType map_type; - Nullable map_button; - Nullable map_axis; + GamePadConfigurationSource source; + GamePadConfigurationTarget target; - public GamePadConfigurationItem() + public GamePadConfigurationItem(GamePadConfigurationSource source, GamePadConfigurationTarget target) { + Source = source; + Target = target; } - public GamePadConfigurationItem(JoystickAxis axis) + public GamePadConfigurationSource Source { - Type = ConfigurationType.Axis; - Axis = axis; + get { return source; } + private set { source = value; } } - public GamePadConfigurationItem(JoystickButton button) + public GamePadConfigurationTarget Target { - Type = ConfigurationType.Button; - Button = button; - } - - public ConfigurationType Type - { - get { return map_type; } - private set { map_type = value; } - } - - public Nullable Axis - { - get { return map_axis; } - private set { map_axis = value; } - } - - public Nullable Button - { - get { return map_button; } - private set { map_button = value; } + get { return target; } + private set { target = value; } } } } + diff --git a/Source/OpenTK/Input/GamePadConfigurationSource.cs b/Source/OpenTK/Input/GamePadConfigurationSource.cs new file mode 100644 index 00000000..c1b25b82 --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationSource.cs @@ -0,0 +1,72 @@ +#region License +// +// GamePadConfigurationItem.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + struct GamePadConfigurationSource + { + ConfigurationType map_type; + Nullable map_button; + Nullable map_axis; + + public GamePadConfigurationSource(JoystickAxis axis) + : this() + { + Type = ConfigurationType.Axis; + Axis = axis; + } + + public GamePadConfigurationSource(JoystickButton button) + : this() + { + Type = ConfigurationType.Button; + Button = button; + } + + public ConfigurationType Type + { + get { return map_type; } + private set { map_type = value; } + } + + public JoystickAxis Axis + { + get { return map_axis.Value; } + private set { map_axis = value; } + } + + public JoystickButton Button + { + get { return map_button.Value; } + private set { map_button = value; } + } + } +} diff --git a/Source/OpenTK/Input/GamePadConfigurationTarget.cs b/Source/OpenTK/Input/GamePadConfigurationTarget.cs new file mode 100644 index 00000000..75199c9f --- /dev/null +++ b/Source/OpenTK/Input/GamePadConfigurationTarget.cs @@ -0,0 +1,72 @@ +#region License +// +// GamePadConfigurationTarget.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Input +{ + struct GamePadConfigurationTarget + { + ConfigurationType map_type; + Nullable map_button; + Nullable map_axis; + + public GamePadConfigurationTarget(Buttons button) + : this() + { + Type = ConfigurationType.Button; + map_button = button; + } + + public GamePadConfigurationTarget(GamePadAxes axis) + : this() + { + Type = ConfigurationType.Axis; + map_axis = axis; + } + + public ConfigurationType Type + { + get { return map_type; } + private set { map_type = value; } + } + + public GamePadAxes Axis + { + get { return map_axis.Value; } + private set { map_axis = value; } + } + + public Buttons Button + { + get { return map_button.Value; } + private set { map_button = value; } + } + } +} diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 13557288..93d0a425 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -793,6 +793,9 @@ + + + From 8f00bbc9f20f4a1285bdbaf95d652a2c270e242c Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 18:36:27 +0100 Subject: [PATCH 175/245] [Input] Added internal Joystick.GetAxisRaw() method --- Source/OpenTK/Input/JoystickState.cs | 30 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index e14ac2d2..7f1624d8 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -56,16 +56,7 @@ namespace OpenTK.Input public float GetAxis(int axis) { - float value = 0.0f; - if (axis >= 0 && axis < MaxAxes) - { - value = GetAxisUnsafe(axis) * ConversionFactor; - } - else - { - Debug.Print("[Joystick] Invalid axis {0}", axis); - } - return value; + return GetAxisRaw(axis) * ConversionFactor; } public ButtonState GetButton(JoystickButton button) @@ -124,6 +115,25 @@ namespace OpenTK.Input #region Internal Members + internal short GetAxisRaw(JoystickAxis axis) + { + return GetAxisRaw((int)axis); + } + + internal short GetAxisRaw(int axis) + { + short value = 0; + if (axis >= 0 && axis < MaxAxes) + { + value = GetAxisUnsafe(axis); + } + else + { + Debug.Print("[Joystick] Invalid axis {0}", axis); + } + return value; + } + internal void SetAxis(JoystickAxis axis, short value) { int index = (int)axis; From d49af2787f89e9d5ba4d76e5b818073db2a0ea74 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 18:37:20 +0100 Subject: [PATCH 176/245] [Input] Simplified configuration parser and added GetEnumerator() --- Source/OpenTK/Input/GamePadConfiguration.cs | 263 +++++++------------- 1 file changed, 88 insertions(+), 175 deletions(-) diff --git a/Source/OpenTK/Input/GamePadConfiguration.cs b/Source/OpenTK/Input/GamePadConfiguration.cs index 887ba6a1..54600e79 100644 --- a/Source/OpenTK/Input/GamePadConfiguration.cs +++ b/Source/OpenTK/Input/GamePadConfiguration.cs @@ -34,101 +34,43 @@ using System.Text; namespace OpenTK.Input { - class GamePadConfiguration + sealed class GamePadConfiguration { - static readonly GamePadConfiguration default_map; + static readonly char[] ConfigurationSeparator = new char[] { ',' }; Guid guid; string name; - GamePadConfigurationItem a, b, x, y; - GamePadConfigurationItem lshoulder, rshoulder; - GamePadConfigurationItem lstick, rstick; - GamePadConfigurationItem start, back; - GamePadConfigurationItem home; - GamePadConfigurationItem axis_lx, axis_ly; - GamePadConfigurationItem axis_rx, axis_ry; - GamePadConfigurationItem ltrigger, rtrigger; - GamePadConfigurationItem dpad_u, dpad_d; - GamePadConfigurationItem dpad_l, dpad_r; + readonly List configuration_items = + new List(); - Guid Guid { get { return guid; } set { guid = value; } } - public string Name { get { return name; } set { name = value; } } - public GamePadConfigurationItem A { get { return a; } set { a = value; } } - public GamePadConfigurationItem B { get { return b; } set { b = value; } } - public GamePadConfigurationItem X { get { return x; } set { x = value; } } - public GamePadConfigurationItem Y { get { return y; } set { y = value; } } - public GamePadConfigurationItem LeftShoulder { get { return lshoulder; } set { lshoulder = value; } } - public GamePadConfigurationItem RightShoulder { get { return rshoulder; } set { rshoulder = value; } } - public GamePadConfigurationItem LeftStick { get { return lstick; } set { lstick = value; } } - public GamePadConfigurationItem RightStick { get { return rstick; } set { rstick = value; } } - public GamePadConfigurationItem Start { get { return start; } set { start = value; } } - public GamePadConfigurationItem Back { get { return back; } set { back = value; } } - public GamePadConfigurationItem BigButton { get { return home; } set { home = value; } } - public GamePadConfigurationItem LeftAxisX { get { return axis_lx; } set { axis_lx = value; } } - public GamePadConfigurationItem LeftAxisY { get { return axis_ly; } set { axis_ly = value; } } - public GamePadConfigurationItem RightAxisX { get { return axis_rx; } set { axis_rx = value; } } - public GamePadConfigurationItem RightAxisY { get { return axis_ry; } set { axis_ry = value; } } - public GamePadConfigurationItem LeftTrigger { get { return ltrigger; } set { ltrigger = value; } } - public GamePadConfigurationItem RightTrigger { get { return rtrigger; } set { rtrigger = value; } } - public GamePadConfigurationItem DPadUp { get { return dpad_u; } set { dpad_u = value; } } - public GamePadConfigurationItem DPadDown { get { return dpad_d; } set { dpad_d = value; } } - public GamePadConfigurationItem DPadLeft { get { return dpad_l; } set { dpad_l = value; } } - public GamePadConfigurationItem DPadRight { get { return dpad_r; } set { dpad_r = value; } } - - public GamePadConfiguration() + public Guid Guid { + get { return guid; } + private set { guid = value; } + } + + public string Name + { + get { return name; } + private set { name = value; } } public GamePadConfiguration(string configuration) { + ParseConfiguration(configuration); } - static GamePadConfiguration() + public List.Enumerator GetEnumerator() { - GamePadConfiguration map = new GamePadConfiguration(); - map.A = new GamePadConfigurationItem(JoystickButton.Button0); - map.B = new GamePadConfigurationItem(JoystickButton.Button1); - map.X = new GamePadConfigurationItem(JoystickButton.Button2); - map.Y = new GamePadConfigurationItem(JoystickButton.Button3); - map.LeftShoulder = new GamePadConfigurationItem(JoystickButton.Button4); - map.RightShoulder = new GamePadConfigurationItem(JoystickButton.Button5); - map.LeftStick = new GamePadConfigurationItem(JoystickButton.Button6); - map.RightStick = new GamePadConfigurationItem(JoystickButton.Button7); - map.Start = new GamePadConfigurationItem(JoystickButton.Button8); - map.Back = new GamePadConfigurationItem(JoystickButton.Button9); - map.BigButton = new GamePadConfigurationItem(JoystickButton.Button10); - map.DPadUp = new GamePadConfigurationItem(JoystickButton.Button11); - map.DPadDown = new GamePadConfigurationItem(JoystickButton.Button12); - map.DPadLeft = new GamePadConfigurationItem(JoystickButton.Button13); - map.DPadRight = new GamePadConfigurationItem(JoystickButton.Button14); - map.LeftAxisX = new GamePadConfigurationItem(JoystickAxis.Axis0); - map.LeftAxisY = new GamePadConfigurationItem(JoystickAxis.Axis1); - map.RightAxisX = new GamePadConfigurationItem(JoystickAxis.Axis2); - map.RightAxisY = new GamePadConfigurationItem(JoystickAxis.Axis3); - map.LeftTrigger = new GamePadConfigurationItem(JoystickAxis.Axis4); - map.RightTrigger = new GamePadConfigurationItem(JoystickAxis.Axis5); - default_map = map; + return configuration_items.GetEnumerator(); } - public static GamePadConfiguration Default - { - get { return default_map; } - } - - public static GamePadConfiguration GetConfiguration(Guid guid) - { - GamePadConfiguration map = GamePadConfiguration.Default; - if (GamePadConfigurationDatabase.Configurations.ContainsKey(guid)) - { - map = ParseConfiguration(GamePadConfigurationDatabase.Configurations[guid]); - } - return map; - } + #region Private Members // Parses a GamePad configuration string. The string // follows the rules for SDL2 GameController, outlined here: // http://wiki.libsdl.org/SDL_GameControllerAddMapping - static GamePadConfiguration ParseConfiguration(string configuration) + void ParseConfiguration(string configuration) { if (String.IsNullOrEmpty(configuration)) { @@ -140,128 +82,97 @@ namespace OpenTK.Input // - name is a human-readable name for the controller // - config is a comma-separated list of configurations as follows: // - [gamepad axis or button name]:[joystick axis, button or hat number] - string[] items = configuration.Split(','); + string[] items = configuration.Split(ConfigurationSeparator, StringSplitOptions.RemoveEmptyEntries); if (items.Length < 3) { throw new ArgumentException(); } - GamePadConfiguration map = new GamePadConfiguration(); + GamePadConfiguration map = this; map.Guid = new Guid(items[0]); map.Name = items[1]; for (int i = 2; i < items.Length; i++) { string[] config = items[i].Split(':'); - GamePadConfigurationItem map_item = ParseItem(config[1]); - switch (config[0]) - { - case "a": - map.A = map_item; - break; - - case "b": - map.B = map_item; - break; - - case "x": - map.X = map_item; - break; - - case "y": - map.Y = map_item; - break; - - case "start": - map.Start = map_item; - break; - - case "back": - map.Back = map_item; - break; - - case "guide": - map.BigButton = map_item; - break; - - case "dpup": - map.DPadUp = map_item; - break; - - case "dpdown": - map.DPadDown = map_item; - break; - - case "dpleft": - map.DPadLeft = map_item; - break; - - case "dpright": - map.DPadRight = map_item; - break; - - case "leftshoulder": - map.LeftShoulder = map_item; - break; - - case "rightshoulder": - map.RightShoulder = map_item; - break; - - case "leftstick": - map.LeftStick = map_item; - break; - - case "rightstick": - map.RightStick = map_item; - break; - - case "leftx": - map.LeftAxisX = map_item; - break; - - case "lefty": - map.LeftAxisY = map_item; - break; - - case "rightx": - map.RightAxisX = map_item; - break; - - case "righty": - map.RightAxisY = map_item; - break; - - case "lefttrigger": - map.LeftTrigger = map_item; - break; - - case "righttrigger": - map.RightTrigger = map_item; - break; - - default: - Debug.Print("[Input] Invalid GamePad configuration name: {0}", items[i]); - break; - } + GamePadConfigurationTarget target = ParseTarget(config[0]); + GamePadConfigurationSource source = ParseSource(config[1]); + configuration_items.Add(new GamePadConfigurationItem(source, target)); } - - return map; } - static GamePadConfigurationItem ParseItem(string item) + static GamePadConfigurationTarget ParseTarget(string target) + { + switch (target) + { + // Buttons + case "a": + return new GamePadConfigurationTarget(Buttons.A); + case "b": + return new GamePadConfigurationTarget(Buttons.B); + case "x": + return new GamePadConfigurationTarget(Buttons.X); + case "y": + return new GamePadConfigurationTarget(Buttons.Y); + case "start": + return new GamePadConfigurationTarget(Buttons.Start); + case "back": + return new GamePadConfigurationTarget(Buttons.Back); + case "guide": + return new GamePadConfigurationTarget(Buttons.BigButton); + case "leftshoulder": + return new GamePadConfigurationTarget(Buttons.LeftShoulder); + case "rightshoulder": + return new GamePadConfigurationTarget(Buttons.RightShoulder); + case "leftstick": + return new GamePadConfigurationTarget(Buttons.LeftStick); + case "rightstick": + return new GamePadConfigurationTarget(Buttons.RightStick); + case "dpup": + return new GamePadConfigurationTarget(Buttons.DPadUp); + case "dpdown": + return new GamePadConfigurationTarget(Buttons.DPadDown); + case "dpleft": + return new GamePadConfigurationTarget(Buttons.DPadLeft); + case "dpright": + return new GamePadConfigurationTarget(Buttons.DPadRight); + + // Axes + case "leftx": + return new GamePadConfigurationTarget(GamePadAxes.LeftX); + case "lefty": + return new GamePadConfigurationTarget(GamePadAxes.LeftY); + case "rightx": + return new GamePadConfigurationTarget(GamePadAxes.RightX); + case "righty": + return new GamePadConfigurationTarget(GamePadAxes.RightY); + + // Triggers + case "lefttrigger": + return new GamePadConfigurationTarget(GamePadAxes.LeftTrigger); + case "righttrigger": + return new GamePadConfigurationTarget(GamePadAxes.RightTrigger); + + + // Unmapped + default: + return new GamePadConfigurationTarget(); + } + } + + static GamePadConfigurationSource ParseSource(string item) { if (String.IsNullOrEmpty(item)) { - return new GamePadConfigurationItem(); + return new GamePadConfigurationSource(); } switch (item[0]) { case 'a': - return new GamePadConfigurationItem(ParseAxis(item)); + return new GamePadConfigurationSource(ParseAxis(item)); case 'b': - return new GamePadConfigurationItem(ParseButton(item)); + return new GamePadConfigurationSource(ParseButton(item)); case 'h': throw new NotImplementedException(); @@ -287,5 +198,7 @@ namespace OpenTK.Input int id = Int32.Parse(item.Substring(1)); return button + id; } + + #endregion } } From b5d42b79925cfd2d05d56ba2caebfd437ecfb147 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 18:37:53 +0100 Subject: [PATCH 177/245] [Input] Implemented GetState() and simplified GetCapabilities() --- Source/OpenTK/Platform/MappedGamePadDriver.cs | 112 +++++++++++------- 1 file changed, 69 insertions(+), 43 deletions(-) diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index 97cb1fae..afe839c4 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -61,13 +61,59 @@ namespace OpenTK.Platform { JoystickState joy = Joystick.GetState(index); GamePadState pad = new GamePadState(); + if (joy.IsConnected) { - GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index)); + pad.SetConnected(true); + GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index)); + foreach (GamePadConfigurationItem map in configuration) + { + switch (map.Source.Type) + { + case ConfigurationType.Axis: + { + // JoystickAxis -> Buttons/GamePadAxes mapping + JoystickAxis source_axis = map.Source.Axis; + GamePadAxes target_axis = map.Target.Axis; + short value = joy.GetAxisRaw(source_axis); - // Todo: implement mapping + switch (map.Target.Type) + { + case ConfigurationType.Axis: + pad.SetAxis(target_axis, value); + break; + + case ConfigurationType.Button: + throw new NotImplementedException(); + break; + } + } + break; + + case ConfigurationType.Button: + { + // JoystickButton -> Buttons/GamePadAxes mapping + JoystickButton source_button = map.Source.Button; + Buttons target_button = map.Target.Button; + bool pressed = joy.GetButton(source_button) == ButtonState.Pressed; + + switch (map.Target.Type) + { + case ConfigurationType.Axis: + throw new NotImplementedException(); + break; + + case ConfigurationType.Button: + pad.SetButton(target_button, pressed); + break; + } + } + break; + } + } } + return pad; } @@ -77,11 +123,28 @@ namespace OpenTK.Platform GamePadCapabilities pad; if (joy.IsConnected) { - GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index)); + GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index)); + GamePadAxes mapped_axes = 0; + Buttons mapped_buttons = 0; + + foreach (GamePadConfigurationItem map in configuration) + { + switch (map.Target.Type) + { + case ConfigurationType.Axis: + mapped_axes |= map.Target.Axis; + break; + + case ConfigurationType.Button: + mapped_buttons |= map.Target.Button; + break; + } + } + pad = new GamePadCapabilities( GamePadType.GamePad, // Todo: detect different types - TranslateAxes(map), - TranslateButtons(map), + mapped_axes, + mapped_buttons, true); } else @@ -116,48 +179,11 @@ namespace OpenTK.Platform return configurations[guid]; } - bool IsMapped(GamePadConfigurationItem item) + bool IsMapped(GamePadConfigurationSource item) { return item.Type != ConfigurationType.Unmapped; } - GamePadAxes TranslateAxes(GamePadConfiguration map) - { - GamePadAxes axes = 0; - axes |= IsMapped(map.LeftAxisX) ? GamePadAxes.LeftX : 0; - axes |= IsMapped(map.LeftAxisY) ? GamePadAxes.LeftY : 0; - axes |= IsMapped(map.RightAxisX) ? GamePadAxes.RightX : 0; - axes |= IsMapped(map.RightAxisY) ? GamePadAxes.RightY : 0; - axes |= IsMapped(map.LeftTrigger) ? GamePadAxes.LeftTrigger : 0; - axes |= IsMapped(map.RightTrigger) ? GamePadAxes.RightTrigger : 0; - return axes; - } - - Buttons TranslateButtons(GamePadConfiguration map) - { - Buttons buttons = 0; - buttons |= IsMapped(map.A) ? Buttons.A : 0; - buttons |= IsMapped(map.B) ? Buttons.B : 0; - buttons |= IsMapped(map.X) ? Buttons.X : 0; - buttons |= IsMapped(map.Y) ? Buttons.Y : 0; - buttons |= IsMapped(map.Start) ? Buttons.Start : 0; - buttons |= IsMapped(map.Back) ? Buttons.Back : 0; - buttons |= IsMapped(map.BigButton) ? Buttons.BigButton : 0; - buttons |= IsMapped(map.LeftShoulder) ? Buttons.LeftShoulder : 0; - buttons |= IsMapped(map.RightShoulder) ? Buttons.RightShoulder : 0; - buttons |= IsMapped(map.LeftStick) ? Buttons.LeftStick : 0; - buttons |= IsMapped(map.RightStick) ? Buttons.RightStick : 0; - return buttons; - } - -// bool TranslateDPad(GamePadMap map) -// { -// pad.HasDPadDownButton = IsMapped(map.DPadDown); -// pad.HasDPadUpButton = IsMapped(map.DPadUp); -// pad.HasDPadLeftButton = IsMapped(map.DPadLeft); -// pad.HasDPadRightButton = IsMapped(map.DPadRight); -// } - #endregion } } From 1ccf7e5ad22d7d0f7e67575e0f65945e49758b48 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 19:24:15 +0100 Subject: [PATCH 178/245] [Input] Added PacketNumber property --- Source/OpenTK/Input/GamePadState.cs | 11 +++++++++++ Source/OpenTK/Input/JoystickState.cs | 11 +++++++++++ Source/OpenTK/Platform/MappedGamePadDriver.cs | 2 ++ 3 files changed, 24 insertions(+) diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index 179762b3..60318dae 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -37,6 +37,7 @@ namespace OpenTK.Input const float RangeMultiplier = 1.0f / (short.MaxValue + 1); Buttons buttons; + int packet_number; short left_stick_x; short left_stick_y; short right_stick_x; @@ -72,6 +73,11 @@ namespace OpenTK.Input get { return is_connected; } } + public int PacketNumber + { + get { return packet_number; } + } + public override string ToString() { return String.Format( @@ -167,6 +173,11 @@ namespace OpenTK.Input right_trigger = right; } + internal void SetPacketNumber(int number) + { + packet_number = number; + } + #endregion #region Private Members diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index 7f1624d8..e8e2abf2 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -45,6 +45,7 @@ namespace OpenTK.Input unsafe fixed short axes[MaxAxes]; int buttons; + int packet_number; bool is_connected; #region Public Members @@ -79,6 +80,11 @@ namespace OpenTK.Input get { return is_connected; } } + internal int PacketNumber + { + get { return packet_number; } + } + public override string ToString() { StringBuilder sb = new StringBuilder(); @@ -167,6 +173,11 @@ namespace OpenTK.Input is_connected = value; } + internal void SetPacketNumber(int number) + { + packet_number = number; + } + #endregion #region Private Members diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index afe839c4..20da30a8 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -65,6 +65,8 @@ namespace OpenTK.Platform if (joy.IsConnected) { pad.SetConnected(true); + pad.SetPacketNumber(joy.PacketNumber); + GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index)); foreach (GamePadConfigurationItem map in configuration) From b513e35ea8a3169754a993447fd7ed1bf4c96714 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 19:24:38 +0100 Subject: [PATCH 179/245] [SDL2] Implemented PacketNumber property --- Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index 25b6b253..faff9226 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -42,6 +42,7 @@ namespace OpenTK.Platform.SDL2 { public IntPtr Handle { get; set; } public Guid Guid { get; set; } + public int PacketNumber { get; set; } public int HatCount { get; set; } public int BallCount { get; set; } public bool IsConnected { get; set; } @@ -73,7 +74,6 @@ namespace OpenTK.Platform.SDL2 readonly Dictionary sdl_instanceid_to_controllers = new Dictionary(); #endif - public Sdl2JoystickDriver() { joysticks_readonly = joysticks.AsReadOnly(); @@ -339,6 +339,7 @@ namespace OpenTK.Platform.SDL2 JoystickDevice joystick = (JoystickDevice)joysticks[index]; float value = ev.Value * RangeMultiplier; joystick.SetAxis((JoystickAxis)ev.Axis, value); + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); } else { @@ -354,6 +355,7 @@ namespace OpenTK.Platform.SDL2 int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; // Todo: does it make sense to support balls? + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); } else { @@ -369,6 +371,7 @@ namespace OpenTK.Platform.SDL2 int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; joystick.SetButton((JoystickButton)ev.Button, ev.State == State.Pressed); + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); } else { @@ -384,6 +387,7 @@ namespace OpenTK.Platform.SDL2 int index = sdl_instanceid_to_joysticks[id]; JoystickDevice joystick = (JoystickDevice)joysticks[index]; // Todo: map hat to an extra axis + joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1)); } else { @@ -588,6 +592,7 @@ namespace OpenTK.Platform.SDL2 } state.SetIsConnected(joystick.Details.IsConnected); + state.SetPacketNumber(joystick.Details.PacketNumber); } return state; From ec43b9ff850c2a9e684591c407824fa95ebbf025 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 19:27:04 +0100 Subject: [PATCH 180/245] [Input] GamePad and Joystick classes should be sealed --- Source/OpenTK/Input/GamePad.cs | 4 +++- Source/OpenTK/Input/Joystick.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index a0a63f49..8f8468d1 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -32,7 +32,7 @@ namespace OpenTK.Input /// /// Provides access to GamePad devices. /// - public class GamePad + public sealed class GamePad { internal const int MaxAxisCount = 10; internal const int MaxDPadCount = 2; @@ -40,6 +40,8 @@ namespace OpenTK.Input static readonly IGamePadDriver driver = Platform.Factory.Default.CreateGamePadDriver(); + private GamePad() { } + /// /// Retrieves a GamePadCapabilities structure describing the /// capabilities of a gamepad device. diff --git a/Source/OpenTK/Input/Joystick.cs b/Source/OpenTK/Input/Joystick.cs index 2b10cfb9..7f1037e1 100644 --- a/Source/OpenTK/Input/Joystick.cs +++ b/Source/OpenTK/Input/Joystick.cs @@ -33,11 +33,13 @@ using System.Text; namespace OpenTK.Input { - public class Joystick + public sealed class Joystick { static readonly IJoystickDriver2 implementation = Platform.Factory.Default.CreateJoystickDriver(); + private Joystick() { } + public static JoystickCapabilities GetCapabilities(int index) { return implementation.GetCapabilities(index); From 8649e4a044f16e52465dccf200b353b209a6f781 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 2 Jan 2014 19:52:00 +0100 Subject: [PATCH 181/245] [Input] Added SetVibration() API skeleton --- Source/OpenTK/Input/GamePad.cs | 17 +++++++++++++++++ Source/OpenTK/Input/IGamePadDriver.cs | 2 ++ Source/OpenTK/Platform/MappedGamePadDriver.cs | 5 +++++ .../OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs | 5 +++++ .../OpenTK/Platform/Windows/XInputJoystick.cs | 7 ++++++- Source/OpenTK/Platform/X11/X11Joystick.cs | 17 +++++++++++------ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index 8f8468d1..50df2667 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -65,5 +65,22 @@ namespace OpenTK.Input { return driver.GetState(index); } + + /// + /// Sets the vibration intensity for the left and right motors of this + /// + /// + /// true, if vibration was set, false otherwise. This method can return false + /// if the GamePad hardware does not support vibration or if it cannot respond to + /// the command for any reason. Do not loop until this becomes true, but rather ignore + /// a return value of false. + /// + /// A zero-based device index for the GamePad device to affect + /// The vibration intensity for the left motor, between 0.0 and 1.0. + /// The vibration intensity for the right motor, between 0.0 and 1.0. + public static bool SetVibration(int index, float left, float right) + { + return driver.SetVibration(index, left, right); + } } } diff --git a/Source/OpenTK/Input/IGamePadDriver.cs b/Source/OpenTK/Input/IGamePadDriver.cs index fc24c8f8..6852dedf 100644 --- a/Source/OpenTK/Input/IGamePadDriver.cs +++ b/Source/OpenTK/Input/IGamePadDriver.cs @@ -19,5 +19,7 @@ namespace OpenTK.Input /// /// If no device exists at the specified index, the return value is . string GetName(int index); + + bool SetVibration(int index, float left, float right); } } diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs index 20da30a8..12f918c9 100644 --- a/Source/OpenTK/Platform/MappedGamePadDriver.cs +++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs @@ -168,6 +168,11 @@ namespace OpenTK.Platform return name; } + public bool SetVibration(int index, float left, float right) + { + return false; + } + #region Private Members GamePadConfiguration GetConfiguration(Guid guid) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs index faff9226..8a08151a 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2JoystickDriver.cs @@ -567,6 +567,11 @@ namespace OpenTK.Platform.SDL2 { return gamepad_driver.GetName(index); } + + public bool SetVibration(int index, float left, float right) + { + return false; + } #endif #endregion diff --git a/Source/OpenTK/Platform/Windows/XInputJoystick.cs b/Source/OpenTK/Platform/Windows/XInputJoystick.cs index 23710df0..910a7f01 100644 --- a/Source/OpenTK/Platform/Windows/XInputJoystick.cs +++ b/Source/OpenTK/Platform/Windows/XInputJoystick.cs @@ -87,7 +87,12 @@ namespace OpenTK.Platform.Windows public string GetName(int index) { - throw new NotImplementedException(); + return String.Empty; + } + + public bool SetVibration(int index, float left, float right) + { + return false; } #endregion diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index 5f76dd54..cbab7099 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -263,17 +263,22 @@ namespace OpenTK.Platform.X11 public GamePadCapabilities GetCapabilities(int index) { - throw new NotImplementedException(); + return new GamePadCapabilities(); } public GamePadState GetState(int index) { - throw new NotImplementedException(); + return new GamePadState(); } public string GetName(int index) { - throw new NotImplementedException(); + return String.Empty; + } + + public bool SetVibration(int index, float left, float right) + { + return false; } #endregion @@ -282,17 +287,17 @@ namespace OpenTK.Platform.X11 JoystickState IJoystickDriver2.GetState(int index) { - throw new NotImplementedException(); + return new JoystickState(); } JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) { - throw new NotImplementedException(); + return new JoystickCapabilities(); } Guid IJoystickDriver2.GetGuid(int index) { - throw new NotImplementedException(); + return new Guid(); } #endregion From b35aad15034b2541560928f334a54f6ef4027d52 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 3 Jan 2014 01:58:08 +0100 Subject: [PATCH 182/245] [Input] Removed JoystickState.GetAxis(int) overload --- Source/OpenTK/Input/JoystickState.cs | 69 +++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index e8e2abf2..f00a5521 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -34,6 +34,9 @@ using System.Text; namespace OpenTK.Input { + /// + /// Describes the current state of a . + /// public struct JoystickState : IEquatable { // If we ever add more values to JoystickAxis or JoystickButton @@ -50,48 +53,70 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets a value between -1.0 and 1.0 representing the current offset of the specified . + /// + /// + /// A value between -1.0 and 1.0 representing offset of the specified + /// to query the number of available axes. + /// + /// The to query. public float GetAxis(JoystickAxis axis) - { - return GetAxis((int)axis); - } - - public float GetAxis(int axis) { return GetAxisRaw(axis) * ConversionFactor; } + /// + /// Gets the current of the specified . + /// + /// if the specified button is pressed; otherwise, . + /// The to query. public ButtonState GetButton(JoystickButton button) { return (buttons & (1 << (int)button)) != 0 ? ButtonState.Pressed : ButtonState.Released; } + /// + /// Gets a value indicating whether the specified is currently pressed. + /// + /// true if the specified button is pressed; otherwise, false. + /// The to query. public bool IsButtonDown(JoystickButton button) { return (buttons & (1 << (int)button)) != 0; } + /// + /// Gets a value indicating whether the specified is currently released. + /// + /// true if the specified button is released; otherwise, false. + /// The to query. public bool IsButtonUp(JoystickButton button) { return (buttons & (1 << (int)button)) == 0; } + /// + /// Gets a value indicating whether this instance is connected. + /// + /// true if this instance is connected; otherwise, false. public bool IsConnected { get { return is_connected; } } - internal int PacketNumber - { - get { return packet_number; } - } - + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < MaxAxes; i++) { sb.Append(" "); - sb.Append(String.Format("{0:f4}", GetAxis(i))); + sb.Append(String.Format("{0:f4}", GetAxis(JoystickAxis.Axis0 + i))); } return String.Format( "{{Axes:{0}; Buttons: {1}; IsConnected: {2}}}", @@ -100,6 +125,11 @@ namespace OpenTK.Input IsConnected); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { int hash = buttons.GetHashCode() ^ IsConnected.GetHashCode(); @@ -110,6 +140,12 @@ namespace OpenTK.Input return hash; } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -121,6 +157,11 @@ namespace OpenTK.Input #region Internal Members + internal int PacketNumber + { + get { return packet_number; } + } + internal short GetAxisRaw(JoystickAxis axis) { return GetAxisRaw((int)axis); @@ -197,6 +238,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(JoystickState other) { bool equals = From 3095afa18a10a758204a131621765923361cd7fe Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 3 Jan 2014 01:58:16 +0100 Subject: [PATCH 183/245] [Input] Documented all public members --- Source/OpenTK/Input/Buttons.cs | 2 +- Source/OpenTK/Input/GamePad.cs | 62 +++++++++------ Source/OpenTK/Input/GamePadButtons.cs | 76 +++++++++++++++++- Source/OpenTK/Input/GamePadDPad.cs | 87 ++++++++++++++++++++- Source/OpenTK/Input/GamePadState.cs | 49 +++++++++++- Source/OpenTK/Input/GamePadThumbSticks.cs | 39 ++++++++- Source/OpenTK/Input/GamePadTriggers.cs | 34 ++++++++ Source/OpenTK/Input/GamePadType.cs | 50 ++++++++++++ Source/OpenTK/Input/Joystick.cs | 29 +++++++ Source/OpenTK/Input/JoystickCapabilities.cs | 36 ++++++++- 10 files changed, 426 insertions(+), 38 deletions(-) diff --git a/Source/OpenTK/Input/Buttons.cs b/Source/OpenTK/Input/Buttons.cs index 878c738f..319e2183 100644 --- a/Source/OpenTK/Input/Buttons.cs +++ b/Source/OpenTK/Input/Buttons.cs @@ -28,7 +28,7 @@ using System; namespace OpenTK.Input { /// - /// Enumerates available buttons for a canonical GamePad device. + /// Enumerates available buttons for a GamePad device. /// [Flags] public enum Buttons diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index 50df2667..11213597 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -1,29 +1,31 @@ - #region License - // - // The Open Toolkit Library License - // - // Copyright (c) 2006 - 2009 the Open Toolkit library. - // - // Permission is hereby granted, free of charge, to any person obtaining a copy - // of this software and associated documentation files (the "Software"), to deal - // in the Software without restriction, including without limitation the rights to - // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - // the Software, and to permit persons to whom the Software is furnished to do - // so, subject to the following conditions: - // - // The above copyright notice and this permission notice shall be included in all - // copies or substantial portions of the Software. - // - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - // OTHER DEALINGS IN THE SOFTWARE. - // - #endregion +#region License +// +// GamePadButtons.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion using System; @@ -31,6 +33,14 @@ namespace OpenTK.Input { /// /// Provides access to GamePad devices. + /// A GamePad device offers a well-defined layout with + /// one direction-pad, two thumbsticks, two triggers, + /// four main buttons (A, B, X, Y) and up to seven + /// auxilliary buttons. + /// Use GetCapabilities to retrieve the exact + /// capabilities of a given device. + /// Use GetState to retrieve the current state + /// of a given device. /// public sealed class GamePad { diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs index c13a9f0c..676fb1f9 100644 --- a/Source/OpenTK/Input/GamePadButtons.cs +++ b/Source/OpenTK/Input/GamePadButtons.cs @@ -1,11 +1,11 @@ -// #region License +#region License // // GamePadButtons.cs // // Author: // Stefanos A. // -// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// Copyright (c) 2006-2014 Stefanos Apostolopoulos // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,16 +25,23 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// #endregion +#endregion + using System; namespace OpenTK.Input { - + /// + /// Describes the of . + /// public struct GamePadButtons : IEquatable { Buttons buttons; + /// + /// Initializes a new instance of the structure. + /// + /// A bitmask containing the button state. public GamePadButtons(Buttons state) { buttons = state; @@ -42,81 +49,136 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets the for the A button. + /// public ButtonState A { get { return GetButton(Buttons.A); } } + /// + /// Gets the for the B button. + /// public ButtonState B { get { return GetButton(Buttons.B); } } + /// + /// Gets the for the X button. + /// public ButtonState X { get { return GetButton(Buttons.X); } } + /// + /// Gets the for the Y button. + /// public ButtonState Y { get { return GetButton(Buttons.Y); } } + /// + /// Gets the for the Back button. + /// public ButtonState Back { get { return GetButton(Buttons.Back); } } + /// + /// Gets the for the big button. + /// This button is also known as Home or Guide. + /// public ButtonState BigButton { get { return GetButton(Buttons.BigButton); } } + /// + /// Gets the for the left shoulder button. + /// public ButtonState LeftShoulder { get { return GetButton(Buttons.LeftShoulder); } } + /// + /// Gets the for the left stick button. + /// This button represents a left stick that is pressed in. + /// public ButtonState LeftStick { get { return GetButton(Buttons.LeftStick); } } + /// + /// Gets the for the right shoulder button. + /// public ButtonState RightShoulder { get { return GetButton(Buttons.RightShoulder); } } + /// + /// Gets the for the right stick button. + /// This button represents a right stick that is pressed in. + /// public ButtonState RightStick { get { return GetButton(Buttons.RightStick); } } + /// + /// Gets the for the starth button. + /// public ButtonState Start { get { return GetButton(Buttons.Start); } } + /// A instance to test for equality. + /// A instance to test for equality. public static bool operator ==(GamePadButtons left, GamePadButtons right) { return left.Equals(right); } + /// A instance to test for inequality. + /// A instance to test for inequality. public static bool operator !=(GamePadButtons left, GamePadButtons right) { return !left.Equals(right); } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return Convert.ToString((int)buttons, 2).PadLeft(10, '0'); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return buttons.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -128,6 +190,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(GamePadButtons other) { return buttons == other.buttons; diff --git a/Source/OpenTK/Input/GamePadDPad.cs b/Source/OpenTK/Input/GamePadDPad.cs index 8e1a0588..d05300ef 100644 --- a/Source/OpenTK/Input/GamePadDPad.cs +++ b/Source/OpenTK/Input/GamePadDPad.cs @@ -31,7 +31,9 @@ using System; namespace OpenTK.Input { - + /// + /// Describes the state of a directional pad. + /// public struct GamePadDPad : IEquatable { [Flags] @@ -45,7 +47,7 @@ namespace OpenTK.Input DPadButtons buttons; - #region Public Members + #region Internal Members internal GamePadDPad(Buttons state) { @@ -54,40 +56,104 @@ namespace OpenTK.Input buttons = (DPadButtons)((int)state & 0x0f); } + #endregion + + #region Public Members + + /// + /// Gets the for the up button. + /// + /// ButtonState.Pressed if the up button is pressed; otherwise, ButtonState.Released. + public ButtonState Up + { + get { return IsUp ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the for the down button. + /// + /// ButtonState.Pressed if the down button is pressed; otherwise, ButtonState.Released. + public ButtonState Down + { + get { return IsDown ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the for the left button. + /// + /// ButtonState.Pressed if the left button is pressed; otherwise, ButtonState.Released. + public ButtonState Left + { + get { return IsLeft ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets the for the right button. + /// + /// ButtonState.Pressed if the right button is pressed; otherwise, ButtonState.Released. + public ButtonState Right + { + get { return IsRight ? ButtonState.Pressed : ButtonState.Released; } + } + + /// + /// Gets a value indicating whether the up button is pressed. + /// + /// true if the up button is pressed; otherwise, false. public bool IsUp { get { return (buttons & DPadButtons.Up) != 0; } internal set { SetButton(DPadButtons.Up, value); } } + /// + /// Gets a value indicating whether the down button is pressed. + /// + /// true if the down button is pressed; otherwise, false. public bool IsDown { get { return (buttons & DPadButtons.Down) != 0; } internal set { SetButton(DPadButtons.Down, value); } } + /// + /// Gets a value indicating whether the left button is pressed. + /// + /// true if the left button is pressed; otherwise, false. public bool IsLeft { get { return (buttons & DPadButtons.Left) != 0; } internal set { SetButton(DPadButtons.Left, value); } } + /// + /// Gets a value indicating whether the right button is pressed. + /// + /// true if the right button is pressed; otherwise, false. public bool IsRight { get { return (buttons & DPadButtons.Right) != 0; } internal set { SetButton(DPadButtons.Right, value); } } + /// A instance to test for equality. + /// A instance to test for equality. public static bool operator ==(GamePadDPad left, GamePadDPad right) { return left.Equals(right); } + /// A instance to test for inequality. + /// A instance to test for inequality. public static bool operator !=(GamePadDPad left, GamePadDPad right) { return !left.Equals(right); } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return String.Format( @@ -98,11 +164,22 @@ namespace OpenTK.Input IsRight ? "R" : String.Empty); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return buttons.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -130,6 +207,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(GamePadDPad other) { return buttons == other.buttons; diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs index 60318dae..7d0f0630 100644 --- a/Source/OpenTK/Input/GamePadState.cs +++ b/Source/OpenTK/Input/GamePadState.cs @@ -30,7 +30,7 @@ using System; namespace OpenTK.Input { /// - /// Encapsulates the state of a GamePad device. + /// Describes the current state of a device. /// public struct GamePadState : IEquatable { @@ -48,36 +48,65 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets a structure describing the + /// state of the GamePad thumb sticks. + /// public GamePadThumbSticks ThumbSticks { get { return new GamePadThumbSticks(left_stick_x, left_stick_y, right_stick_x, right_stick_y); } } + /// + /// Gets a structure describing the + /// state of the GamePad buttons. + /// public GamePadButtons Buttons { get { return new GamePadButtons(buttons); } } + /// + /// Gets a structure describing the + /// state of the GamePad directional pad. + /// public GamePadDPad DPad { get { return new GamePadDPad(buttons); } } + /// + /// Gets a structure describing the + /// state of the GamePad triggers. + /// public GamePadTriggers Triggers { get { return new GamePadTriggers(left_trigger, right_trigger); } } + /// + /// Gets a value indicating whether this GamePad instance is connected. + /// + /// true if this instance is connected; otherwise, false. public bool IsConnected { get { return is_connected; } } + /// + /// Gets the packet number for this GamePadState instance. + /// Use the packet number to determine whether the state of a + /// GamePad device has changed. + /// public int PacketNumber { get { return packet_number; } } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return String.Format( @@ -85,6 +114,11 @@ namespace OpenTK.Input ThumbSticks, Buttons, DPad, IsConnected); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return @@ -92,6 +126,12 @@ namespace OpenTK.Input DPad.GetHashCode() ^ IsConnected.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -102,6 +142,13 @@ namespace OpenTK.Input #endregion #region IEquatable Members + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(GamePadState other) { return diff --git a/Source/OpenTK/Input/GamePadThumbSticks.cs b/Source/OpenTK/Input/GamePadThumbSticks.cs index 63838c6f..42fd2c0f 100644 --- a/Source/OpenTK/Input/GamePadThumbSticks.cs +++ b/Source/OpenTK/Input/GamePadThumbSticks.cs @@ -1,4 +1,4 @@ -// #region License +#region License // // GamePadThumbSticks.cs // @@ -25,13 +25,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // -// #endregion - +#endregion using System; namespace OpenTK.Input { + /// + /// Describes the current thumb stick state of a device + /// public struct GamePadThumbSticks : IEquatable { const float ConversionFactor = 1.0f / short.MaxValue; @@ -50,26 +52,40 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets a describing the state of the left thumb stick. + /// public Vector2 Left { get { return new Vector2(left_x * ConversionFactor, left_y * ConversionFactor); } } + /// + /// Gets a describing the state of the right thumb stick. + /// public Vector2 Right { get { return new Vector2(right_x * ConversionFactor, right_y * ConversionFactor); } } + /// A instance to test for equality. + /// A instance to test for equality. public static bool operator ==(GamePadThumbSticks left, GamePadThumbSticks right) { return left.Equals(right); } + /// A instance to test for inequality. + /// A instance to test for inequality. public static bool operator !=(GamePadThumbSticks left, GamePadThumbSticks right) { return !left.Equals(right); } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return String.Format( @@ -77,6 +93,11 @@ namespace OpenTK.Input Left.X, Left.Y, Right.X, Right.Y); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return @@ -84,6 +105,12 @@ namespace OpenTK.Input right_x.GetHashCode() ^ right_y.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -95,6 +122,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(GamePadThumbSticks other) { return diff --git a/Source/OpenTK/Input/GamePadTriggers.cs b/Source/OpenTK/Input/GamePadTriggers.cs index 0e505d81..df9df1a7 100644 --- a/Source/OpenTK/Input/GamePadTriggers.cs +++ b/Source/OpenTK/Input/GamePadTriggers.cs @@ -32,6 +32,9 @@ using System; namespace OpenTK.Input { + /// + /// Describes the state of a trigger buttons. + /// public struct GamePadTriggers : IEquatable { const float ConversionFactor = 1.0f / short.MaxValue; @@ -46,26 +49,40 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets the offset of the left trigger button, between 0.0 and 1.0. + /// public float Left { get { return left * ConversionFactor; } } + /// + /// Gets the offset of the left trigger button, between 0.0 and 1.0. + /// public float Right { get { return right * ConversionFactor; } } + /// A instance to test for equality. + /// A instance to test for equality. public static bool operator ==(GamePadTriggers left, GamePadTriggers right) { return left.Equals(right); } + /// A instance to test for equality. + /// A instance to test for equality. public static bool operator !=(GamePadTriggers left, GamePadTriggers right) { return !left.Equals(right); } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return String.Format( @@ -73,12 +90,23 @@ namespace OpenTK.Input Left, Right); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return left.GetHashCode() ^ right.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -90,6 +118,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(GamePadTriggers other) { return diff --git a/Source/OpenTK/Input/GamePadType.cs b/Source/OpenTK/Input/GamePadType.cs index 37311627..1464117f 100644 --- a/Source/OpenTK/Input/GamePadType.cs +++ b/Source/OpenTK/Input/GamePadType.cs @@ -29,19 +29,69 @@ namespace OpenTK.Input { + /// + /// Enumerates available types. + /// public enum GamePadType { + /// + /// The GamePad is of an unknown type. + /// Unknown = 0, + + /// + /// The GamePad is an arcade stick. + /// ArcadeStick, + + /// + /// The GamePad is a dance pad. + /// DancePad, + + /// + /// The GamePad is a flight stick. + /// FlightStick, + + /// + /// The GamePad is a guitar. + /// Guitar, + + /// + /// The GamePad is a driving wheel. + /// Wheel, + + /// + /// The GamePad is an alternate guitar. + /// AlternateGuitar, + + /// + /// The GamePad is a big button pad. + /// BigButtonPad, + + /// + /// The GamePad is a drum kit. + /// DrumKit, + + /// + /// The GamePad is a game pad. + /// GamePad, + + /// + /// The GamePad is an arcade pad. + /// ArcadePad, + + /// + /// The GamePad is a bass guitar. + /// BassGuitar, } } diff --git a/Source/OpenTK/Input/Joystick.cs b/Source/OpenTK/Input/Joystick.cs index 7f1037e1..d280815c 100644 --- a/Source/OpenTK/Input/Joystick.cs +++ b/Source/OpenTK/Input/Joystick.cs @@ -33,6 +33,14 @@ using System.Text; namespace OpenTK.Input { + /// + /// Provides access to Joystick devices. + /// Joystick devices provide a varying number of axes and buttons. + /// Use GetCapabilities to retrieve the number of supported + /// axes and buttons on a given device. + /// Use GetState to retrieve the current state of a given device. + /// + /// public sealed class Joystick { static readonly IJoystickDriver2 implementation = @@ -40,11 +48,32 @@ namespace OpenTK.Input private Joystick() { } + /// + /// Retrieves the of the device connected + /// at the specified index. + /// + /// + /// A structure describing + /// the capabilities of the device at the specified index. + /// If no device is connected at the specified index, the IsConnected + /// property of the returned structure will be false. + /// + /// The zero-based index of the device to poll. public static JoystickCapabilities GetCapabilities(int index) { return implementation.GetCapabilities(index); } + /// + /// Retrieves the of the device connected + /// at the specified index. + /// + /// A structure describing + /// the current state of the device at the specified index. + /// If no device is connected at this index, the IsConnected + /// property of the returned structure will be false. + /// + /// The zero-based index of the device to poll. public static JoystickState GetState(int index) { return implementation.GetState(index); diff --git a/Source/OpenTK/Input/JoystickCapabilities.cs b/Source/OpenTK/Input/JoystickCapabilities.cs index 80036211..3bfab7a1 100644 --- a/Source/OpenTK/Input/JoystickCapabilities.cs +++ b/Source/OpenTK/Input/JoystickCapabilities.cs @@ -33,6 +33,9 @@ using System.Text; namespace OpenTK.Input { + /// + /// Describes the JoystickCapabilities of a . + /// public struct JoystickCapabilities : IEquatable { byte axis_count; @@ -42,7 +45,7 @@ namespace OpenTK.Input #region Constructors - public JoystickCapabilities(int axis_count, int button_count, bool is_connected) + internal JoystickCapabilities(int axis_count, int button_count, bool is_connected) { if (axis_count < 0 || axis_count >= JoystickState.MaxAxes) throw new ArgumentOutOfRangeException("axis_count"); @@ -59,21 +62,35 @@ namespace OpenTK.Input #region Public Members + /// + /// Gets the number of axes supported by this . + /// public int AxisCount { get { return axis_count; } } + /// + /// Gets the number of buttons supported by this . + /// public int ButtonCount { get { return button_count; } } + /// + /// Gets a value indicating whether this is connected. + /// + /// true if this instance is connected; otherwise, false. public bool IsConnected { get { return is_connected; } } + /// + /// Returns a that represents the current . + /// + /// A that represents the current . public override string ToString() { return String.Format( @@ -81,6 +98,11 @@ namespace OpenTK.Input AxisCount, ButtonCount, IsConnected); } + /// + /// Serves as a hash function for a object. + /// + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a + /// hash table. public override int GetHashCode() { return @@ -89,6 +111,12 @@ namespace OpenTK.Input IsConnected.GetHashCode(); } + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public override bool Equals(object obj) { return @@ -109,6 +137,12 @@ namespace OpenTK.Input #region IEquatable Members + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current + /// ; otherwise, false. public bool Equals(JoystickCapabilities other) { return From 53552b0070d6573bf3b43f70588fd1eccb08b384 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 3 Jan 2014 02:17:42 +0100 Subject: [PATCH 184/245] [Input] Corrected malformed documentation comments --- Source/OpenTK/Input/GamePad.cs | 2 +- Source/OpenTK/Input/JoystickState.cs | 7 +++---- Source/OpenTK/Platform/SDL2/Sdl2.cs | 2 +- Source/OpenTK/ToolkitOptions.cs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/OpenTK/Input/GamePad.cs b/Source/OpenTK/Input/GamePad.cs index 11213597..7578eb23 100644 --- a/Source/OpenTK/Input/GamePad.cs +++ b/Source/OpenTK/Input/GamePad.cs @@ -1,6 +1,6 @@ #region License // -// GamePadButtons.cs +// GamePad.cs // // Author: // Stefanos A. diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index f00a5521..f5f533a2 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -5,7 +5,7 @@ // Author: // Stefanos A. // -// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// Copyright (c) 2006-2014 Stefanos Apostolopoulos // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,6 @@ #endregion using System; -using System.Collections.Generic; using System.Diagnostics; using System.Text; @@ -57,8 +56,8 @@ namespace OpenTK.Input /// Gets a value between -1.0 and 1.0 representing the current offset of the specified . /// /// - /// A value between -1.0 and 1.0 representing offset of the specified + /// A value between -1.0 and 1.0 representing offset of the specified . + /// If the specified axis does not exist, then the return value is 0.0. Use /// to query the number of available axes. /// /// The to query. diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 38ebef43..7989410b 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -140,7 +140,7 @@ namespace OpenTK.Platform.SDL2 /// Gets the SDL joystick layer binding for the specified game controller button /// /// Pointer to a game controller instance returned by GameControllerOpen. - /// A value from the GameControllerButton enumeration + /// A value from the GameControllerButton enumeration /// A GameControllerButtonBind instance describing the specified binding [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GameControllerGetBindForButton", ExactSpelling = true)] diff --git a/Source/OpenTK/ToolkitOptions.cs b/Source/OpenTK/ToolkitOptions.cs index 91a16a9a..5541a2bb 100644 --- a/Source/OpenTK/ToolkitOptions.cs +++ b/Source/OpenTK/ToolkitOptions.cs @@ -84,7 +84,7 @@ namespace OpenTK /// Set to false for applications that are not /// DPI-aware (e.g. WinForms.) /// - /// + /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ee308410(v=vs.85).aspx public bool EnableHighResolution { get; set; } /// From 514390fc9a0843f8e0b65dbf77e9442e45d475b5 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Fri, 3 Jan 2014 02:18:28 +0100 Subject: [PATCH 185/245] [Graphics] Added missing documentation comment --- Source/OpenTK/Graphics/GraphicsContext.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs index 1fd39b2d..970bdab8 100644 --- a/Source/OpenTK/Graphics/GraphicsContext.cs +++ b/Source/OpenTK/Graphics/GraphicsContext.cs @@ -644,6 +644,11 @@ namespace OpenTK.Graphics } } + /// + /// Marks this context as deleted, but does not actually release unmanaged resources + /// due to the threading requirements of OpenGL. Use + /// instead. + /// ~GraphicsContext() { Dispose(false); From 9659a1d78610fc647967b47693d85aa5d22f6b27 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 4 Jan 2014 23:55:38 +0100 Subject: [PATCH 186/245] [Mac] Add skeleton HID input implementation for joysticks --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 140 ++++++++++++++++------- 1 file changed, 99 insertions(+), 41 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 9090e703..5322e30e 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -60,11 +60,17 @@ namespace OpenTK.Platform.MacOS new Dictionary(new IntPtrEqualityComparer()); readonly Dictionary MouseIndexToDevice = new Dictionary(); + readonly Dictionary KeyboardDevices = new Dictionary(new IntPtrEqualityComparer()); readonly Dictionary KeyboardIndexToDevice = new Dictionary(); + readonly Dictionary JoystickDevices = + new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary JoystickIndexToDevice = + new Dictionary(); + readonly CFRunLoop RunLoop = CF.CFRunLoopGetMain(); readonly CFString InputLoopMode = CF.RunLoopModeDefault; readonly CFDictionary DeviceTypes = new CFDictionary(); @@ -126,41 +132,19 @@ namespace OpenTK.Platform.MacOS if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse)) { - if (!MouseDevices.ContainsKey(device)) - { - Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender); - MouseState state = new MouseState(); - state.IsConnected = true; - MouseIndexToDevice.Add(MouseDevices.Count, device); - MouseDevices.Add(device, state); - } - else - { - Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender); - MouseState state = MouseDevices[device]; - state.IsConnected = true; - MouseDevices[device] = state; - } + AddMouse(sender, device); } if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard)) { - if (!KeyboardDevices.ContainsKey(device)) - { - Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender); - KeyboardState state = new KeyboardState(); - state.IsConnected = true; - KeyboardIndexToDevice.Add(KeyboardDevices.Count, device); - KeyboardDevices.Add(device, state); - } - else - { - Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender); - KeyboardState state = KeyboardDevices[device]; - state.IsConnected = true; - KeyboardDevices[device] = state; - } + AddKeyboard(sender, device); + } + + if (NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick)) + { + AddJoystick(sender, device); } // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include @@ -178,23 +162,19 @@ namespace OpenTK.Platform.MacOS if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && MouseDevices.ContainsKey(device)) { - Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender); - - // Keep the device in case it comes back later on - MouseState state = MouseDevices[device]; - state.IsConnected = false; - MouseDevices[device] = state; + RemoveMouse(sender, device); } if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && KeyboardDevices.ContainsKey(device)) { - Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender); + RemoveKeyboard(sender, device); + } - // Keep the device in case it comes back later on - KeyboardState state = KeyboardDevices[device]; - state.IsConnected = false; - KeyboardDevices[device] = state; + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick) && + JoystickDevices.ContainsKey(device)) + { + RemoveJoystick(sender, device); } NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); @@ -224,6 +204,36 @@ namespace OpenTK.Platform.MacOS } } + #region Mouse + + void AddMouse(CFAllocatorRef sender, CFAllocatorRef device) + { + if (!MouseDevices.ContainsKey(device)) + { + Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender); + MouseState state = new MouseState(); + state.IsConnected = true; + MouseIndexToDevice.Add(MouseDevices.Count, device); + MouseDevices.Add(device, state); + } + else + { + Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender); + MouseState state = MouseDevices[device]; + state.IsConnected = true; + MouseDevices[device] = state; + } + } + + void RemoveMouse(CFAllocatorRef sender, CFAllocatorRef device) + { + Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender); + // Keep the device in case it comes back later on + MouseState state = MouseDevices[device]; + state.IsConnected = false; + MouseDevices[device] = state; + } + static MouseState UpdateMouse(MouseState state, IOHIDValueRef val) { IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); @@ -260,6 +270,38 @@ namespace OpenTK.Platform.MacOS return state; } + #endregion + + #region Keyboard + + void AddKeyboard(CFAllocatorRef sender, CFAllocatorRef device) + { + if (!KeyboardDevices.ContainsKey(device)) + { + Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender); + KeyboardState state = new KeyboardState(); + state.IsConnected = true; + KeyboardIndexToDevice.Add(KeyboardDevices.Count, device); + KeyboardDevices.Add(device, state); + } + else + { + Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender); + KeyboardState state = KeyboardDevices[device]; + state.IsConnected = true; + KeyboardDevices[device] = state; + } + } + + void RemoveKeyboard(CFAllocatorRef sender, CFAllocatorRef device) + { + Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender); + // Keep the device in case it comes back later on + KeyboardState state = KeyboardDevices[device]; + state.IsConnected = false; + KeyboardDevices[device] = state; + } + static KeyboardState UpdateKeyboard(KeyboardState state, IOHIDValueRef val) { IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); @@ -289,6 +331,22 @@ namespace OpenTK.Platform.MacOS #endregion + #region Joystick + + void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device) + { + throw new NotImplementedException(); + } + + void RemoveJoystick(CFAllocatorRef sender, CFAllocatorRef device) + { + throw new NotImplementedException(); + } + + #endregion + + #endregion + #region IInputDriver2 Members public IMouseDriver2 MouseDriver { get { return this; } } From c44b477388a8ee416504c364bcf0598d987c1573 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sat, 4 Jan 2014 23:58:59 +0100 Subject: [PATCH 187/245] [Mac] Implement device added/removed events for joysticks --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 5322e30e..0d2fab01 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -335,12 +335,30 @@ namespace OpenTK.Platform.MacOS void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device) { - throw new NotImplementedException(); + if (!JoystickDevices.ContainsKey(device)) + { + Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender); + JoystickState state = new JoystickState(); + state.SetIsConnected(true); + JoystickIndexToDevice.Add(KeyboardDevices.Count, device); + JoystickDevices.Add(device, state); + } + else + { + Debug.Print("Joystick device {0:x} reconnected, sender is {1:x}", device, sender); + JoystickState state = JoystickDevices[device]; + state.SetIsConnected(true); + JoystickDevices[device] = state; + } } void RemoveJoystick(CFAllocatorRef sender, CFAllocatorRef device) { - throw new NotImplementedException(); + Debug.Print("Joystick device {0:x} disconnected, sender is {1:x}", device, sender); + // Keep the device in case it comes back later on + JoystickState state = JoystickDevices[device]; + state.SetIsConnected(false); + JoystickDevices[device] = state; } #endregion From bf8efea121e5ddfb7bfcf3a908881fcb7adaf882 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 00:00:49 +0100 Subject: [PATCH 188/245] [Mac] Only add callbacks for recognized USB HID devices --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 0d2fab01..27c4c283 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -127,58 +127,74 @@ namespace OpenTK.Platform.MacOS void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) { + bool recognized = false; + if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero) { if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse)) { AddMouse(sender, device); + recognized = true; } if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard)) { AddKeyboard(sender, device); + recognized = true; } if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick)) { AddJoystick(sender, device); + recognized = true; } - // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include - // the device identifier as the context variable, so we can identify it and figure out the device later. - // Thanks to Jase: http://www.opentk.com/node/2800 - NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, - HandleDeviceValueReceived, device); + if (recognized) + { + // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include + // the device identifier as the context variable, so we can identify it and figure out the device later. + // Thanks to Jase: http://www.opentk.com/node/2800 + NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, + HandleDeviceValueReceived, device); - NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode); + NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode); + } } } void DeviceRemoved(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) { + bool recognized = false; + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && MouseDevices.ContainsKey(device)) { RemoveMouse(sender, device); + recognized = true; } if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && KeyboardDevices.ContainsKey(device)) { RemoveKeyboard(sender, device); + recognized = true; } if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick) && JoystickDevices.ContainsKey(device)) { RemoveJoystick(sender, device); + recognized = true; } - NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); - NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode); + if (recognized) + { + NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); + NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode); + } } void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val) From 3757db99787a9d4668f9e1787b73db9282465638 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 00:46:59 +0100 Subject: [PATCH 189/245] [Input] Allow extending JoystickDevice<> --- Source/OpenTK/Input/JoystickDevice.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Input/JoystickDevice.cs b/Source/OpenTK/Input/JoystickDevice.cs index 55a92bef..8e239f56 100644 --- a/Source/OpenTK/Input/JoystickDevice.cs +++ b/Source/OpenTK/Input/JoystickDevice.cs @@ -155,7 +155,7 @@ namespace OpenTK.Input #region JoystickDevice : JoystickDevice // Provides platform-specific information about the relevant JoystickDevice. - internal sealed class JoystickDevice : JoystickDevice + internal class JoystickDevice : JoystickDevice where TDetail : new() { internal JoystickDevice(int id, int axes, int buttons) From 32653d2c641dd672e1a746f0cd378da25752b481 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 00:47:40 +0100 Subject: [PATCH 190/245] [Mac] Construct JoystickDevice when joysticks are connected --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 70 +++++++++++++++++++----- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 27c4c283..49a38e64 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -54,6 +54,19 @@ namespace OpenTK.Platform.MacOS { #region Fields + class JoystickDetails + { + public JoystickState State; + public JoystickCapabilities Capabilities; + } + + class MacJoystick : JoystickDevice + { + internal MacJoystick(int id, int axes, int buttons) + : base(id, axes, buttons) + { } + } + readonly IOHIDManagerRef hidmanager; readonly Dictionary MouseDevices = @@ -66,8 +79,8 @@ namespace OpenTK.Platform.MacOS readonly Dictionary KeyboardIndexToDevice = new Dictionary(); - readonly Dictionary JoystickDevices = - new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary JoystickDevices = + new Dictionary(new IntPtrEqualityComparer()); readonly Dictionary JoystickIndexToDevice = new Dictionary(); @@ -208,6 +221,7 @@ namespace OpenTK.Platform.MacOS MouseState mouse; KeyboardState keyboard; + MacJoystick joystick; if (MouseDevices.TryGetValue(context, out mouse)) { MouseDevices[context] = UpdateMouse(mouse, val); @@ -215,7 +229,13 @@ namespace OpenTK.Platform.MacOS else if (KeyboardDevices.TryGetValue(context, out keyboard)) { KeyboardDevices[context] = UpdateKeyboard(keyboard, val); - }else{ + } + else if (JoystickDevices.TryGetValue(context, out joystick)) + { + JoystickDevices[context] = UpdateJoystick(joystick, val); + } + else + { //Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender); } } @@ -349,22 +369,31 @@ namespace OpenTK.Platform.MacOS #region Joystick + MacJoystick CreateJoystick(CFAllocatorRef sender, CFAllocatorRef device) + { + MacJoystick joy = new MacJoystick(-1, 0, 0); + joy.Details.State.SetIsConnected(true); + + // Todo: discover joystick capabilities + joy.Details.Capabilities = new JoystickCapabilities(0, 0, true); + + return joy; + } + void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device) { if (!JoystickDevices.ContainsKey(device)) { Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender); - JoystickState state = new JoystickState(); - state.SetIsConnected(true); - JoystickIndexToDevice.Add(KeyboardDevices.Count, device); - JoystickDevices.Add(device, state); + MacJoystick joy = CreateJoystick(sender, device); + JoystickIndexToDevice.Add(JoystickDevices.Count, device); + JoystickDevices.Add(device, joy); } else { Debug.Print("Joystick device {0:x} reconnected, sender is {1:x}", device, sender); - JoystickState state = JoystickDevices[device]; - state.SetIsConnected(true); - JoystickDevices[device] = state; + JoystickDevices[device].Details.State.SetIsConnected(true); + //JoystickDevices[device].Details.Capabilities.SetIsConnected(true); } } @@ -372,9 +401,24 @@ namespace OpenTK.Platform.MacOS { Debug.Print("Joystick device {0:x} disconnected, sender is {1:x}", device, sender); // Keep the device in case it comes back later on - JoystickState state = JoystickDevices[device]; - state.SetIsConnected(false); - JoystickDevices[device] = state; + JoystickDevices[device].Details.State.SetIsConnected(false); + //JoystickDevices[device].Details.Capabilities.SetIsConnected(false); + } + + static MacJoystick UpdateJoystick(MacJoystick state, IOHIDValueRef val) + { + //IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); + //int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); + //HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem); + //int usage = NativeMethods.IOHIDElementGetUsage(elem); + + //switch (page) + //{ + // case HIDPage.GenericDesktop: + // break; + //} + + return state; } #endregion From 6dadbd3570c51ce9218eb43fd48e92566a6aada3 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 02:42:33 +0100 Subject: [PATCH 191/245] [Mac] Implemented necessary CFString methods --- .../MacOS/CarbonBindings/CoreFoundation.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs index f6c9b156..a4d6527b 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs @@ -32,7 +32,10 @@ using System.Text; namespace OpenTK.Platform.MacOS.Carbon { + using CFIndex = System.IntPtr; using CFRunLoop = System.IntPtr; + using CFStringRef = System.IntPtr; + using CFTypeRef = System.IntPtr; struct CFArray { @@ -106,6 +109,9 @@ namespace OpenTK.Platform.MacOS.Carbon [DllImport(appServices)] internal static extern IntPtr CFDictionaryGetValue(IntPtr theDictionary, IntPtr theKey); + [DllImport(appServices)] + internal static extern void CFRelease(CFTypeRef cf); + // this mirrors the definition in CFString.h. // I don't know why, but __CFStringMakeConstantString is marked as "private and should not be used directly" // even though the CFSTR macro just calls it. @@ -116,6 +122,33 @@ namespace OpenTK.Platform.MacOS.Carbon return __CFStringMakeConstantString(cStr); } + [DllImport(appServices)] + internal static extern Boolean CFStringGetCString( + CFStringRef theString, + byte[] buffer, + CFIndex bufferSize, + CFStringEncoding encoding + ); + + internal static string CFStringGetCString(IntPtr cfstr) + { + CFIndex length = CFStringGetLength(cfstr); + if (length != IntPtr.Zero) + { + byte[] utf8_chars = new byte[length.ToInt32() + 1]; + if (CFStringGetCString(cfstr, utf8_chars, new IntPtr(utf8_chars.Length), CFStringEncoding.UTF8)) + { + return Encoding.UTF8.GetString(utf8_chars); + } + } + return String.Empty; + } + + [DllImport(appServices)] + internal static extern CFIndex CFStringGetLength( + CFStringRef theString + ); + [DllImport(appServices)] internal unsafe static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, int* valuePtr); [DllImport(appServices)] @@ -150,6 +183,25 @@ namespace OpenTK.Platform.MacOS.Carbon HandledSource = 4 } + public enum CFStringEncoding + { + MacRoman = 0, + WindowsLatin1 = 0x0500, + ISOLatin1 = 0x0201, + NextStepLatin = 0x0B01, + ASCII = 0x0600, + Unicode = 0x0100, + UTF8 = 0x08000100, + NonLossyASCII = 0x0BFF, + + UTF16 = 0x0100, + UTF16BE = 0x10000100, + UTF16LE = 0x14000100, + UTF32 = 0x0c000100, + UTF32BE = 0x18000100, + UTF32LE = 0x1c000100 + } + public static readonly IntPtr RunLoopModeDefault = CF.CFSTR("kCFRunLoopDefaultMode"); [DllImport(appServices)] From 07bcda0f57a1c8523e6f9bfdc09f15973a03e208 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 02:42:47 +0100 Subject: [PATCH 192/245] [Mac] Retrieve HID joystick name --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 37 ++++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 49a38e64..2a8fb486 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -35,6 +35,7 @@ namespace OpenTK.Platform.MacOS { using Carbon; using CFAllocatorRef = System.IntPtr; + using CFArrayRef = System.IntPtr; using CFDictionaryRef = System.IntPtr; using CFIndex = System.IntPtr; using CFRunLoop = System.IntPtr; @@ -58,6 +59,7 @@ namespace OpenTK.Platform.MacOS { public JoystickState State; public JoystickCapabilities Capabilities; + public string Name; } class MacJoystick : JoystickDevice @@ -371,11 +373,24 @@ namespace OpenTK.Platform.MacOS MacJoystick CreateJoystick(CFAllocatorRef sender, CFAllocatorRef device) { - MacJoystick joy = new MacJoystick(-1, 0, 0); - joy.Details.State.SetIsConnected(true); + MacJoystick joy = null; - // Todo: discover joystick capabilities - joy.Details.Capabilities = new JoystickCapabilities(0, 0, true); + // Retrieve all elements of this device + CFArrayRef element_array = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero, IntPtr.Zero); + if (element_array != IntPtr.Zero) + { + CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey); + string name = CF.CFStringGetCString(name_ref); + CF.CFRelease(name_ref); + + joy = new MacJoystick(-1, 0, 0); + joy.Details.Name = name; + joy.Details.State.SetIsConnected(true); + joy.Details.Capabilities = new JoystickCapabilities(0, 0, true); + + //NativeMethods.IOHIDDeviceGetProperty(device, nativem + } + CF.CFRelease(element_array); return joy; } @@ -386,8 +401,11 @@ namespace OpenTK.Platform.MacOS { Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender); MacJoystick joy = CreateJoystick(sender, device); - JoystickIndexToDevice.Add(JoystickDevices.Count, device); - JoystickDevices.Add(device, joy); + if (joy != null) + { + JoystickIndexToDevice.Add(JoystickDevices.Count, device); + JoystickDevices.Add(device, joy); + } } else { @@ -602,6 +620,13 @@ namespace OpenTK.Platform.MacOS HIDPage inUsagePage, // the usage page to test conformance with int inUsage); // the usage to test conformance with + // return the HID elements that match the criteria contained in the matching dictionary + [DllImport(hid)] + public static extern CFArrayRef IOHIDDeviceCopyMatchingElements( + IOHIDDeviceRef inIOHIDDeviceRef, // IOHIDDeviceRef for the HID device + CFDictionaryRef inMatchingCFDictRef, // the matching dictionary + IOOptionBits inOptions); // Option bits + [DllImport(hid)] public static extern void IOHIDDeviceRegisterInputValueCallback( IOHIDDeviceRef device, From 59249c262464af9aa25d33f8c633de22714796d3 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 03:03:46 +0100 Subject: [PATCH 193/245] [Mac] Implemented joystick device detection --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 164 ++++++++++++++++++++++- 1 file changed, 159 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 2a8fb486..d3334467 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -376,25 +376,90 @@ namespace OpenTK.Platform.MacOS MacJoystick joy = null; // Retrieve all elements of this device - CFArrayRef element_array = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero, IntPtr.Zero); - if (element_array != IntPtr.Zero) + CFArrayRef element_array_ref = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero, IntPtr.Zero); + if (element_array_ref != IntPtr.Zero) { + int axes = 0; + int buttons = 0; + int dpads = 0; + CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey); string name = CF.CFStringGetCString(name_ref); CF.CFRelease(name_ref); - joy = new MacJoystick(-1, 0, 0); + CFArray element_array = new CFArray(element_array_ref); + for (int i = 0; i < element_array.Count; i++) + { + IOHIDElementRef element_ref = element_array[i]; + IOHIDElementType type = NativeMethods.IOHIDElementGetType(element_ref); + HIDPage page = NativeMethods.IOHIDElementGetUsagePage(element_ref); + int usage = NativeMethods.IOHIDElementGetUsage(element_ref); + + switch (page) + { + case HIDPage.GenericDesktop: + switch ((HIDUsageGD)usage) + { + case HIDUsageGD.X: + case HIDUsageGD.Y: + case HIDUsageGD.Z: + case HIDUsageGD.Rx: + case HIDUsageGD.Ry: + case HIDUsageGD.Rz: + case HIDUsageGD.Slider: + case HIDUsageGD.Dial: + case HIDUsageGD.Wheel: + axes++; + break; + + case HIDUsageGD.Hatswitch: + dpads++; + break; + } + break; + + case HIDPage.Simulation: + switch ((HIDUsageSim)usage) + { + case HIDUsageSim.Rudder: + case HIDUsageSim.Throttle: + axes++; + break; + } + break; + + case HIDPage.Button: + buttons++; + break; + } + } + + joy = new MacJoystick(-1, axes, buttons); joy.Details.Name = name; joy.Details.State.SetIsConnected(true); - joy.Details.Capabilities = new JoystickCapabilities(0, 0, true); + joy.Details.Capabilities = new JoystickCapabilities(axes, buttons, true); //NativeMethods.IOHIDDeviceGetProperty(device, nativem } - CF.CFRelease(element_array); + CF.CFRelease(element_array_ref); return joy; } + MacJoystick GetJoystick(int index) + { + IntPtr device; + if (JoystickIndexToDevice.TryGetValue(index, out device)) + { + MacJoystick joystick; + if (JoystickDevices.TryGetValue(device, out joystick)) + { + return joystick; + } + } + return null; + } + void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device) { if (!JoystickDevices.ContainsKey(device)) @@ -527,16 +592,31 @@ namespace OpenTK.Platform.MacOS JoystickState IJoystickDriver2.GetState(int index) { + MacJoystick joystick = GetJoystick(index); + if (joystick != null) + { + return joystick.Details.State; + } return new JoystickState(); } JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) { + MacJoystick joystick = GetJoystick(index); + if (joystick != null) + { + return joystick.Details.Capabilities; + } return new JoystickCapabilities(); } Guid IJoystickDriver2.GetGuid(int index) { + MacJoystick joystick = GetJoystick(index); + if (joystick != null) + { + //return joystick.Details.Capabilities; + } return new Guid(); } @@ -662,6 +742,10 @@ namespace OpenTK.Platform.MacOS IOHIDValueRef @value, IOHIDValueScaleType type) ; + [DllImport(hid)] + public static extern IOHIDElementType IOHIDElementGetType( + IOHIDElementRef element); + [DllImport(hid)] public static extern int IOHIDElementGetUsage(IOHIDElementRef elem); @@ -672,6 +756,17 @@ namespace OpenTK.Platform.MacOS public delegate void IOHIDValueCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDValueRef val); } + enum IOHIDElementType + { + Input_Misc = 1, + Input_Button = 2, + Input_Axis = 3, + Input_ScanCodes = 4, + Output = 129, + Feature = 257, + Collection = 513 + } + enum IOHIDValueScaleType { Physical, // [device min, device max] @@ -776,6 +871,65 @@ namespace OpenTK.Platform.MacOS Reserved = 0xFFFF } + enum HIDUsageSim + { + FlightSimulationDevice = 0x01, /* Application Collection */ + AutomobileSimulationDevice = 0x02, /* Application Collection */ + TankSimulationDevice = 0x03, /* Application Collection */ + SpaceshipSimulationDevice = 0x04, /* Application Collection */ + SubmarineSimulationDevice = 0x05, /* Application Collection */ + SailingSimulationDevice = 0x06, /* Application Collection */ + MotorcycleSimulationDevice = 0x07, /* Application Collection */ + SportsSimulationDevice = 0x08, /* Application Collection */ + AirplaneSimulationDevice = 0x09, /* Application Collection */ + HelicopterSimulationDevice = 0x0A, /* Application Collection */ + MagicCarpetSimulationDevice = 0x0B, /* Application Collection */ + BicycleSimulationDevice = 0x0C, /* Application Collection */ + /* 0x0D - 0x1F Reserved */ + FlightControlStick = 0x20, /* Application Collection */ + FlightStick = 0x21, /* Application Collection */ + CyclicControl = 0x22, /* Physical Collection */ + CyclicTrim = 0x23, /* Physical Collection */ + FlightYoke = 0x24, /* Application Collection */ + TrackControl = 0x25, /* Physical Collection */ + /* 0x26 - 0xAF Reserved */ + Aileron = 0xB0, /* Dynamic Value */ + AileronTrim = 0xB1, /* Dynamic Value */ + AntiTorqueControl = 0xB2, /* Dynamic Value */ + AutopilotEnable = 0xB3, /* On/Off Control */ + ChaffRelease = 0xB4, /* One-Shot Control */ + CollectiveControl = 0xB5, /* Dynamic Value */ + DiveBrake = 0xB6, /* Dynamic Value */ + ElectronicCountermeasures = 0xB7, /* On/Off Control */ + Elevator = 0xB8, /* Dynamic Value */ + ElevatorTrim = 0xB9, /* Dynamic Value */ + Rudder = 0xBA, /* Dynamic Value */ + Throttle = 0xBB, /* Dynamic Value */ + FlightCommunications = 0xBC, /* On/Off Control */ + FlareRelease = 0xBD, /* One-Shot Control */ + LandingGear = 0xBE, /* On/Off Control */ + ToeBrake = 0xBF, /* Dynamic Value */ + Trigger = 0xC0, /* Momentary Control */ + WeaponsArm = 0xC1, /* On/Off Control */ + Weapons = 0xC2, /* Selector */ + WingFlaps = 0xC3, /* Dynamic Value */ + Accelerator = 0xC4, /* Dynamic Value */ + Brake = 0xC5, /* Dynamic Value */ + Clutch = 0xC6, /* Dynamic Value */ + Shifter = 0xC7, /* Dynamic Value */ + Steering = 0xC8, /* Dynamic Value */ + TurretDirection = 0xC9, /* Dynamic Value */ + BarrelElevation = 0xCA, /* Dynamic Value */ + DivePlane = 0xCB, /* Dynamic Value */ + Ballast = 0xCC, /* Dynamic Value */ + BicycleCrank = 0xCD, /* Dynamic Value */ + HandleBars = 0xCE, /* Dynamic Value */ + FrontBrake = 0xCF, /* Dynamic Value */ + RearBrake = 0xD0, /* Dynamic Value */ + /* 0xD1 - 0xFFFF Reserved */ + Reserved = 0xFFFF + } + enum HIDButton { Button_1 = 0x01, /* (primary/trigger) */ From 92635b3f3566d04f5620c6b98a70aae6395526eb Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 03:54:34 +0100 Subject: [PATCH 194/245] [Input] Added JoystickAxis/Button.Last; added JoystickAxis.Axis10. JoystickAxis/Button.Last is used internally to allocate the correct amount of storage for joystick axes and buttons. JoystickAxis.Axis10 is required to support the maximum number of axes available on Mac OS X. --- Source/OpenTK/Input/JoystickAxis.cs | 6 +++++- Source/OpenTK/Input/JoystickButton.cs | 2 ++ Source/OpenTK/Input/JoystickState.cs | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/OpenTK/Input/JoystickAxis.cs b/Source/OpenTK/Input/JoystickAxis.cs index 1e166e66..751ec7bd 100644 --- a/Source/OpenTK/Input/JoystickAxis.cs +++ b/Source/OpenTK/Input/JoystickAxis.cs @@ -5,7 +5,7 @@ // Author: // Stefanos A. // -// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// Copyright (c) 2006-2014 Stefanos Apostolopoulos // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -58,5 +58,9 @@ namespace OpenTK.Input Axis8, /// The tenth axis of the JoystickDevice. Axis9, + /// The eleventh axis of the JoystickDevice. + Axis10, + /// The highest supported axis of the JoystickDevice. + Last = Axis10, } } diff --git a/Source/OpenTK/Input/JoystickButton.cs b/Source/OpenTK/Input/JoystickButton.cs index e8daf6ed..2ab2b88a 100644 --- a/Source/OpenTK/Input/JoystickButton.cs +++ b/Source/OpenTK/Input/JoystickButton.cs @@ -70,5 +70,7 @@ namespace OpenTK.Input Button14, /// The sixteenth button of the JoystickDevice. Button15, + /// The last supported button of the JoystickDevice. + Last = Button15, } } diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs index f5f533a2..8005b096 100644 --- a/Source/OpenTK/Input/JoystickState.cs +++ b/Source/OpenTK/Input/JoystickState.cs @@ -40,8 +40,8 @@ namespace OpenTK.Input { // 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; + internal const int MaxAxes = (int)JoystickAxis.Last; + internal const int MaxButtons = (int)JoystickButton.Last; const float ConversionFactor = 1.0f / (short.MaxValue + 0.5f); From fad42994e15a3c393c70a2379acf6e9a2987fc39 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 03:54:53 +0100 Subject: [PATCH 195/245] [Mac] Implemented joystick axis movement --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 127 +++++++++++++++++++++-- 1 file changed, 116 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index d3334467..06c9972a 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -488,20 +488,119 @@ namespace OpenTK.Platform.MacOS //JoystickDevices[device].Details.Capabilities.SetIsConnected(false); } - static MacJoystick UpdateJoystick(MacJoystick state, IOHIDValueRef val) + static MacJoystick UpdateJoystick(MacJoystick joy, IOHIDValueRef val) { - //IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); - //int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); - //HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem); - //int usage = NativeMethods.IOHIDElementGetUsage(elem); + IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); + HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem); + int usage = NativeMethods.IOHIDElementGetUsage(elem); - //switch (page) - //{ - // case HIDPage.GenericDesktop: - // break; - //} + switch (page) + { + case HIDPage.GenericDesktop: + switch ((HIDUsageGD)usage) + { + case HIDUsageGD.X: + case HIDUsageGD.Y: + case HIDUsageGD.Z: + case HIDUsageGD.Rx: + case HIDUsageGD.Ry: + case HIDUsageGD.Rz: + case HIDUsageGD.Slider: + case HIDUsageGD.Dial: + case HIDUsageGD.Wheel: + short offset = GetJoystickAxis(val, elem); + joy.Details.State.SetAxis(TranslateJoystickAxis(usage), offset); + break; + + case HIDUsageGD.Hatswitch: + break; + } + break; + + case HIDPage.Simulation: + switch ((HIDUsageSim)usage) + { + case HIDUsageSim.Rudder: + case HIDUsageSim.Throttle: + short offset = GetJoystickAxis(val, elem); + joy.Details.State.SetAxis(TranslateJoystickAxis(usage), offset); + break; + } + break; + + case HIDPage.Button: + { + bool pressed = GetJoystickButton(val, elem); + joy.Details.State.SetButton(TranslateJoystickButton(usage), value); + } + break; + } + + return joy; + } + + static short GetJoystickAxis(IOHIDValueRef val, IOHIDElementRef element) + { + int max = NativeMethods.IOHIDElementGetLogicalMax(element).ToInt32(); + int min = NativeMethods.IOHIDElementGetLogicalMin(element).ToInt32(); + int offset = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); + if (offset < min) + offset = min; + if (offset > max) + offset = max; + + const int range = short.MaxValue - short.MinValue + 1; + const int half_range = short.MaxValue + 1; + return (short)((offset - min) * range / (max - min) + half_range); + } + + static JoystickAxis TranslateJoystickAxis(int usage) + { + switch (usage) + { + case (int)HIDUsageGD.X: + return JoystickAxis.Axis0; + case (int)HIDUsageGD.Y: + return JoystickAxis.Axis1; + + case (int)HIDUsageGD.Z: + return JoystickAxis.Axis2; + case (int)HIDUsageGD.Rz: + return JoystickAxis.Axis3; + + case (int)HIDUsageGD.Rx: + return JoystickAxis.Axis4; + case (int)HIDUsageGD.Ry: + return JoystickAxis.Axis5; + + case (int)HIDUsageGD.Slider: + return JoystickAxis.Axis6; + case (int)HIDUsageGD.Dial: + return JoystickAxis.Axis7; + case (int)HIDUsageGD.Wheel: + return JoystickAxis.Axis8; + + case (int)HIDUsageSim.Rudder: + return JoystickAxis.Axis9; + case (int)HIDUsageSim.Throttle: + return JoystickAxis.Axis10; + + default: + Debug.Print("[Mac] Unknown axis with HID usage {0}", usage); + return 0; + } + } + + static bool GetJoystickButton(IOHIDValueRef val, IOHIDElementRef element) + { + // Todo: analogue buttons are transformed to digital + int value = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); + return value >= 1; + } + + static JoystickButton TranslateJoystickButton(int usage) + { - return state; } #endregion @@ -752,6 +851,12 @@ namespace OpenTK.Platform.MacOS [DllImport(hid)] public static extern HIDPage IOHIDElementGetUsagePage(IOHIDElementRef elem); + [DllImport(hid)] + public static extern CFIndex IOHIDElementGetLogicalMax(IOHIDElementRef element); + + [DllImport(hid)] + public static extern CFIndex IOHIDElementGetLogicalMin(IOHIDElementRef element); + public delegate void IOHIDDeviceCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDDeviceRef device); public delegate void IOHIDValueCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDValueRef val); } From 2ee24efb2f3206f5c5c9e9bcb603d16efc81b5c8 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 04:11:11 +0100 Subject: [PATCH 196/245] [Mac] Implemented joystick buttons --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 40 +++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 06c9972a..e90ecf58 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -57,9 +57,11 @@ namespace OpenTK.Platform.MacOS class JoystickDetails { + public string Name; public JoystickState State; public JoystickCapabilities Capabilities; - public string Name; + readonly public Dictionary ElementUsageToButton = + new Dictionary(); } class MacJoystick : JoystickDevice @@ -387,6 +389,7 @@ namespace OpenTK.Platform.MacOS string name = CF.CFStringGetCString(name_ref); CF.CFRelease(name_ref); + List button_elements = new List(); CFArray element_array = new CFArray(element_array_ref); for (int i = 0; i < element_array.Count; i++) { @@ -429,7 +432,7 @@ namespace OpenTK.Platform.MacOS break; case HIDPage.Button: - buttons++; + button_elements.Add(usage); break; } } @@ -439,7 +442,11 @@ namespace OpenTK.Platform.MacOS joy.Details.State.SetIsConnected(true); joy.Details.Capabilities = new JoystickCapabilities(axes, buttons, true); - //NativeMethods.IOHIDDeviceGetProperty(device, nativem + // Map button elements to JoystickButtons + for (int button = 0; button < button_elements.Count; button++) + { + joy.Details.ElementUsageToButton.Add(button_elements[button], JoystickButton.Button0 + button); + } } CF.CFRelease(element_array_ref); @@ -509,7 +516,11 @@ namespace OpenTK.Platform.MacOS case HIDUsageGD.Dial: case HIDUsageGD.Wheel: short offset = GetJoystickAxis(val, elem); - joy.Details.State.SetAxis(TranslateJoystickAxis(usage), offset); + JoystickAxis axis = TranslateJoystickAxis(usage); + if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last) + { + joy.Details.State.SetAxis(axis, offset); + } break; case HIDUsageGD.Hatswitch: @@ -523,7 +534,11 @@ namespace OpenTK.Platform.MacOS case HIDUsageSim.Rudder: case HIDUsageSim.Throttle: short offset = GetJoystickAxis(val, elem); - joy.Details.State.SetAxis(TranslateJoystickAxis(usage), offset); + JoystickAxis axis = TranslateJoystickAxis(usage); + if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last) + { + joy.Details.State.SetAxis(axis, offset); + } break; } break; @@ -531,7 +546,11 @@ namespace OpenTK.Platform.MacOS case HIDPage.Button: { bool pressed = GetJoystickButton(val, elem); - joy.Details.State.SetButton(TranslateJoystickButton(usage), value); + JoystickButton button = TranslateJoystickButton(joy, usage); + if (button >= JoystickButton.Button0 && button <= JoystickButton.Last) + { + joy.Details.State.SetButton(button, pressed); + } } break; } @@ -598,9 +617,14 @@ namespace OpenTK.Platform.MacOS return value >= 1; } - static JoystickButton TranslateJoystickButton(int usage) + static JoystickButton TranslateJoystickButton(MacJoystick joy, int usage) { - + JoystickButton button; + if (joy.Details.ElementUsageToButton.TryGetValue(usage, out button)) + { + return button; + } + return JoystickButton.Last + 1; } #endregion From 892d129e54c2ac24e565d4788933d162be8cc3fb Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 04:15:30 +0100 Subject: [PATCH 197/245] [Mac] Fixed DllEntryPointNotFound crash on device unplugging --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index e90ecf58..68496067 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -210,7 +210,7 @@ namespace OpenTK.Platform.MacOS if (recognized) { NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); - NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode); + NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode); } } @@ -849,7 +849,7 @@ namespace OpenTK.Platform.MacOS CFString inCFRunLoopMode); [DllImport(hid)] - public static extern void IOHIDDeviceUnscheduleWithRunLoop( + public static extern void IOHIDDeviceUnscheduleFromRunLoop( IOHIDDeviceRef device, CFRunLoop inCFRunLoop, CFString inCFRunLoopMode); From 4c7f6a92a7d7b45eb041150715ccc7d5db4236c6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 04:20:51 +0100 Subject: [PATCH 198/245] [Mac] Don't let exceptions escape to unmanaged code --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 167 +++++++++++++---------- 1 file changed, 94 insertions(+), 73 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 68496067..85cab913 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -144,103 +144,124 @@ namespace OpenTK.Platform.MacOS void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) { - bool recognized = false; - - if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero) + try { - if (NativeMethods.IOHIDDeviceConformsTo(device, - HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse)) - { - AddMouse(sender, device); - recognized = true; - } + bool recognized = false; - if (NativeMethods.IOHIDDeviceConformsTo(device, - HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard)) + if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero) { - AddKeyboard(sender, device); - recognized = true; - } + if (NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse)) + { + AddMouse(sender, device); + recognized = true; + } - if (NativeMethods.IOHIDDeviceConformsTo(device, - HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick)) - { - AddJoystick(sender, device); - recognized = true; - } + if (NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard)) + { + AddKeyboard(sender, device); + recognized = true; + } - if (recognized) - { - // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include - // the device identifier as the context variable, so we can identify it and figure out the device later. - // Thanks to Jase: http://www.opentk.com/node/2800 - NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, - HandleDeviceValueReceived, device); + if (NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick)) + { + AddJoystick(sender, device); + recognized = true; + } - NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode); + if (recognized) + { + // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include + // the device identifier as the context variable, so we can identify it and figure out the device later. + // Thanks to Jase: http://www.opentk.com/node/2800 + NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, + HandleDeviceValueReceived, device); + + NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode); + } } } + catch (Exception e) + { + Debug.Print("[Mac] Exception in managed callback: {0}", e); + } } void DeviceRemoved(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device) { - bool recognized = false; - - if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && - MouseDevices.ContainsKey(device)) + try { - RemoveMouse(sender, device); - recognized = true; + bool recognized = false; + + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && + MouseDevices.ContainsKey(device)) + { + RemoveMouse(sender, device); + recognized = true; + } + + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && + KeyboardDevices.ContainsKey(device)) + { + RemoveKeyboard(sender, device); + recognized = true; + } + + if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick) && + JoystickDevices.ContainsKey(device)) + { + RemoveJoystick(sender, device); + recognized = true; + } + + if (recognized) + { + NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); + NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode); + } } - - if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && - KeyboardDevices.ContainsKey(device)) + catch (Exception e) { - RemoveKeyboard(sender, device); - recognized = true; - } - - if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick) && - JoystickDevices.ContainsKey(device)) - { - RemoveJoystick(sender, device); - recognized = true; - } - - if (recognized) - { - NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero); - NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode); + Debug.Print("[Mac] Exception in managed callback: {0}", e); } } void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val) { - if (disposed) + try { - Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}", - context, res, sender, val, GetType()); - return; - } + if (disposed) + { + Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}", + context, res, sender, val, GetType()); + return; + } - MouseState mouse; - KeyboardState keyboard; - MacJoystick joystick; - if (MouseDevices.TryGetValue(context, out mouse)) - { - MouseDevices[context] = UpdateMouse(mouse, val); + MouseState mouse; + KeyboardState keyboard; + MacJoystick joystick; + if (MouseDevices.TryGetValue(context, out mouse)) + { + MouseDevices[context] = UpdateMouse(mouse, val); + } + else if (KeyboardDevices.TryGetValue(context, out keyboard)) + { + KeyboardDevices[context] = UpdateKeyboard(keyboard, val); + } + else if (JoystickDevices.TryGetValue(context, out joystick)) + { + JoystickDevices[context] = UpdateJoystick(joystick, val); + } + else + { + //Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender); + } } - else if (KeyboardDevices.TryGetValue(context, out keyboard)) + catch (Exception e) { - KeyboardDevices[context] = UpdateKeyboard(keyboard, val); - } - else if (JoystickDevices.TryGetValue(context, out joystick)) - { - JoystickDevices[context] = UpdateJoystick(joystick, val); - } - else - { - //Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender); + Debug.Print("[Mac] Exception in managed callback: {0}", e); } } From 7388bd935d18412862263d86fdac82d86b0baa5d Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 20:17:03 +0100 Subject: [PATCH 199/245] [Mac] Correctly unschedule HIDManager from run loop --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 85cab913..7ff0e498 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -818,6 +818,12 @@ namespace OpenTK.Platform.MacOS CFRunLoop inCFRunLoop, CFString inCFRunLoopMode); + [DllImport(hid)] + public static void IOHIDManagerUnscheduleFromRunLoop( + IOHIDManagerRef inIOHIDManagerRef, + CFRunLoop inCFRunLoop, + CFString inCFRunLoopMode); + [DllImport(hid)] public static extern void IOHIDManagerSetDeviceMatching( IOHIDManagerRef manager, @@ -1468,8 +1474,8 @@ namespace OpenTK.Platform.MacOS hidmanager, IntPtr.Zero, IntPtr.Zero); NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback( hidmanager, IntPtr.Zero, IntPtr.Zero); - NativeMethods.IOHIDManagerScheduleWithRunLoop( - hidmanager, IntPtr.Zero, IntPtr.Zero); + NativeMethods.IOHIDManagerUnscheduleFromRunLoop( + hidmanager, RunLoop, InputLoopMode); foreach (var device in MouseDevices.Keys) { From 02c9f471eadb256a83d924d4795d6c9910b8e6f8 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 21:18:20 +0100 Subject: [PATCH 200/245] [Mac] DllImport must be marked as extern --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 7ff0e498..382e1f90 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -819,7 +819,7 @@ namespace OpenTK.Platform.MacOS CFString inCFRunLoopMode); [DllImport(hid)] - public static void IOHIDManagerUnscheduleFromRunLoop( + public static extern void IOHIDManagerUnscheduleFromRunLoop( IOHIDManagerRef inIOHIDManagerRef, CFRunLoop inCFRunLoop, CFString inCFRunLoopMode); From 05af21e61b215737f8d1a24abc898a91e476d06a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 21:27:34 +0100 Subject: [PATCH 201/245] [Mac] Do not call CFRelease on HID properties HID properties are callee-owned and should not be released by the caller. Fixes crash (memory corruption) on device hot plugging. --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 382e1f90..6deb0b93 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -408,7 +408,6 @@ namespace OpenTK.Platform.MacOS CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey); string name = CF.CFStringGetCString(name_ref); - CF.CFRelease(name_ref); List button_elements = new List(); CFArray element_array = new CFArray(element_array_ref); From fc81f302513c0638a2cb02ca83b40adc25e6b601 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 21:32:24 +0100 Subject: [PATCH 202/245] [Examples] Draw device index for Joysticks and GamePads --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index aa9d27d2..b5bf17f1 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -236,7 +236,7 @@ namespace Examples.Tests GamePadState state = GamePad.GetState(i); if (state.IsConnected) { - DrawString(gfx, caps.ToString(), line++); + DrawString(gfx, String.Format("{0}: {1}", i, caps), line++); DrawString(gfx, state.ToString(), line++); } } @@ -248,7 +248,7 @@ namespace Examples.Tests JoystickState state = Joystick.GetState(i); if (state.IsConnected) { - DrawString(gfx, caps.ToString(), line++); + DrawString(gfx, String.Format("{0}: {1}", i, caps), line++); DrawString(gfx, state.ToString(), line++); } } From fa386dc991facc0fc849f99f7cb5e6939ddf2abf Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 21:53:28 +0100 Subject: [PATCH 203/245] [Mac] Connect devices to first available index --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 51 ++++++++++++++++++------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 6deb0b93..6b09f8d9 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -489,21 +489,46 @@ namespace OpenTK.Platform.MacOS void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device) { - if (!JoystickDevices.ContainsKey(device)) + Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender); + MacJoystick joy = CreateJoystick(sender, device); + if (joy != null) { - Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender); - MacJoystick joy = CreateJoystick(sender, device); - if (joy != null) + // Add a device->joy lookup entry for this device. + if (!JoystickDevices.ContainsKey(device)) { - JoystickIndexToDevice.Add(JoystickDevices.Count, device); + // First time we've seen this device. JoystickDevices.Add(device, joy); } - } - else - { - Debug.Print("Joystick device {0:x} reconnected, sender is {1:x}", device, sender); - JoystickDevices[device].Details.State.SetIsConnected(true); - //JoystickDevices[device].Details.Capabilities.SetIsConnected(true); + else + { + // This is an old device that is replugged. + // This branch does not appear to be executed, ever. + JoystickDevices[device] = joy; + } + + // Add an index->device lookup entry for this device. + // Use the first free (i.e. disconnected) index. + // If all indices are connected, append a new one. + int i; + for (i = 0; i < JoystickIndexToDevice.Count; i++) + { + IntPtr candidate = JoystickIndexToDevice[i]; + if (!JoystickDevices[candidate].Details.State.IsConnected) + { + break; + } + } + + if (i == JoystickDevices.Count) + { + // All indices connected, append a new one. + JoystickIndexToDevice.Add(JoystickDevices.Count, device); + } + else + { + // Replace joystick at that index + JoystickIndexToDevice[i] = device; + } } } @@ -511,8 +536,8 @@ namespace OpenTK.Platform.MacOS { Debug.Print("Joystick device {0:x} disconnected, sender is {1:x}", device, sender); // Keep the device in case it comes back later on - JoystickDevices[device].Details.State.SetIsConnected(false); - //JoystickDevices[device].Details.Capabilities.SetIsConnected(false); + JoystickDevices[device].Details.State = new JoystickState(); + JoystickDevices[device].Details.Capabilities = new JoystickCapabilities(); } static MacJoystick UpdateJoystick(MacJoystick joy, IOHIDValueRef val) From b458b406834fe79ef9dc0ea14eb15d49509ae1af Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 22:05:54 +0100 Subject: [PATCH 204/245] [Mac] Use out/ref parameters instead of unsafe pointers in bindings --- .../MacOS/CarbonBindings/CoreFoundation.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs b/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs index a4d6527b..bec5c6f0 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs @@ -62,6 +62,7 @@ namespace OpenTK.Platform.MacOS.Carbon } } } + struct CFDictionary { public CFDictionary(IntPtr reference) @@ -79,18 +80,16 @@ namespace OpenTK.Platform.MacOS.Carbon return CF.CFDictionaryGetCount(dictionaryRef); } } + public double GetNumberValue(string key) { - unsafe - { - double retval; - IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef, - CF.CFSTR(key)); + double retval; + IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef, + CF.CFSTR(key)); - CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, &retval); + CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, out retval); - return retval; - } + return retval; } } class CF @@ -150,9 +149,11 @@ namespace OpenTK.Platform.MacOS.Carbon ); [DllImport(appServices)] - internal unsafe static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, int* valuePtr); + internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out int valuePtr); [DllImport(appServices)] - internal unsafe static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, double* valuePtr); + internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out long valuePtr); + [DllImport(appServices)] + internal static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, out double valuePtr); internal enum CFNumberType { From 5c5afb3ea32934caf7f2d462194f49ef4adfc23f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 22:06:19 +0100 Subject: [PATCH 205/245] [Mac] Calculate joystick Guid for USB devices --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 31 ++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 6b09f8d9..5aa9be92 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -58,6 +58,7 @@ namespace OpenTK.Platform.MacOS class JoystickDetails { public string Name; + public Guid Guid; public JoystickState State; public JoystickCapabilities Capabilities; readonly public Dictionary ElementUsageToButton = @@ -394,7 +395,30 @@ namespace OpenTK.Platform.MacOS #region Joystick - MacJoystick CreateJoystick(CFAllocatorRef sender, CFAllocatorRef device) + Guid CreateJoystickGuid(IntPtr device) + { + // Create a device guid from the product and vendor id keys + List guid_bytes = new List(); + long vendor_id = 0; + long product_id = 0; + + IntPtr vendor_id_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDVendorIDKey); + IntPtr product_id_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductIDKey); + if (vendor_id_ref != IntPtr.Zero) + { + CF.CFNumberGetValue(vendor_id_ref, CF.CFNumberType.kCFNumberLongType, out vendor_id); + } + if (product_id_ref != IntPtr.Zero) + { + CF.CFNumberGetValue(product_id_ref, CF.CFNumberType.kCFNumberLongType, out product_id); + } + + guid_bytes.AddRange(BitConverter.GetBytes(vendor_id)); + guid_bytes.AddRange(BitConverter.GetBytes(product_id)); + return new Guid(guid_bytes.ToArray()); + } + + MacJoystick CreateJoystick(IntPtr sender, IntPtr device) { MacJoystick joy = null; @@ -406,6 +430,8 @@ namespace OpenTK.Platform.MacOS int buttons = 0; int dpads = 0; + Guid guid = CreateJoystickGuid(device); + CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey); string name = CF.CFStringGetCString(name_ref); @@ -459,6 +485,7 @@ namespace OpenTK.Platform.MacOS joy = new MacJoystick(-1, axes, buttons); joy.Details.Name = name; + joy.Details.Guid = guid; joy.Details.State.SetIsConnected(true); joy.Details.Capabilities = new JoystickCapabilities(axes, buttons, true); @@ -783,7 +810,7 @@ namespace OpenTK.Platform.MacOS MacJoystick joystick = GetJoystick(index); if (joystick != null) { - //return joystick.Details.Capabilities; + return joystick.Details.Guid; } return new Guid(); } From 9e223486f6696228b42c1f0c662e25073dbbfca0 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 22:15:55 +0100 Subject: [PATCH 206/245] [Mac] Implement Joystick.GetGuid() --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 28 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 5aa9be92..82aa7879 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -395,7 +395,7 @@ namespace OpenTK.Platform.MacOS #region Joystick - Guid CreateJoystickGuid(IntPtr device) + Guid CreateJoystickGuid(IntPtr device, string name) { // Create a device guid from the product and vendor id keys List guid_bytes = new List(); @@ -413,8 +413,26 @@ namespace OpenTK.Platform.MacOS CF.CFNumberGetValue(product_id_ref, CF.CFNumberType.kCFNumberLongType, out product_id); } - guid_bytes.AddRange(BitConverter.GetBytes(vendor_id)); - guid_bytes.AddRange(BitConverter.GetBytes(product_id)); + if (vendor_id != 0 && product_id != 0) + { + guid_bytes.AddRange(BitConverter.GetBytes(vendor_id)); + guid_bytes.AddRange(BitConverter.GetBytes(product_id)); + } + else + { + // Bluetooth devices don't have USB vendor/product id keys. + // Match SDL2 algorithm for compatibility. + guid_bytes.Add(0x05); // BUS_BLUETOOTH + guid_bytes.Add(0x00); + guid_bytes.Add(0x00); + guid_bytes.Add(0x00); + + // Copy the first 12 bytes of the product name + byte[] name_bytes = new byte[12]; + Array.Copy(System.Text.Encoding.UTF8.GetBytes(name), name_bytes, name_bytes.Length); + guid_bytes.AddRange(name_bytes); + } + return new Guid(guid_bytes.ToArray()); } @@ -430,11 +448,11 @@ namespace OpenTK.Platform.MacOS int buttons = 0; int dpads = 0; - Guid guid = CreateJoystickGuid(device); - CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey); string name = CF.CFStringGetCString(name_ref); + Guid guid = CreateJoystickGuid(device, name); + List button_elements = new List(); CFArray element_array = new CFArray(element_array_ref); for (int i = 0; i < element_array.Count; i++) From ab85afd5ba58f51f4ee3901e26a4c3b9d36e9041 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 22:36:01 +0100 Subject: [PATCH 207/245] [Mac] Avoid IOHIDDeviceConformsTo call in DeviceRemoved handler DeviceAdded already checks that devices conform to the desired usage pages. Checking again in DeviceRemoved is unnecessary - if a device exists, then it has already passed muster. --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 82aa7879..7454ff7e 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -196,22 +196,19 @@ namespace OpenTK.Platform.MacOS { bool recognized = false; - if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) && - MouseDevices.ContainsKey(device)) + if (MouseDevices.ContainsKey(device)) { RemoveMouse(sender, device); recognized = true; } - if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) && - KeyboardDevices.ContainsKey(device)) + if (KeyboardDevices.ContainsKey(device)) { RemoveKeyboard(sender, device); recognized = true; } - if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick) && - JoystickDevices.ContainsKey(device)) + if (JoystickDevices.ContainsKey(device)) { RemoveJoystick(sender, device); recognized = true; From a1123834a02f713ea1da8b6e83bdb5ddb0b08fa7 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 22:38:26 +0100 Subject: [PATCH 208/245] [Mac] Call DeviceRemoved() in Dispose() event The necessary cleanup code already exists in DeviceRemoved(). No need to duplicate this in the Dispose() event. --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 7454ff7e..024d85cc 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -1545,14 +1545,17 @@ namespace OpenTK.Platform.MacOS foreach (var device in MouseDevices.Keys) { - NativeMethods.IOHIDDeviceRegisterInputValueCallback( - device, IntPtr.Zero, IntPtr.Zero); + DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device); } foreach (var device in KeyboardDevices.Keys) { - NativeMethods.IOHIDDeviceRegisterInputValueCallback( - device, IntPtr.Zero, IntPtr.Zero); + DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device); + } + + foreach (var device in JoystickDevices.Keys) + { + DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device); } HandleDeviceAdded = null; From fda2d32d2d8c8bb479ed471155b70f9a75bb874b Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 22:44:58 +0100 Subject: [PATCH 209/245] [Mac] Map GamePads, MultiAxisControllers and Wheels to joysticks --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 024d85cc..25cdd15e 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -165,8 +165,17 @@ namespace OpenTK.Platform.MacOS recognized = true; } - if (NativeMethods.IOHIDDeviceConformsTo(device, - HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick)) + bool is_joystick = false; + is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick); + is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.GamePad); + is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.MultiAxisController); + is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device, + HIDPage.GenericDesktop, (int)HIDUsageGD.Wheel); + // Todo: any other interesting devices under HIDPage.Simulation + HIDUsageSim? + if (is_joystick) { AddJoystick(sender, device); recognized = true; From 4d660fdeba512e60892932b1b3559372f319110d Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 23:50:17 +0100 Subject: [PATCH 210/245] [Input] Added Keyboard/MouseState.SetIsConnected to mirror JoystickState --- Source/OpenTK/Input/KeyboardState.cs | 5 +++++ Source/OpenTK/Input/MouseState.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Source/OpenTK/Input/KeyboardState.cs b/Source/OpenTK/Input/KeyboardState.cs index 49860eb0..2ba3c928 100644 --- a/Source/OpenTK/Input/KeyboardState.cs +++ b/Source/OpenTK/Input/KeyboardState.cs @@ -291,6 +291,11 @@ namespace OpenTK.Input IsConnected |= other.IsConnected; } + internal void SetIsConnected(bool value) + { + IsConnected = value; + } + #endregion #region Private Members diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs index eee3a0aa..7fa48a8f 100644 --- a/Source/OpenTK/Input/MouseState.cs +++ b/Source/OpenTK/Input/MouseState.cs @@ -329,6 +329,11 @@ namespace OpenTK.Input } } + internal void SetIsConnected(bool value) + { + IsConnected = value; + } + #endregion #region Private Members From a9ab3650da17d9e926c28daa38cc54af843ad922 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 5 Jan 2014 23:58:05 +0100 Subject: [PATCH 211/245] [Mac] Do not store *State structs directly Storing Mouse/KeyboardState structs directly makes updates more difficult than they should be. It is simpler to create simple classes instead. --- Source/OpenTK/Platform/MacOS/HIDInput.cs | 174 +++++++++++------------ 1 file changed, 80 insertions(+), 94 deletions(-) diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs index 25cdd15e..f54e97c4 100755 --- a/Source/OpenTK/Platform/MacOS/HIDInput.cs +++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs @@ -55,7 +55,17 @@ namespace OpenTK.Platform.MacOS { #region Fields - class JoystickDetails + class MouseData + { + public MouseState State; + } + + class KeyboardData + { + public KeyboardState State; + } + + class JoystickData { public string Name; public Guid Guid; @@ -65,27 +75,20 @@ namespace OpenTK.Platform.MacOS new Dictionary(); } - class MacJoystick : JoystickDevice - { - internal MacJoystick(int id, int axes, int buttons) - : base(id, axes, buttons) - { } - } - readonly IOHIDManagerRef hidmanager; - readonly Dictionary MouseDevices = - new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary MouseDevices = + new Dictionary(new IntPtrEqualityComparer()); readonly Dictionary MouseIndexToDevice = new Dictionary(); - readonly Dictionary KeyboardDevices = - new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary KeyboardDevices = + new Dictionary(new IntPtrEqualityComparer()); readonly Dictionary KeyboardIndexToDevice = new Dictionary(); - readonly Dictionary JoystickDevices = - new Dictionary(new IntPtrEqualityComparer()); + readonly Dictionary JoystickDevices = + new Dictionary(new IntPtrEqualityComparer()); readonly Dictionary JoystickIndexToDevice = new Dictionary(); @@ -246,20 +249,20 @@ namespace OpenTK.Platform.MacOS return; } - MouseState mouse; - KeyboardState keyboard; - MacJoystick joystick; + MouseData mouse; + KeyboardData keyboard; + JoystickData joystick; if (MouseDevices.TryGetValue(context, out mouse)) { - MouseDevices[context] = UpdateMouse(mouse, val); + UpdateMouse(mouse, val); } else if (KeyboardDevices.TryGetValue(context, out keyboard)) { - KeyboardDevices[context] = UpdateKeyboard(keyboard, val); + UpdateKeyboard(keyboard, val); } else if (JoystickDevices.TryGetValue(context, out joystick)) { - JoystickDevices[context] = UpdateJoystick(joystick, val); + UpdateJoystick(joystick, val); } else { @@ -279,30 +282,24 @@ namespace OpenTK.Platform.MacOS if (!MouseDevices.ContainsKey(device)) { Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender); - MouseState state = new MouseState(); - state.IsConnected = true; MouseIndexToDevice.Add(MouseDevices.Count, device); - MouseDevices.Add(device, state); + MouseDevices.Add(device, new MouseData()); } else { Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender); - MouseState state = MouseDevices[device]; - state.IsConnected = true; - MouseDevices[device] = state; } + MouseDevices[device].State.SetIsConnected(true); } void RemoveMouse(CFAllocatorRef sender, CFAllocatorRef device) { Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender); // Keep the device in case it comes back later on - MouseState state = MouseDevices[device]; - state.IsConnected = false; - MouseDevices[device] = state; + MouseDevices[device].State.SetIsConnected(false); } - static MouseState UpdateMouse(MouseState state, IOHIDValueRef val) + static void UpdateMouse(MouseData mouse, IOHIDValueRef val) { IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); @@ -317,25 +314,23 @@ namespace OpenTK.Platform.MacOS switch ((HIDUsageGD)usage) { case HIDUsageGD.X: - state.X += v_int; + mouse.State.X += v_int; break; case HIDUsageGD.Y: - state.Y += v_int; + mouse.State.Y += v_int; break; case HIDUsageGD.Wheel: - state.WheelPrecise += v_int; + mouse.State.WheelPrecise += v_int; break; } break; case HIDPage.Button: - state[OpenTK.Input.MouseButton.Left + usage - 1] = v_int == 1; + mouse.State[OpenTK.Input.MouseButton.Left + usage - 1] = v_int == 1; break; } - - return state; } #endregion @@ -347,30 +342,24 @@ namespace OpenTK.Platform.MacOS if (!KeyboardDevices.ContainsKey(device)) { Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender); - KeyboardState state = new KeyboardState(); - state.IsConnected = true; KeyboardIndexToDevice.Add(KeyboardDevices.Count, device); - KeyboardDevices.Add(device, state); + KeyboardDevices.Add(device, new KeyboardData()); } else { Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender); - KeyboardState state = KeyboardDevices[device]; - state.IsConnected = true; - KeyboardDevices[device] = state; } + KeyboardDevices[device].State.SetIsConnected(true); } void RemoveKeyboard(CFAllocatorRef sender, CFAllocatorRef device) { Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender); // Keep the device in case it comes back later on - KeyboardState state = KeyboardDevices[device]; - state.IsConnected = false; - KeyboardDevices[device] = state; + KeyboardDevices[device].State.SetIsConnected(false); } - static KeyboardState UpdateKeyboard(KeyboardState state, IOHIDValueRef val) + static void UpdateKeyboard(KeyboardData keyboard, IOHIDValueRef val) { IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); int v_int = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32(); @@ -379,22 +368,21 @@ namespace OpenTK.Platform.MacOS // This will supress the debug printing below. Seems like it generates a lot of -1s. // Couldn't find any details in USB spec or Apple docs for this behavior. - if(usage < 0 ) return state; - - switch (page) + if (usage >= 0) { - case HIDPage.GenericDesktop: - case HIDPage.KeyboardOrKeypad: - if (usage >= RawKeyMap.Length) - { - Debug.Print("[Warning] Key {0} not mapped.", usage); - return state; - } - state.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0); - break; - } - return state; + switch (page) + { + case HIDPage.GenericDesktop: + case HIDPage.KeyboardOrKeypad: + if (usage >= RawKeyMap.Length) + { + Debug.Print("[Warning] Key {0} not mapped.", usage); + } + keyboard.State.SetKeyState(RawKeyMap[usage], (byte)usage, v_int != 0); + break; + } + } } #endregion @@ -442,14 +430,15 @@ namespace OpenTK.Platform.MacOS return new Guid(guid_bytes.ToArray()); } - MacJoystick CreateJoystick(IntPtr sender, IntPtr device) + JoystickData CreateJoystick(IntPtr sender, IntPtr device) { - MacJoystick joy = null; + JoystickData joy = null; // Retrieve all elements of this device CFArrayRef element_array_ref = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero, IntPtr.Zero); if (element_array_ref != IntPtr.Zero) { + joy = new JoystickData(); int axes = 0; int buttons = 0; int dpads = 0; @@ -507,16 +496,15 @@ namespace OpenTK.Platform.MacOS } } - joy = new MacJoystick(-1, axes, buttons); - joy.Details.Name = name; - joy.Details.Guid = guid; - joy.Details.State.SetIsConnected(true); - joy.Details.Capabilities = new JoystickCapabilities(axes, buttons, true); + joy.Name = name; + joy.Guid = guid; + joy.State.SetIsConnected(true); + joy.Capabilities = new JoystickCapabilities(axes, buttons, true); // Map button elements to JoystickButtons for (int button = 0; button < button_elements.Count; button++) { - joy.Details.ElementUsageToButton.Add(button_elements[button], JoystickButton.Button0 + button); + joy.ElementUsageToButton.Add(button_elements[button], JoystickButton.Button0 + button); } } CF.CFRelease(element_array_ref); @@ -524,12 +512,12 @@ namespace OpenTK.Platform.MacOS return joy; } - MacJoystick GetJoystick(int index) + JoystickData GetJoystick(int index) { IntPtr device; if (JoystickIndexToDevice.TryGetValue(index, out device)) { - MacJoystick joystick; + JoystickData joystick; if (JoystickDevices.TryGetValue(device, out joystick)) { return joystick; @@ -541,7 +529,7 @@ namespace OpenTK.Platform.MacOS void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device) { Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender); - MacJoystick joy = CreateJoystick(sender, device); + JoystickData joy = CreateJoystick(sender, device); if (joy != null) { // Add a device->joy lookup entry for this device. @@ -564,7 +552,7 @@ namespace OpenTK.Platform.MacOS for (i = 0; i < JoystickIndexToDevice.Count; i++) { IntPtr candidate = JoystickIndexToDevice[i]; - if (!JoystickDevices[candidate].Details.State.IsConnected) + if (!JoystickDevices[candidate].State.IsConnected) { break; } @@ -587,11 +575,11 @@ namespace OpenTK.Platform.MacOS { Debug.Print("Joystick device {0:x} disconnected, sender is {1:x}", device, sender); // Keep the device in case it comes back later on - JoystickDevices[device].Details.State = new JoystickState(); - JoystickDevices[device].Details.Capabilities = new JoystickCapabilities(); + JoystickDevices[device].State = new JoystickState(); + JoystickDevices[device].Capabilities = new JoystickCapabilities(); } - static MacJoystick UpdateJoystick(MacJoystick joy, IOHIDValueRef val) + static void UpdateJoystick(JoystickData joy, IOHIDValueRef val) { IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val); HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem); @@ -615,7 +603,7 @@ namespace OpenTK.Platform.MacOS JoystickAxis axis = TranslateJoystickAxis(usage); if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last) { - joy.Details.State.SetAxis(axis, offset); + joy.State.SetAxis(axis, offset); } break; @@ -633,7 +621,7 @@ namespace OpenTK.Platform.MacOS JoystickAxis axis = TranslateJoystickAxis(usage); if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last) { - joy.Details.State.SetAxis(axis, offset); + joy.State.SetAxis(axis, offset); } break; } @@ -645,13 +633,11 @@ namespace OpenTK.Platform.MacOS JoystickButton button = TranslateJoystickButton(joy, usage); if (button >= JoystickButton.Button0 && button <= JoystickButton.Last) { - joy.Details.State.SetButton(button, pressed); + joy.State.SetButton(button, pressed); } } break; } - - return joy; } static short GetJoystickAxis(IOHIDValueRef val, IOHIDElementRef element) @@ -713,10 +699,10 @@ namespace OpenTK.Platform.MacOS return value >= 1; } - static JoystickButton TranslateJoystickButton(MacJoystick joy, int usage) + static JoystickButton TranslateJoystickButton(JoystickData joy, int usage) { JoystickButton button; - if (joy.Details.ElementUsageToButton.TryGetValue(usage, out button)) + if (joy.ElementUsageToButton.TryGetValue(usage, out button)) { return button; } @@ -741,9 +727,9 @@ namespace OpenTK.Platform.MacOS MouseState IMouseDriver2.GetState() { MouseState master = new MouseState(); - foreach (KeyValuePair item in MouseDevices) + foreach (KeyValuePair item in MouseDevices) { - master.MergeBits(item.Value); + master.MergeBits(item.Value.State); } return master; @@ -754,7 +740,7 @@ namespace OpenTK.Platform.MacOS IntPtr device; if (MouseIndexToDevice.TryGetValue(index, out device)) { - return MouseDevices[device]; + return MouseDevices[device].State; } return new MouseState(); @@ -773,9 +759,9 @@ namespace OpenTK.Platform.MacOS KeyboardState IKeyboardDriver2.GetState() { KeyboardState master = new KeyboardState(); - foreach (KeyValuePair item in KeyboardDevices) + foreach (KeyValuePair item in KeyboardDevices) { - master.MergeBits(item.Value); + master.MergeBits(item.Value.State); } return master; @@ -786,7 +772,7 @@ namespace OpenTK.Platform.MacOS IntPtr device; if (KeyboardIndexToDevice.TryGetValue(index, out device)) { - return KeyboardDevices[device]; + return KeyboardDevices[device].State; } return new KeyboardState(); @@ -811,30 +797,30 @@ namespace OpenTK.Platform.MacOS JoystickState IJoystickDriver2.GetState(int index) { - MacJoystick joystick = GetJoystick(index); + JoystickData joystick = GetJoystick(index); if (joystick != null) { - return joystick.Details.State; + return joystick.State; } return new JoystickState(); } JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) { - MacJoystick joystick = GetJoystick(index); + JoystickData joystick = GetJoystick(index); if (joystick != null) { - return joystick.Details.Capabilities; + return joystick.Capabilities; } return new JoystickCapabilities(); } Guid IJoystickDriver2.GetGuid(int index) { - MacJoystick joystick = GetJoystick(index); + JoystickData joystick = GetJoystick(index); if (joystick != null) { - return joystick.Details.Guid; + return joystick.Guid; } return new Guid(); } From 88c57db5b682835f16c4c5adc1178c72d67e6bb5 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 6 Jan 2014 01:52:08 +0100 Subject: [PATCH 212/245] [Math] Added MathHelper.Clamp --- Source/OpenTK/Math/MathHelper.cs | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Source/OpenTK/Math/MathHelper.cs b/Source/OpenTK/Math/MathHelper.cs index 215f540f..87fad768 100644 --- a/Source/OpenTK/Math/MathHelper.cs +++ b/Source/OpenTK/Math/MathHelper.cs @@ -288,6 +288,46 @@ namespace OpenTK #endregion + #region Clamp + + /// + /// Clamps a number between a minimum and a maximum. + /// + /// The number to clamp. + /// The minimum allowed value. + /// The maximum allowed value. + /// min, if n is lower than min; max, if n is higher than max; n otherwise. + public static int Clamp(int n, int min, int max) + { + return Math.Max(Math.Min(n, max), min); + } + + /// + /// Clamps a number between a minimum and a maximum. + /// + /// The number to clamp. + /// The minimum allowed value. + /// The maximum allowed value. + /// min, if n is lower than min; max, if n is higher than max; n otherwise. + public static float Clamp(float n, float min, float max) + { + return Math.Max(Math.Min(n, max), min); + } + + /// + /// Clamps a number between a minimum and a maximum. + /// + /// The number to clamp. + /// The minimum allowed value. + /// The maximum allowed value. + /// min, if n is lower than min; max, if n is higher than max; n otherwise. + public static double Clamp(double n, double min, double max) + { + return Math.Max(Math.Min(n, max), min); + } + + #endregion + #endregion } } From 83f54f70aac177ca2724319bd291f8f1e5316311 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 6 Jan 2014 01:57:54 +0100 Subject: [PATCH 213/245] [OpenTK] Simplify and improve timing calculations This patch modifies GameWindow.Run() to use a single stopwatch instead of two separate stopwatches for timing UpdateFrame and RenderFrame events. It improves timing accuracy for issue #20 (FrameEventArgs.Time Inconsistencies) --- Source/OpenTK/GameWindow.cs | 132 +++++++++++++++++------------------- 1 file changed, 64 insertions(+), 68 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 792869f8..0f41a3f8 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -87,7 +87,6 @@ namespace OpenTK double update_time, render_time; VSyncMode vsync; - Stopwatch update_watch = new Stopwatch(), render_watch = new Stopwatch(); double next_render = 0.0, next_update = 0.0; FrameEventArgs update_args = new FrameEventArgs(); FrameEventArgs render_args = new FrameEventArgs(); @@ -403,9 +402,12 @@ namespace OpenTK //Move += DispatchUpdateAndRenderFrame; //Resize += DispatchUpdateAndRenderFrame; + Debug.Print("Calibrating Stopwatch to account for drift"); + CalibrateStopwatch(); + Debug.Print("Stopwatch overhead: {0}", stopwatch_overhead); + Debug.Print("Entering main loop."); - update_watch.Start(); - render_watch.Start(); + watch.Start(); while (true) { ProcessEvents(); @@ -429,100 +431,94 @@ namespace OpenTK } } - void DispatchUpdateAndRenderFrame(object sender, EventArgs e) + double stopwatch_overhead = 0; + void CalibrateStopwatch() { - RaiseUpdateFrame(update_watch, ref next_update, update_args); - RaiseRenderFrame(render_watch, ref next_render, render_args); + // Make sure everything is JITted + watch.Start(); + stopwatch_overhead = watch.Elapsed.TotalSeconds; + watch.Stop(); + watch.Reset(); + // Measure stopwatch overhead + const int count = 10; + for (int i = 0; i < count; i++) + { + watch.Start(); + double sample = watch.Elapsed.TotalSeconds; + if (sample < 0 || sample > 0.1) + { + // calculation failed, repeat + i--; + continue; + } + stopwatch_overhead += sample; + watch.Stop(); + watch.Reset(); + } + stopwatch_overhead /= 10; } - void RaiseUpdateFrame(Stopwatch update_watch, ref double next_update, FrameEventArgs update_args) + Stopwatch watch = new Stopwatch(); + double update_timestamp = 0; + double render_timestamp = 0; + double update_elapsed = 0; + double render_elapsed = 0; + void DispatchUpdateAndRenderFrame(object sender, EventArgs e) { - int num_updates = 0; - double total_update_time = 0; + const int max_frameskip = 10; + int frameskip = 0; + double timestamp = 0; - // Cap the maximum time drift to 1 second (e.g. when the process is suspended). - double time = update_watch.Elapsed.TotalSeconds; - if (time <= 0) + do { - // Protect against negative Stopwatch.Elapsed values. - // See http://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time - update_watch.Reset(); - update_watch.Start(); - return; - } - if (time > 1.0) - time = 1.0; + // Raise UpdateFrame events until we catch up with our target update rate. + timestamp = watch.Elapsed.TotalSeconds; + update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); + update_timestamp = timestamp; + RaiseUpdateFrame(update_elapsed, ref next_update); + } while (next_update > 0 && ++frameskip < max_frameskip); + // Calculate statistics + //update_period = total_update_time / (double)num_updates; - // Raise UpdateFrame events until we catch up with our target update rate. - while (next_update - time <= 0 && time > 0) + timestamp = watch.Elapsed.TotalSeconds; + render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); + render_timestamp = timestamp; + RaiseRenderFrame(render_elapsed, ref next_render); + } + + void RaiseUpdateFrame(double time, ref double next_update) + { + double time_left = next_render - time; + if (time_left <= 0.0 && time > 0) { - next_update -= time; update_args.Time = time; OnUpdateFrameInternal(update_args); - time = update_time = Math.Max(update_watch.Elapsed.TotalSeconds, 0) - time; - // Stopwatches are not accurate over long time periods. - // We accumulate the total elapsed time into the time variable - // while reseting the Stopwatch frequently. - update_watch.Reset(); - update_watch.Start(); // Don't schedule a new update more than 1 second in the future. // Sometimes the hardware cannot keep up with updates // (e.g. when the update rate is too high, or the UpdateFrame processing // is too costly). This cap ensures we can catch up in a reasonable time // once the load becomes lighter. - next_update += TargetUpdatePeriod; + next_update = time_left + TargetUpdatePeriod; next_update = Math.Max(next_update, -1.0); - - total_update_time += update_time; - - // Allow up to 10 consecutive UpdateFrame events to prevent the - // application from "hanging" when the hardware cannot keep up - // with the requested update rate. - if (++num_updates >= 10 || TargetUpdateFrequency == 0.0) - break; - } - - // Calculate statistics - if (num_updates > 0) - { - update_period = total_update_time / (double)num_updates; } } - void RaiseRenderFrame(Stopwatch render_watch, ref double next_render, FrameEventArgs render_args) + void RaiseRenderFrame(double time, ref double next_render) { - // Cap the maximum time drift to 1 second (e.g. when the process is suspended). - double time = render_watch.Elapsed.TotalSeconds; - if (time <= 0) - { - // Protect against negative Stopwatch.Elapsed values. - // See http://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time - render_watch.Reset(); - render_watch.Start(); - return; - } - if (time > 1.0) - time = 1.0; double time_left = next_render - time; - if (time_left <= 0.0 && time > 0) { - // Schedule next render event. The 1 second cap ensures - // the process does not appear to hang. - next_render = time_left + TargetRenderPeriod; - if (next_render < -1.0) - next_render = -1.0; - - render_watch.Reset(); - render_watch.Start(); - if (time > 0) { render_period = render_args.Time = time; OnRenderFrameInternal(render_args); - render_time = render_watch.Elapsed.TotalSeconds; } + + // Schedule next render event. The 1 second cap ensures + // the process does not appear to hang. + next_render = time_left + TargetRenderPeriod; + next_update = Math.Max(next_update, -1.0); } } From d49dacb5b3b850ff59c8f634dae9db588264f6b6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 7 Jan 2014 00:48:09 +0100 Subject: [PATCH 214/245] [Examples] Cleaned up input device printing in GameWindowStates --- .../Examples/OpenTK/Test/GameWindowStates.cs | 179 ++++++++++-------- 1 file changed, 98 insertions(+), 81 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index b5bf17f1..4fdbee4c 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -25,8 +25,6 @@ namespace Examples.Tests int texture; bool mouse_in_window = false; bool viewport_changed = true; - MouseState mouse; - KeyboardState keyboard; public GameWindowStates() : base(800, 600, GraphicsMode.Default) @@ -102,80 +100,103 @@ namespace Examples.Tests return val > max ? max : val < min ? min : val; } - static void DrawString(Graphics gfx, string str, int line) + static float DrawString(Graphics gfx, string str, int line) { - gfx.DrawString(str, TextFont, Brushes.White, new PointF(0, line * TextFont.Height)); + return DrawString(gfx, str, line, 0); } - static void DrawString(Graphics gfx, string str, int line, float offset) + static float DrawString(Graphics gfx, string str, int line, float offset) { gfx.DrawString(str, TextFont, Brushes.White, new PointF(offset, line * TextFont.Height)); + return offset + gfx.MeasureString(str, TextFont).Width; } - static void DrawKeyboard(Graphics gfx, KeyboardState keyboard, int line) + static int DrawKeyboards(Graphics gfx, int line) { - const string str = "Keys pressed:"; - float space = gfx.MeasureString(" ", TextFont).Width; - float offset = gfx.MeasureString(str, TextFont).Width + space; - DrawString(gfx, str, line); - for (int i = 0; i < (int)Key.LastKey; i++) + line++; + DrawString(gfx, "Keyboard:", line++); + for (int i = 0; i < 4; i++) { - Key k = (Key)i; - if (keyboard[k]) + var state = OpenTK.Input.Keyboard.GetState(i); + if (state.IsConnected) { - string key = k.ToString(); - DrawString(gfx, key, line, offset); - offset += gfx.MeasureString(key, TextFont).Width + space; + StringBuilder sb = new StringBuilder(); + sb.Append(i); + sb.Append(": "); + for (int key_index = 0; key_index < (int)Key.LastKey; key_index++) + { + Key k = (Key)key_index; + if (state[k]) + { + sb.Append(k); + sb.Append(" "); + } + } + DrawString(gfx, sb.ToString(), line++); } } + return line; } - static void DrawMouse(Graphics gfx, MouseState mouse, int line) + static int DrawMice(Graphics gfx, int line) { - const string str = "Buttons pressed:"; - float space = gfx.MeasureString(" ", TextFont).Width; - float offset = gfx.MeasureString(str, TextFont).Width + space; - DrawString(gfx, str, line); - for (int i = 0; i < (int)MouseButton.LastButton; i++) + line++; + DrawString(gfx, "Mouse:", line++); + for (int i = 0; i < 4; i++) { - MouseButton b = (MouseButton)i; - if (mouse[b]) + var state = OpenTK.Input.Mouse.GetState(i); + if (state.IsConnected) { - string button = b.ToString(); - DrawString(gfx, button, line, offset); - offset += gfx.MeasureString(button, TextFont).Width + space; + StringBuilder sb = new StringBuilder(); + Vector3 pos = new Vector3(state.X, state.Y, state.WheelPrecise); + sb.Append(i); + sb.Append(": "); + sb.Append(pos); + for (int button_index = 0; button_index < (int)MouseButton.LastButton; button_index++) + { + MouseButton b = (MouseButton)button_index; + if (state[b]) + { + sb.Append(b); + sb.Append(" "); + } + } + DrawString(gfx, sb.ToString(), line++); } } + return line; } - static int DrawJoysticks(Graphics gfx, IList joysticks, int line) + static int DrawLegacyJoysticks(Graphics gfx, IList joysticks, int line) { - float space = gfx.MeasureString(" ", TextFont).Width; + line++; + DrawString(gfx, "Legacy Joystick:", line++); + int joy_index = -1; foreach (var joy in joysticks) { - string str = String.Format("Joystick '{0}': ", joy.Description); - DrawString(gfx, str, line); - - float offset = 0; - line++; - for (int i = 0; i < joy.Axis.Count; i++) + joy_index++; + if (!String.IsNullOrEmpty(joy.Description)) { - string axis = joy.Axis[i].ToString(); - DrawString(gfx, axis, line, offset); - offset += gfx.MeasureString(axis, TextFont).Width + space; - } + StringBuilder sb = new StringBuilder(); + sb.Append(joy_index); + sb.Append(": '"); + sb.Append(joy.Description); + sb.Append("' "); - offset = 0; - line++; - for (int i = 0; i < joy.Button.Count; i++) - { - string button = joy.Button[i].ToString(); - DrawString(gfx, button, line, offset); - offset += gfx.MeasureString(button, TextFont).Width + space; - } + for (int i = 0; i < joy.Axis.Count; i++) + { + sb.Append(joy.Axis[i]); + sb.Append(" "); + } - line++; + for (int i = 0; i < joy.Button.Count; i++) + { + sb.Append(joy.Button[i]); + sb.Append(" "); + } + DrawString(gfx, sb.ToString(), line++); + } } return line; @@ -183,11 +204,6 @@ namespace Examples.Tests protected override void OnUpdateFrame(FrameEventArgs e) { - InputDriver.Poll(); - - mouse = OpenTK.Input.Mouse.GetState(); - keyboard = OpenTK.Input.Keyboard.GetState(); - using (Graphics gfx = Graphics.FromImage(TextBitmap)) { int line = 0; @@ -195,41 +211,34 @@ 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, GL.GetString(StringName.Version), line++); DrawString(gfx, Context.GraphicsMode.ToString(), line++); + line++; + DrawString(gfx, "GameWindow:", 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, String.Format("Mouse {0} and {1}. {2}.", + mouse_in_window ? "inside" : "outside", + CursorVisible ? "visible" : "hidden", + Focused ? "Focused" : "Not focused"), line++); + DrawString(gfx, String.Format("Mouse (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), line++); + DrawString(gfx, String.Format("Bounds: {0}", Bounds), line++); + DrawString(gfx, String.Format("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( - new System.Drawing.Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height), - System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, TextBitmap.Width, TextBitmap.Height, PixelFormat.Bgra, - PixelType.UnsignedByte, data.Scan0); - TextBitmap.UnlockBits(data); + line = DrawKeyboards(gfx, line); + line = DrawMice(gfx, line); + line = DrawJoysticks(gfx, line); + line = DrawLegacyJoysticks(gfx, Joysticks, line); + } } - int DrawGamePads(Graphics gfx, int line) + int DrawJoysticks(Graphics gfx, int line) { line++; - DrawString(gfx, "GamePads:", line++); + DrawString(gfx, "GamePad:", line++); for (int i = 0; i < 4; i++) { GamePadCapabilities caps = GamePad.GetCapabilities(i); @@ -240,8 +249,9 @@ namespace Examples.Tests DrawString(gfx, state.ToString(), line++); } } + line++; - DrawString(gfx, "Joysticks:", line++); + DrawString(gfx, "Joystick:", line++); for (int i = 0; i < 4; i++) { JoystickCapabilities caps = Joystick.GetCapabilities(i); @@ -283,13 +293,20 @@ namespace Examples.Tests protected override void OnRenderFrame(FrameEventArgs e) { base.OnRenderFrame(e); - + + System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits( + new System.Drawing.Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height), + System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, TextBitmap.Width, TextBitmap.Height, PixelFormat.Bgra, + PixelType.UnsignedByte, data.Scan0); + TextBitmap.UnlockBits(data); + if (viewport_changed) { viewport_changed = false; GL.Viewport(0, 0, Width, Height); - + Matrix4 ortho_projection = Matrix4.CreateOrthographicOffCenter(0, Width, Height, 0, -1, 1); GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref ortho_projection); @@ -297,7 +314,7 @@ namespace Examples.Tests GL.Clear(ClearBufferMask.ColorBufferBit); - GL.Begin(BeginMode.Quads); + GL.Begin(PrimitiveType.Quads); GL.TexCoord2(0, 0); GL.Vertex2(0, 0); GL.TexCoord2(1, 0); GL.Vertex2(TextBitmap.Width, 0); From c5dcc8a93b64419208a0a824b06f151266e3124f Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 7 Jan 2014 01:01:00 +0100 Subject: [PATCH 215/245] [Examples] Calculate timing information in GameWindowStates --- .../Examples/OpenTK/Test/GameWindowStates.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 4fdbee4c..d2207219 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -25,6 +25,8 @@ namespace Examples.Tests int texture; bool mouse_in_window = false; bool viewport_changed = true; + Stopwatch watch = new Stopwatch(); + double update_time, render_time; public GameWindowStates() : base(800, 600, GraphicsMode.Default) @@ -204,6 +206,9 @@ namespace Examples.Tests protected override void OnUpdateFrame(FrameEventArgs e) { + double clock_time = watch.Elapsed.TotalSeconds; + update_time += e.Time; + using (Graphics gfx = Graphics.FromImage(TextBitmap)) { int line = 0; @@ -226,7 +231,14 @@ namespace Examples.Tests DrawString(gfx, String.Format("Mouse (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), line++); DrawString(gfx, String.Format("Bounds: {0}", Bounds), line++); DrawString(gfx, String.Format("ClientRectangle: {0}", ClientRectangle), line++); - DrawString(gfx, TypedText.ToString(), line++); + DrawString(gfx, String.Format("Vsync: {0}", VSync), line++); + DrawString(gfx, String.Format("Frequency: Update ({0:f1}/{1:f1}); Render ({2:f1}/{3:f1})", + UpdateFrequency, TargetUpdateFrequency, RenderFrequency, TargetRenderFrequency), line++); + DrawString(gfx, String.Format("Period: Update ({0:f1}/{1:f1}); Render ({2:f1}/{3:f1})", + UpdatePeriod, TargetUpdatePeriod, RenderPeriod, TargetRenderPeriod), line++); + DrawString(gfx, String.Format("Time drift: Clock {0:f4}; Update {1:f4}; Render {2:f4}", + clock_time, clock_time - update_time, clock_time - render_time), line++); + DrawString(gfx, String.Format("Text: {0}", TypedText.ToString()), line++); line = DrawKeyboards(gfx, line); line = DrawMice(gfx, line); @@ -268,8 +280,8 @@ namespace Examples.Tests protected override void OnLoad(EventArgs e) { - base.OnLoad(e); - + watch.Start(); + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.Texture2D); @@ -292,7 +304,7 @@ namespace Examples.Tests protected override void OnRenderFrame(FrameEventArgs e) { - base.OnRenderFrame(e); + render_time += e.Time; System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits( new System.Drawing.Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height), From 3856fcd48e679e6122d675b1cf1f9f19def63d31 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 7 Jan 2014 08:52:02 +0100 Subject: [PATCH 216/245] [OpenTK] More robust timing for UpdateFrame and RenderFrame FrameEventArgs.Time should no longer drift from clock time measured outside GameWindow. --- Source/OpenTK/GameWindow.cs | 145 +++++++++++++++--------------------- 1 file changed, 62 insertions(+), 83 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 0f41a3f8..048d5f64 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -1,4 +1,4 @@ -#region License +#region License // // The Open Toolkit Library License // @@ -75,7 +75,7 @@ namespace OpenTK { #region --- Fields --- - object exit_lock = new object(); + readonly Stopwatch watch = new Stopwatch(); IGraphicsContext glContext; @@ -83,6 +83,10 @@ namespace OpenTK double update_period, render_period; double target_update_period, target_render_period; + + double update_timestamp; // timestamp of last UpdateFrame event + double render_timestamp; // timestamp of last RenderFrame event + // TODO: Implement these: double update_time, render_time; VSyncMode vsync; @@ -402,10 +406,6 @@ namespace OpenTK //Move += DispatchUpdateAndRenderFrame; //Resize += DispatchUpdateAndRenderFrame; - Debug.Print("Calibrating Stopwatch to account for drift"); - CalibrateStopwatch(); - Debug.Print("Stopwatch overhead: {0}", stopwatch_overhead); - Debug.Print("Entering main loop."); watch.Start(); while (true) @@ -431,97 +431,76 @@ namespace OpenTK } } - double stopwatch_overhead = 0; - void CalibrateStopwatch() - { - // Make sure everything is JITted - watch.Start(); - stopwatch_overhead = watch.Elapsed.TotalSeconds; - watch.Stop(); - watch.Reset(); - // Measure stopwatch overhead - const int count = 10; - for (int i = 0; i < count; i++) - { - watch.Start(); - double sample = watch.Elapsed.TotalSeconds; - if (sample < 0 || sample > 0.1) - { - // calculation failed, repeat - i--; - continue; - } - stopwatch_overhead += sample; - watch.Stop(); - watch.Reset(); - } - stopwatch_overhead /= 10; - } - - Stopwatch watch = new Stopwatch(); - double update_timestamp = 0; - double render_timestamp = 0; - double update_elapsed = 0; - double render_elapsed = 0; void DispatchUpdateAndRenderFrame(object sender, EventArgs e) { const int max_frameskip = 10; int frameskip = 0; - double timestamp = 0; + double timestamp = watch.Elapsed.TotalSeconds; do { // Raise UpdateFrame events until we catch up with our target update rate. - timestamp = watch.Elapsed.TotalSeconds; - update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); - update_timestamp = timestamp; - RaiseUpdateFrame(update_elapsed, ref next_update); - } while (next_update > 0 && ++frameskip < max_frameskip); - // Calculate statistics - //update_period = total_update_time / (double)num_updates; - - timestamp = watch.Elapsed.TotalSeconds; - render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); - render_timestamp = timestamp; - RaiseRenderFrame(render_elapsed, ref next_render); - } - - void RaiseUpdateFrame(double time, ref double next_update) - { - double time_left = next_render - time; - if (time_left <= 0.0 && time > 0) - { - update_args.Time = time; - OnUpdateFrameInternal(update_args); - - // Don't schedule a new update more than 1 second in the future. - // Sometimes the hardware cannot keep up with updates - // (e.g. when the update rate is too high, or the UpdateFrame processing - // is too costly). This cap ensures we can catch up in a reasonable time - // once the load becomes lighter. - next_update = time_left + TargetUpdatePeriod; - next_update = Math.Max(next_update, -1.0); - } - } - - void RaiseRenderFrame(double time, ref double next_render) - { - double time_left = next_render - time; - if (time_left <= 0.0 && time > 0) - { - if (time > 0) + double update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); + if (RaiseUpdateFrame(update_elapsed, ref next_update)) { - render_period = render_args.Time = time; - OnRenderFrameInternal(render_args); + update_timestamp = timestamp; } + timestamp = watch.Elapsed.TotalSeconds; + } while (next_update <= 0 && ++frameskip < max_frameskip); - // Schedule next render event. The 1 second cap ensures - // the process does not appear to hang. - next_render = time_left + TargetRenderPeriod; - next_update = Math.Max(next_update, -1.0); + double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); + if (RaiseRenderFrame(render_elapsed, ref next_render)) + { + render_timestamp = timestamp; } } + bool RaiseUpdateFrame(double time, ref double next_update) + { + if (time > 0) + { + next_update -= time; + if (next_update <= 0.0) + { + update_args.Time = time; + OnUpdateFrameInternal(update_args); + + // Don't schedule a new update more than 1 second in the future. + // Sometimes the hardware cannot keep up with updates + // (e.g. when the update rate is too high, or the UpdateFrame processing + // is too costly). This cap ensures we can catch up in a reasonable time + // once the load becomes lighter. + next_update += TargetUpdatePeriod; + next_update = Math.Max(next_update, -1.0); + update_period = Math.Max(next_update, 0.0); + return true; + } + } + return false; + } + + + bool RaiseRenderFrame(double time, ref double next_render) + { + if (time > 0) + { + next_render -= time; + if (next_render <= 0.0) + { + render_args.Time = time; + OnRenderFrameInternal(render_args); + + // Schedule next render event. The 1 second cap ensures + // the process does not appear to hang. + next_render += TargetRenderPeriod; + next_render = Math.Max(next_render, -1.0); + render_period = Math.Max(next_render, 0.0); + return true; + } + } + return false; + } + #endregion #region SwapBuffers From 56a3dd91e5243a9d16ece6070cb94b5fe1564320 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 7 Jan 2014 09:09:43 +0100 Subject: [PATCH 217/245] [OpenTK] Implemented GameWindow.UpdateTime and RenderTime properties --- Source/OpenTK/GameWindow.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 048d5f64..3669970f 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -446,6 +446,7 @@ namespace OpenTK update_timestamp = timestamp; } timestamp = watch.Elapsed.TotalSeconds; + update_time = timestamp - update_timestamp; } while (next_update <= 0 && ++frameskip < max_frameskip); double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); @@ -453,6 +454,8 @@ namespace OpenTK { render_timestamp = timestamp; } + timestamp = watch.Elapsed.TotalSeconds; + render_time = timestamp - render_timestamp; } bool RaiseUpdateFrame(double time, ref double next_update) From 99df27b6356a0d9b6f20ce48f90ba1258c0e3f8a Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 7 Jan 2014 09:11:55 +0100 Subject: [PATCH 218/245] [OpenTK] Corrected GameWindow.Update/RenderFrequency information --- Source/OpenTK/GameWindow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 3669970f..c3063ff5 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -443,6 +443,7 @@ namespace OpenTK double update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); if (RaiseUpdateFrame(update_elapsed, ref next_update)) { + update_period = update_elapsed; update_timestamp = timestamp; } timestamp = watch.Elapsed.TotalSeconds; @@ -452,6 +453,7 @@ namespace OpenTK double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); if (RaiseRenderFrame(render_elapsed, ref next_render)) { + render_period = render_elapsed; render_timestamp = timestamp; } timestamp = watch.Elapsed.TotalSeconds; @@ -475,7 +477,6 @@ namespace OpenTK // once the load becomes lighter. next_update += TargetUpdatePeriod; next_update = Math.Max(next_update, -1.0); - update_period = Math.Max(next_update, 0.0); return true; } } @@ -497,7 +498,6 @@ namespace OpenTK // the process does not appear to hang. next_render += TargetRenderPeriod; next_render = Math.Max(next_render, -1.0); - render_period = Math.Max(next_render, 0.0); return true; } } From 251f5717ae7a218a70091d5f8938e04419d045ec Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 7 Jan 2014 09:12:35 +0100 Subject: [PATCH 219/245] [Examples] Improve timing information; add vsync toggle --- .../Examples/OpenTK/Test/GameWindowStates.cs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index d2207219..05289eaa 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -82,6 +82,10 @@ namespace Examples.Tests case Key.KeypadMinus: case Key.Minus: Size -= new Size(16, 16); break; + + case Key.V: + VSync = VSync == VSyncMode.On ? VSyncMode.Off : VSyncMode.On; + break; } } @@ -216,30 +220,39 @@ namespace Examples.Tests gfx.Clear(Color.Black); gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + // OpenGL information DrawString(gfx, GL.GetString(StringName.Renderer), line++); DrawString(gfx, GL.GetString(StringName.Version), line++); DrawString(gfx, Context.GraphicsMode.ToString(), line++); - line++; + // GameWindow information + line++; DrawString(gfx, "GameWindow:", 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("[1 - 4]:[5 - 7]: WindowState.{0}:WindowBorder.{1}", + this.WindowState, this.WindowBorder), line++); + DrawString(gfx, String.Format("[V]: VSync.{0}.", VSync), line++); + DrawString(gfx, String.Format("Bounds: {0}", Bounds), line++); + DrawString(gfx, String.Format("ClientRectangle: {0}", ClientRectangle), line++); DrawString(gfx, String.Format("Mouse {0} and {1}. {2}.", mouse_in_window ? "inside" : "outside", CursorVisible ? "visible" : "hidden", Focused ? "Focused" : "Not focused"), line++); - DrawString(gfx, String.Format("Mouse (absolute): {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), line++); - DrawString(gfx, String.Format("Bounds: {0}", Bounds), line++); - DrawString(gfx, String.Format("ClientRectangle: {0}", ClientRectangle), line++); - DrawString(gfx, String.Format("Vsync: {0}", VSync), line++); - DrawString(gfx, String.Format("Frequency: Update ({0:f1}/{1:f1}); Render ({2:f1}/{3:f1})", + DrawString(gfx, String.Format("Mouse coordinates: {0}", new Vector3(Mouse.X, Mouse.Y, Mouse.WheelPrecise)), line++); + + // Timing information + line++; + DrawString(gfx, "Timing:", line++); + DrawString(gfx, String.Format("Frequency: update ({0:f2}/{1:f2}); render ({2:f2}/{3:f2})", UpdateFrequency, TargetUpdateFrequency, RenderFrequency, TargetRenderFrequency), line++); - DrawString(gfx, String.Format("Period: Update ({0:f1}/{1:f1}); Render ({2:f1}/{3:f1})", + DrawString(gfx, String.Format("Period: update ({0:f4}/{1:f4}); render ({2:f4}/{3:f4})", UpdatePeriod, TargetUpdatePeriod, RenderPeriod, TargetRenderPeriod), line++); - DrawString(gfx, String.Format("Time drift: Clock {0:f4}; Update {1:f4}; Render {2:f4}", + DrawString(gfx, String.Format("Time: update {0:f4}; render {1:f4}", + UpdateTime, RenderTime), line++); + DrawString(gfx, String.Format("Drift: clock {0:f4}; update {1:f4}; render {2:f4}", clock_time, clock_time - update_time, clock_time - render_time), line++); DrawString(gfx, String.Format("Text: {0}", TypedText.ToString()), line++); + // Input information line = DrawKeyboards(gfx, line); line = DrawMice(gfx, line); line = DrawJoysticks(gfx, line); @@ -343,7 +356,7 @@ namespace Examples.Tests using (GameWindowStates ex = new GameWindowStates()) { Utilities.SetWindowTitle(ex); - ex.Run(30.0); + ex.Run(30.0); } } } From 6e03d501aea7c4a614034b83c8e7d2deb87aa5c0 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 7 Jan 2014 15:55:11 +0100 Subject: [PATCH 220/245] [OpenTK] Fixed Update/RenderTime calculation These values should only be re-calculated when an Update/RenderFrame event is raised. Otherwise, they should retain their previous values. --- Source/OpenTK/GameWindow.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index c3063ff5..925733e9 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -445,9 +445,13 @@ namespace OpenTK { update_period = update_elapsed; update_timestamp = timestamp; + timestamp = watch.Elapsed.TotalSeconds; + update_time = timestamp - update_timestamp; + } + else + { + timestamp = watch.Elapsed.TotalSeconds; } - timestamp = watch.Elapsed.TotalSeconds; - update_time = timestamp - update_timestamp; } while (next_update <= 0 && ++frameskip < max_frameskip); double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); @@ -455,9 +459,9 @@ namespace OpenTK { render_period = render_elapsed; render_timestamp = timestamp; + timestamp = watch.Elapsed.TotalSeconds; + render_time = timestamp - render_timestamp; } - timestamp = watch.Elapsed.TotalSeconds; - render_time = timestamp - render_timestamp; } bool RaiseUpdateFrame(double time, ref double next_update) From b6a806a5687dc5d9436a4254bafe69cf29fd8aad Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 7 Jan 2014 22:09:02 +0100 Subject: [PATCH 221/245] [OpenTK] Improved timing stability OpenTK now directly calculates the elapsed time between UpdateFrame (RenderFrame) events and compares that directly to TargetUpdatePeriod (TargetRenderPeriod). This significantly simplifies the implementation and improves timing stability. --- Source/OpenTK/GameWindow.cs | 60 +++++++++++++------------------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 925733e9..e3429237 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -83,15 +83,15 @@ namespace OpenTK double update_period, render_period; double target_update_period, target_render_period; + + double update_time; // length of last UpdateFrame event + double render_time; // length of last RenderFrame event double update_timestamp; // timestamp of last UpdateFrame event double render_timestamp; // timestamp of last RenderFrame event - // TODO: Implement these: - double update_time, render_time; VSyncMode vsync; - double next_render = 0.0, next_update = 0.0; FrameEventArgs update_args = new FrameEventArgs(); FrameEventArgs render_args = new FrameEventArgs(); @@ -441,7 +441,7 @@ namespace OpenTK { // Raise UpdateFrame events until we catch up with our target update rate. double update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); - if (RaiseUpdateFrame(update_elapsed, ref next_update)) + if (RaiseUpdateFrame(update_elapsed)) { update_period = update_elapsed; update_timestamp = timestamp; @@ -450,12 +450,15 @@ namespace OpenTK } else { - timestamp = watch.Elapsed.TotalSeconds; + // We have executed enough UpdateFrame events to catch up. + // Break and issue a RenderFrame event. + break; } - } while (next_update <= 0 && ++frameskip < max_frameskip); + } while (++frameskip < max_frameskip); + timestamp = watch.Elapsed.TotalSeconds; double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); - if (RaiseRenderFrame(render_elapsed, ref next_render)) + if (RaiseRenderFrame(render_elapsed)) { render_period = render_elapsed; render_timestamp = timestamp; @@ -464,46 +467,25 @@ namespace OpenTK } } - bool RaiseUpdateFrame(double time, ref double next_update) + bool RaiseUpdateFrame(double time) { - if (time > 0) + if (time >= TargetUpdatePeriod) { - next_update -= time; - if (next_update <= 0.0) - { - update_args.Time = time; - OnUpdateFrameInternal(update_args); - - // Don't schedule a new update more than 1 second in the future. - // Sometimes the hardware cannot keep up with updates - // (e.g. when the update rate is too high, or the UpdateFrame processing - // is too costly). This cap ensures we can catch up in a reasonable time - // once the load becomes lighter. - next_update += TargetUpdatePeriod; - next_update = Math.Max(next_update, -1.0); - return true; - } + update_args.Time = time; + OnUpdateFrameInternal(update_args); + return true; } return false; } - bool RaiseRenderFrame(double time, ref double next_render) + bool RaiseRenderFrame(double time) { - if (time > 0) + if (time >= TargetRenderPeriod) { - next_render -= time; - if (next_render <= 0.0) - { - render_args.Time = time; - OnRenderFrameInternal(render_args); - - // Schedule next render event. The 1 second cap ensures - // the process does not appear to hang. - next_render += TargetRenderPeriod; - next_render = Math.Max(next_render, -1.0); - return true; - } + render_args.Time = time; + OnRenderFrameInternal(render_args); + return true; } return false; } @@ -1083,4 +1065,4 @@ namespace OpenTK } #endregion -} \ No newline at end of file +} From a961fb3db36f4001c1aa8f2b4deba9f180d5f4a9 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 7 Jan 2014 22:09:52 +0100 Subject: [PATCH 222/245] [Examples] Added keys to modify timing Use [ and ] to decrease and increase the UpdateFrame frequency. Use < and > to decrease and increase the RenderFrame frequency. --- Source/Examples/OpenTK/Test/GameWindowStates.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 05289eaa..10ae3141 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -86,6 +86,11 @@ namespace Examples.Tests case Key.V: VSync = VSync == VSyncMode.On ? VSyncMode.Off : VSyncMode.On; break; + + case Key.BracketLeft: TargetUpdateFrequency--; break; + case Key.BracketRight: TargetUpdateFrequency++; break; + case Key.Comma: TargetRenderFrequency--; break; + case Key.Period: TargetRenderFrequency++; break; } } @@ -356,7 +361,7 @@ namespace Examples.Tests using (GameWindowStates ex = new GameWindowStates()) { Utilities.SetWindowTitle(ex); - ex.Run(30.0); + ex.Run(30.0); } } } From e260a429549f9cc23fa62cefd661c9e2a5b8a718 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Wed, 8 Jan 2014 00:02:27 +0100 Subject: [PATCH 223/245] [Win] Refactor huge wndproc into functions --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 551 +++++++++++------- 1 file changed, 333 insertions(+), 218 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 48f7c4b1..220f82ce 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -240,6 +240,316 @@ namespace OpenTK.Platform.Windows #endregion + #region Message Handlers + + void HandleActivate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + // See http://msdn.microsoft.com/en-us/library/ms646274(VS.85).aspx (WM_ACTIVATE notification): + // wParam: The low-order word specifies whether the window is being activated or deactivated. + bool new_focused_state = Focused; + if (IntPtr.Size == 4) + focused = (wParam.ToInt32() & 0xFFFF) != 0; + else + focused = (wParam.ToInt64() & 0xFFFF) != 0; + + if (new_focused_state != Focused) + FocusedChanged(this, EventArgs.Empty); + } + + void HandleEnterModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + // Entering the modal size/move loop: we don't want rendering to + // stop during this time, so we register a timer callback to continue + // processing from time to time. + is_in_modal_loop = true; + StartTimer(handle); + + if (!CursorVisible) + UngrabCursor(); + } + + void HandleExitModalLoop(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + // Exiting from Modal size/move loop: the timer callback is no longer + // necessary. + is_in_modal_loop = false; + StopTimer(handle); + + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); + } + + void HandleWindowPositionChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + unsafe + { + WindowPosition* pos = (WindowPosition*)lParam; + if (window != null && pos->hwnd == window.Handle) + { + Point new_location = new Point(pos->x, pos->y); + if (Location != new_location) + { + bounds.Location = new_location; + Move(this, EventArgs.Empty); + } + + Size new_size = new Size(pos->cx, pos->cy); + if (Size != new_size) + { + bounds.Width = pos->cx; + bounds.Height = pos->cy; + + Win32Rectangle rect; + Functions.GetClientRect(handle, out rect); + client_rectangle = rect.ToRectangle(); + + Functions.SetWindowPos(child_window.Handle, IntPtr.Zero, 0, 0, ClientRectangle.Width, ClientRectangle.Height, + SetWindowPosFlags.NOZORDER | SetWindowPosFlags.NOOWNERZORDER | + SetWindowPosFlags.NOACTIVATE | SetWindowPosFlags.NOSENDCHANGING); + + if (suppress_resize <= 0) + Resize(this, EventArgs.Empty); + } + + if (!is_in_modal_loop) + { + // If we are in a modal resize/move loop, cursor grabbing is + // handled inside [ENTER|EXIT]SIZEMOVE case above. + // If not, then we have to handle cursor grabbing here. + if (!CursorVisible) + GrabCursor(); + } + } + } + } + + void HandleStyleChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + unsafe + { + Debug.WriteLine(wParam.ToString()); + if (wParam == new IntPtr((int)GWL.STYLE)) + { + WindowStyle style = ((StyleStruct*)lParam)->New; + Debug.WriteLine(style.ToString()); + if ((style & WindowStyle.Popup) != 0) + windowBorder = WindowBorder.Hidden; + else if ((style & WindowStyle.ThickFrame) != 0) + windowBorder = WindowBorder.Resizable; + else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0) + windowBorder = WindowBorder.Fixed; + } + } + + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); + } + + void HandleSize(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + SizeMessage state = (SizeMessage)wParam.ToInt64(); + WindowState new_state = windowState; + switch (state) + { + case SizeMessage.RESTORED: new_state = borderless_maximized_window_state ? + WindowState.Maximized : WindowState.Normal; break; + case SizeMessage.MINIMIZED: new_state = WindowState.Minimized; break; + case SizeMessage.MAXIMIZED: new_state = WindowBorder == WindowBorder.Hidden ? + WindowState.Fullscreen : WindowState.Maximized; + break; + } + + if (new_state != windowState) + { + windowState = new_state; + WindowStateChanged(this, EventArgs.Empty); + + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); + } + } + + void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + + if (IntPtr.Size == 4) + key_press.KeyChar = (char)wParam.ToInt32(); + else + key_press.KeyChar = (char)wParam.ToInt64(); + + KeyPress(this, key_press); + } + + void HandleMouseMove(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Point point = new Point( + (short)((uint)lParam.ToInt32() & 0x0000FFFF), + (short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16)); + mouse.Position = point; + + if (mouse_outside_window) + { + // Once we receive a mouse move event, it means that the mouse has + // re-entered the window. + mouse_outside_window = false; + EnableMouseTracking(); + + MouseEnter(this, EventArgs.Empty); + } + } + + void HandleMouseLeave(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + mouse_outside_window = true; + // Mouse tracking is disabled automatically by the OS + + MouseLeave(this, EventArgs.Empty); + } + + void HandleMouseWheel(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + // 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; + } + + void HandleLButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.SetCapture(window.Handle); + mouse[MouseButton.Left] = true; + } + + void HandleMButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.SetCapture(window.Handle); + mouse[MouseButton.Middle] = true; + } + + void HandleRButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.SetCapture(window.Handle); + mouse[MouseButton.Right] = true; + } + + void HandleXButtonDown(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.SetCapture(window.Handle); + mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != + (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; + } + + void HandleLButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.ReleaseCapture(); + mouse[MouseButton.Left] = false; + } + + void HandleMButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.ReleaseCapture(); + mouse[MouseButton.Middle] = false; + } + + void HandleRButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.ReleaseCapture(); + mouse[MouseButton.Right] = false; + } + + void HandleXButtonUp(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + Functions.ReleaseCapture(); + mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != + (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; + } + + void HandleKeyboard(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + bool pressed = + message == WindowMessage.KEYDOWN || + message == WindowMessage.SYSKEYDOWN; + + // Shift/Control/Alt behave strangely when e.g. ShiftRight is held down and ShiftLeft is pressed + // and released. It looks like neither key is released in this case, or that the wrong key is + // released in the case of Control and Alt. + // To combat this, we are going to release both keys when either is released. Hacky, but should work. + // Win95 does not distinguish left/right key constants (GetAsyncKeyState returns 0). + // In this case, both keys will be reported as pressed. + + bool extended = (lParam.ToInt64() & ExtendedBit) != 0; + short scancode = (short)((lParam.ToInt64() >> 16) & 0xFF); + VirtualKeys vkey = (VirtualKeys)wParam; + bool is_valid; + Key key = KeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid); + + 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); + } + } + } + + void HandleKillFocus(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + keyboard.ClearKeys(); + } + + void HandleCreate(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + CreateStruct cs = (CreateStruct)Marshal.PtrToStructure(lParam, typeof(CreateStruct)); + if (cs.hwndParent == IntPtr.Zero) + { + bounds.X = cs.x; + bounds.Y = cs.y; + bounds.Width = cs.cx; + bounds.Height = cs.cy; + + Win32Rectangle rect; + Functions.GetClientRect(handle, out rect); + client_rectangle = rect.ToRectangle(); + + invisible_since_creation = true; + } + } + + void HandleClose(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); + + Closing(this, e); + + if (!e.Cancel) + { + DestroyWindow(); + } + } + + void HandleDestroy(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + exists = false; + + Functions.UnregisterClass(ClassName, Instance); + window.Dispose(); + child_window.Dispose(); + + Closed(this, EventArgs.Empty); + } + + #endregion + #region WindowProcedure IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -249,132 +559,32 @@ namespace OpenTK.Platform.Windows #region Size / Move / Style events case WindowMessage.ACTIVATE: - // See http://msdn.microsoft.com/en-us/library/ms646274(VS.85).aspx (WM_ACTIVATE notification): - // wParam: The low-order word specifies whether the window is being activated or deactivated. - bool new_focused_state = Focused; - if (IntPtr.Size == 4) - focused = (wParam.ToInt32() & 0xFFFF) != 0; - else - focused = (wParam.ToInt64() & 0xFFFF) != 0; - - if (new_focused_state != Focused) - FocusedChanged(this, EventArgs.Empty); + HandleActivate(handle, message, wParam, lParam); break; case WindowMessage.ENTERMENULOOP: case WindowMessage.ENTERSIZEMOVE: - // Entering the modal size/move loop: we don't want rendering to - // stop during this time, so we register a timer callback to continue - // processing from time to time. - is_in_modal_loop = true; - StartTimer(handle); - - if (!CursorVisible) - UngrabCursor(); + HandleEnterModalLoop(handle, message, wParam, lParam); break; case WindowMessage.EXITMENULOOP: case WindowMessage.EXITSIZEMOVE: - // Exiting from Modal size/move loop: the timer callback is no longer - // necessary. - is_in_modal_loop = false; - StopTimer(handle); - - // Ensure cursor remains grabbed - if (!CursorVisible) - GrabCursor(); + HandleExitModalLoop(handle, message, wParam, lParam); break; case WindowMessage.ERASEBKGND: return new IntPtr(1); case WindowMessage.WINDOWPOSCHANGED: - unsafe - { - WindowPosition* pos = (WindowPosition*)lParam; - if (window != null && pos->hwnd == window.Handle) - { - Point new_location = new Point(pos->x, pos->y); - if (Location != new_location) - { - bounds.Location = new_location; - Move(this, EventArgs.Empty); - } - - Size new_size = new Size(pos->cx, pos->cy); - if (Size != new_size) - { - bounds.Width = pos->cx; - bounds.Height = pos->cy; - - Win32Rectangle rect; - Functions.GetClientRect(handle, out rect); - client_rectangle = rect.ToRectangle(); - - Functions.SetWindowPos(child_window.Handle, IntPtr.Zero, 0, 0, ClientRectangle.Width, ClientRectangle.Height, - SetWindowPosFlags.NOZORDER | SetWindowPosFlags.NOOWNERZORDER | - SetWindowPosFlags.NOACTIVATE | SetWindowPosFlags.NOSENDCHANGING); - - if (suppress_resize <= 0) - Resize(this, EventArgs.Empty); - } - - if (!is_in_modal_loop) - { - // If we are in a modal resize/move loop, cursor grabbing is - // handled inside [ENTER|EXIT]SIZEMOVE case above. - // If not, then we have to handle cursor grabbing here. - if (!CursorVisible) - GrabCursor(); - } - } - } + HandleWindowPositionChanged(handle, message, wParam, lParam); break; case WindowMessage.STYLECHANGED: - unsafe - { - if (wParam.ToInt64() == (long)GWL.STYLE) - { - WindowStyle style = ((StyleStruct*)lParam)->New; - if ((style & WindowStyle.Popup) != 0) - windowBorder = WindowBorder.Hidden; - else if ((style & WindowStyle.ThickFrame) != 0) - windowBorder = WindowBorder.Resizable; - else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0) - windowBorder = WindowBorder.Fixed; - } - } - - // Ensure cursor remains grabbed - if (!CursorVisible) - GrabCursor(); - + HandleStyleChanged(handle, message, wParam, lParam); break; case WindowMessage.SIZE: - SizeMessage state = (SizeMessage)wParam.ToInt64(); - WindowState new_state = windowState; - switch (state) - { - case SizeMessage.RESTORED: new_state = borderless_maximized_window_state ? - WindowState.Maximized : WindowState.Normal; break; - case SizeMessage.MINIMIZED: new_state = WindowState.Minimized; break; - case SizeMessage.MAXIMIZED: new_state = WindowBorder == WindowBorder.Hidden ? - WindowState.Fullscreen : WindowState.Maximized; - break; - } - - if (new_state != windowState) - { - windowState = new_state; - WindowStateChanged(this, EventArgs.Empty); - - // Ensure cursor remains grabbed - if (!CursorVisible) - GrabCursor(); - } - + HandleSize(handle, message, wParam, lParam); break; #endregion @@ -382,84 +592,51 @@ namespace OpenTK.Platform.Windows #region Input events case WindowMessage.CHAR: - if (IntPtr.Size == 4) - key_press.KeyChar = (char)wParam.ToInt32(); - else - key_press.KeyChar = (char)wParam.ToInt64(); - - KeyPress(this, key_press); + HandleChar(handle, message, wParam, lParam); break; case WindowMessage.MOUSEMOVE: - Point point = new Point( - (short)((uint)lParam.ToInt32() & 0x0000FFFF), - (short)(((uint)lParam.ToInt32() & 0xFFFF0000) >> 16)); - mouse.Position = point; - - if (mouse_outside_window) - { - // Once we receive a mouse move event, it means that the mouse has - // re-entered the window. - mouse_outside_window = false; - EnableMouseTracking(); - - MouseEnter(this, EventArgs.Empty); - } + HandleMouseMove(handle, message, wParam, lParam); break; case WindowMessage.MOUSELEAVE: - mouse_outside_window = true; - // Mouse tracking is disabled automatically by the OS - - MouseLeave(this, EventArgs.Empty); + HandleMouseLeave(handle, message, wParam, lParam); 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; + HandleMouseWheel(handle, message, wParam, lParam); break; case WindowMessage.LBUTTONDOWN: - Functions.SetCapture(window.Handle); - mouse[MouseButton.Left] = true; + HandleLButtonDown(handle, message, wParam, lParam); break; case WindowMessage.MBUTTONDOWN: - Functions.SetCapture(window.Handle); - mouse[MouseButton.Middle] = true; + HandleMButtonDown(handle, message, wParam, lParam); break; case WindowMessage.RBUTTONDOWN: - Functions.SetCapture(window.Handle); - mouse[MouseButton.Right] = true; + HandleRButtonDown(handle, message, wParam, lParam); break; case WindowMessage.XBUTTONDOWN: - Functions.SetCapture(window.Handle); - mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != - (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; + HandleXButtonDown(handle, message, wParam, lParam); break; case WindowMessage.LBUTTONUP: - Functions.ReleaseCapture(); - mouse[MouseButton.Left] = false; + HandleLButtonUp(handle, message, wParam, lParam); break; case WindowMessage.MBUTTONUP: - Functions.ReleaseCapture(); - mouse[MouseButton.Middle] = false; + HandleMButtonUp(handle, message, wParam, lParam); break; case WindowMessage.RBUTTONUP: - Functions.ReleaseCapture(); - mouse[MouseButton.Right] = false; + HandleRButtonUp(handle, message, wParam, lParam); break; case WindowMessage.XBUTTONUP: - Functions.ReleaseCapture(); - mouse[((wParam.ToInt32() & 0xFFFF0000) >> 16) != - (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; + HandleXButtonUp(handle, message, wParam, lParam); break; // Keyboard events: @@ -467,47 +644,14 @@ namespace OpenTK.Platform.Windows case WindowMessage.KEYUP: case WindowMessage.SYSKEYDOWN: case WindowMessage.SYSKEYUP: - bool pressed = - message == WindowMessage.KEYDOWN || - message == WindowMessage.SYSKEYDOWN; - - // Shift/Control/Alt behave strangely when e.g. ShiftRight is held down and ShiftLeft is pressed - // and released. It looks like neither key is released in this case, or that the wrong key is - // released in the case of Control and Alt. - // To combat this, we are going to release both keys when either is released. Hacky, but should work. - // Win95 does not distinguish left/right key constants (GetAsyncKeyState returns 0). - // In this case, both keys will be reported as pressed. - - bool extended = (lParam.ToInt64() & ExtendedBit) != 0; - short scancode = (short)((lParam.ToInt64() >> 16) & 0xFF); - VirtualKeys vkey = (VirtualKeys)wParam; - bool is_valid; - Key key = KeyMap.TranslateKey(scancode, vkey, extended, false, out is_valid); - - 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); - } - - } - + HandleKeyboard(handle, message, wParam, lParam); return IntPtr.Zero; case WindowMessage.SYSCHAR: return IntPtr.Zero; case WindowMessage.KILLFOCUS: - keyboard.ClearKeys(); + HandleKillFocus(handle, message, wParam, lParam); break; #endregion @@ -515,44 +659,15 @@ namespace OpenTK.Platform.Windows #region Creation / Destruction events case WindowMessage.CREATE: - CreateStruct cs = (CreateStruct)Marshal.PtrToStructure(lParam, typeof(CreateStruct)); - if (cs.hwndParent == IntPtr.Zero) - { - bounds.X = cs.x; - bounds.Y = cs.y; - bounds.Width = cs.cx; - bounds.Height = cs.cy; - - Win32Rectangle rect; - Functions.GetClientRect(handle, out rect); - client_rectangle = rect.ToRectangle(); - - invisible_since_creation = true; - } + HandleCreate(handle, message, wParam, lParam); break; case WindowMessage.CLOSE: - System.ComponentModel.CancelEventArgs e = new System.ComponentModel.CancelEventArgs(); - - Closing(this, e); - - if (!e.Cancel) - { - DestroyWindow(); - break; - } - + HandleClose(handle, message, wParam, lParam); return IntPtr.Zero; case WindowMessage.DESTROY: - exists = false; - - Functions.UnregisterClass(ClassName, Instance); - window.Dispose(); - child_window.Dispose(); - - Closed(this, EventArgs.Empty); - + HandleDestroy(handle, message, wParam, lParam); break; #endregion From 51baed7286440fba00a27ae8dc57ba5fb319e535 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Wed, 8 Jan 2014 00:45:42 +0100 Subject: [PATCH 224/245] [Win] Remove unnecessary #if clauses --- Source/OpenTK/Platform/Windows/API.cs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index 316a85ae..c6e22287 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -241,9 +241,7 @@ namespace OpenTK.Platform.Windows #region CallWindowProc -#if RELEASE [SuppressUnmanagedCodeSecurity] -#endif [DllImport("user32.dll", SetLastError = true)] internal static extern LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, WindowMessage Msg, WPARAM wParam, LPARAM lParam); @@ -283,28 +281,20 @@ namespace OpenTK.Platform.Windows return SetWindowLong(handle, GetWindowLongOffsets.WNDPROC, Marshal.GetFunctionPointerForDelegate(newValue)); } -#if RELASE [SuppressUnmanagedCodeSecurity] -#endif [DllImport("user32.dll", SetLastError = true)] static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong); -#if RELASE [SuppressUnmanagedCodeSecurity] -#endif [DllImport("user32.dll", SetLastError = true)] static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong); -#if RELASE [SuppressUnmanagedCodeSecurity] -#endif [DllImport("user32.dll", SetLastError = true)] static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex, [MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong); -#if RELASE [SuppressUnmanagedCodeSecurity] -#endif [DllImport("user32.dll", SetLastError = true)] static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex, [MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong); @@ -407,9 +397,7 @@ namespace OpenTK.Platform.Windows #region DispatchMessage -#if RELEASE - [System.Security.SuppressUnmanagedCodeSecurity] -#endif + [SuppressUnmanagedCodeSecurity] [DllImport("User32.dll"), CLSCompliant(false)] internal static extern LRESULT DispatchMessage(ref MSG msg); @@ -417,9 +405,7 @@ namespace OpenTK.Platform.Windows #region TranslateMessage -#if RELEASE - [System.Security.SuppressUnmanagedCodeSecurity] -#endif + [SuppressUnmanagedCodeSecurity] [DllImport("User32.dll"), CLSCompliant(false)] internal static extern BOOL TranslateMessage(ref MSG lpMsg); @@ -3752,7 +3738,7 @@ namespace OpenTK.Platform.Windows #region WindowMessage - internal enum WindowMessage : uint + internal enum WindowMessage : int { NULL = 0x0000, CREATE = 0x0001, From 7363cfee7baa5644654a5161d936a1bf1c4853f0 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Wed, 8 Jan 2014 19:21:29 +0100 Subject: [PATCH 225/245] [Win] Do not unregister class twice Only the parent window would register a class, but both the parent and the child window would unregister it. This is now fixed. --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 220f82ce..d9e73b02 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -326,25 +326,31 @@ namespace OpenTK.Platform.Windows void HandleStyleChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { + WindowBorder new_border = windowBorder; + unsafe { - Debug.WriteLine(wParam.ToString()); if (wParam == new IntPtr((int)GWL.STYLE)) { WindowStyle style = ((StyleStruct*)lParam)->New; - Debug.WriteLine(style.ToString()); if ((style & WindowStyle.Popup) != 0) - windowBorder = WindowBorder.Hidden; + new_border = WindowBorder.Hidden; else if ((style & WindowStyle.ThickFrame) != 0) - windowBorder = WindowBorder.Resizable; + new_border = WindowBorder.Resizable; else if ((style & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox)) != 0) - windowBorder = WindowBorder.Fixed; + new_border = WindowBorder.Fixed; } } - // Ensure cursor remains grabbed - if (!CursorVisible) - GrabCursor(); + if (new_border != windowBorder) + { + // Ensure cursor remains grabbed + if (!CursorVisible) + GrabCursor(); + + windowBorder = new_border; + WindowBorderChanged(this, EventArgs.Empty); + } } void HandleSize(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) @@ -353,11 +359,18 @@ namespace OpenTK.Platform.Windows WindowState new_state = windowState; switch (state) { - case SizeMessage.RESTORED: new_state = borderless_maximized_window_state ? - WindowState.Maximized : WindowState.Normal; break; - case SizeMessage.MINIMIZED: new_state = WindowState.Minimized; break; - case SizeMessage.MAXIMIZED: new_state = WindowBorder == WindowBorder.Hidden ? - WindowState.Fullscreen : WindowState.Maximized; + case SizeMessage.RESTORED: + new_state = borderless_maximized_window_state ? + WindowState.Maximized : WindowState.Normal; + break; + + case SizeMessage.MINIMIZED: + new_state = WindowState.Minimized; + break; + + case SizeMessage.MAXIMIZED: + new_state = WindowBorder == WindowBorder.Hidden ? + WindowState.Fullscreen : WindowState.Maximized; break; } @@ -374,7 +387,6 @@ namespace OpenTK.Platform.Windows void HandleChar(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { - if (IntPtr.Size == 4) key_press.KeyChar = (char)wParam.ToInt32(); else @@ -541,7 +553,10 @@ namespace OpenTK.Platform.Windows { exists = false; - Functions.UnregisterClass(ClassName, Instance); + if (handle == window.Handle) + { + Functions.UnregisterClass(ClassName, Instance); + } window.Dispose(); child_window.Dispose(); @@ -554,6 +569,8 @@ namespace OpenTK.Platform.Windows IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { + Debug.WriteLine(message.ToString()); + switch (message) { #region Size / Move / Style events @@ -1271,8 +1288,6 @@ namespace OpenTK.Platform.Windows Visible = true; WindowState = state; - - WindowBorderChanged(this, EventArgs.Empty); } } From 51ad513dbbf0b7f4275c42848d438553a38d73e3 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Wed, 8 Jan 2014 19:22:03 +0100 Subject: [PATCH 226/245] [Win] Do not overload internal SetWindowLong The internal function is now appended with "Internal". --- Source/OpenTK/Platform/Windows/API.cs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index c6e22287..e265afdc 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -151,7 +151,12 @@ namespace OpenTK.Platform.Windows internal static extern BOOL AdjustWindowRect([In, Out] ref Win32Rectangle lpRect, WindowStyle dwStyle, BOOL bMenu); [DllImport("user32.dll", EntryPoint = "AdjustWindowRectEx", CallingConvention = CallingConvention.StdCall, SetLastError = true), SuppressUnmanagedCodeSecurity] - internal static extern bool AdjustWindowRectEx(ref Win32Rectangle lpRect, WindowStyle dwStyle, bool bMenu, ExtendedWindowStyle dwExStyle); + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool AdjustWindowRectEx( + ref Win32Rectangle lpRect, + WindowStyle dwStyle, + [MarshalAs(UnmanagedType.Bool)] bool bMenu, + ExtendedWindowStyle dwExStyle); #endregion @@ -262,9 +267,9 @@ namespace OpenTK.Platform.Windows SetLastError(0); if (IntPtr.Size == 4) - retval = new IntPtr(SetWindowLong(handle, item, newValue.ToInt32())); + retval = new IntPtr(SetWindowLongInternal(handle, item, newValue.ToInt32())); else - retval = SetWindowLongPtr(handle, item, newValue); + retval = SetWindowLongPtrInternal(handle, item, newValue); if (retval == IntPtr.Zero) { @@ -282,21 +287,21 @@ namespace OpenTK.Platform.Windows } [SuppressUnmanagedCodeSecurity] - [DllImport("user32.dll", SetLastError = true)] - static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong); + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] + static extern LONG SetWindowLongInternal(HWND hWnd, GetWindowLongOffsets nIndex, LONG dwNewLong); [SuppressUnmanagedCodeSecurity] - [DllImport("user32.dll", SetLastError = true)] - static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong); + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")] + static extern LONG_PTR SetWindowLongPtrInternal(HWND hWnd, GetWindowLongOffsets nIndex, LONG_PTR dwNewLong); [SuppressUnmanagedCodeSecurity] - [DllImport("user32.dll", SetLastError = true)] - static extern LONG SetWindowLong(HWND hWnd, GetWindowLongOffsets nIndex, + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] + static extern LONG SetWindowLongInternal(HWND hWnd, GetWindowLongOffsets nIndex, [MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong); [SuppressUnmanagedCodeSecurity] - [DllImport("user32.dll", SetLastError = true)] - static extern LONG_PTR SetWindowLongPtr(HWND hWnd, GetWindowLongOffsets nIndex, + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")] + static extern LONG_PTR SetWindowLongPtrInternal(HWND hWnd, GetWindowLongOffsets nIndex, [MarshalAs(UnmanagedType.FunctionPtr)]WindowProcedure dwNewLong); #endregion From 4af9d30ccbb9399ea71771233f65cd7acc3860b3 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Wed, 8 Jan 2014 22:29:22 +0100 Subject: [PATCH 227/245] [Win] Fix issue #33; fix issue #34 This patch adds a workaround for WM_STYLECHANGED messages that are not delivered when running on Mono/Windows. If we detect Mono, then we call HandleStyleChanged() directly in order to update the internal state of our WinGLNative instance. --- Source/OpenTK/Platform/Windows/WinGLNative.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index d9e73b02..ae2cfc93 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -326,11 +326,13 @@ namespace OpenTK.Platform.Windows void HandleStyleChanged(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { - WindowBorder new_border = windowBorder; + WindowBorder old_border = windowBorder; + WindowBorder new_border = old_border; unsafe { - if (wParam == new IntPtr((int)GWL.STYLE)) + GWL get_window_style = (GWL)unchecked(wParam.ToInt32()); + if ((get_window_style & (GWL.STYLE | GWL.EXSTYLE)) != 0) { WindowStyle style = ((StyleStruct*)lParam)->New; if ((style & WindowStyle.Popup) != 0) @@ -569,8 +571,6 @@ namespace OpenTK.Platform.Windows IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) { - Debug.WriteLine(message.ToString()); - switch (message) { #region Size / Move / Style events @@ -1249,34 +1249,35 @@ namespace OpenTK.Platform.Windows WindowState state = WindowState; ResetWindowState(); - WindowStyle style = WindowStyle.ClipChildren | WindowStyle.ClipSiblings; + WindowStyle old_style = WindowStyle.ClipChildren | WindowStyle.ClipSiblings; + WindowStyle new_style = old_style; switch (value) { case WindowBorder.Resizable: - style |= WindowStyle.OverlappedWindow; + new_style |= WindowStyle.OverlappedWindow; break; case WindowBorder.Fixed: - style |= WindowStyle.OverlappedWindow & + new_style |= WindowStyle.OverlappedWindow & ~(WindowStyle.ThickFrame | WindowStyle.MaximizeBox | WindowStyle.SizeBox); break; case WindowBorder.Hidden: - style |= WindowStyle.Popup; + new_style |= WindowStyle.Popup; break; } // Make sure client size doesn't change when changing the border style. Size client_size = ClientSize; Win32Rectangle rect = Win32Rectangle.From(client_size); - Functions.AdjustWindowRectEx(ref rect, style, false, ParentStyleEx); + Functions.AdjustWindowRectEx(ref rect, new_style, false, ParentStyleEx); // This avoids leaving garbage on the background window. if (was_visible) Visible = false; - Functions.SetWindowLong(window.Handle, GetWindowLongOffsets.STYLE, (IntPtr)(int)style); + Functions.SetWindowLong(window.Handle, GetWindowLongOffsets.STYLE, (IntPtr)(int)new_style); Functions.SetWindowPos(window.Handle, IntPtr.Zero, 0, 0, rect.Width, rect.Height, SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOZORDER | SetWindowPosFlags.FRAMECHANGED); @@ -1288,6 +1289,24 @@ namespace OpenTK.Platform.Windows Visible = true; WindowState = state; + + // Workaround for github issues #33 and #34, + // where WindowMessage.STYLECHANGED is not + // delivered when running on Mono/Windows. + if (Configuration.RunningOnMono) + { + StyleStruct style = new StyleStruct(); + style.New = new_style; + style.Old = old_style; + unsafe + { + HandleStyleChanged( + window.Handle, + WindowMessage.STYLECHANGED, + new IntPtr((int)(GWL.STYLE | GWL.EXSTYLE)), + new IntPtr(&style)); + } + } } } From 1b3b510376465136adba1e4e976762426be74260 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Thu, 9 Jan 2014 00:10:41 +0100 Subject: [PATCH 228/245] [Win] Check registry keys before accessing Fixes crashes when using OpenTK over the Remote Desktop Client for Mac (version 2010). --- Source/OpenTK/Platform/Windows/WinRawKeyboard.cs | 10 ++++++++-- Source/OpenTK/Platform/Windows/WinRawMouse.cs | 13 ++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs index 81f6e6ee..84b17a18 100644 --- a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs +++ b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs @@ -103,10 +103,11 @@ namespace OpenTK.Platform.Windows // This is a keyboard or USB keyboard device. In the latter case, discover if it really is a // keyboard device by qeurying the registry. RegistryKey regkey = GetRegistryKey(name); - string deviceDesc = (string)regkey.GetValue("DeviceDesc"); + if (regkey == null) + continue; + string deviceDesc = (string)regkey.GetValue("DeviceDesc"); string deviceClass = (string)regkey.GetValue("Class"); - string deviceClassGUID = (string)regkey.GetValue("ClassGUID"); // for windows 8 support via OpenTK issue 3198 // making a guess at backwards compatability. Not sure what older windows returns in these cases... @@ -205,10 +206,15 @@ namespace OpenTK.Platform.Windows static RegistryKey GetRegistryKey(string name) { + if (name.Length < 4) + return null; + // remove the \??\ name = name.Substring(4); string[] split = name.Split('#'); + if (split.Length < 3) + return null; string id_01 = split[0]; // ACPI (Class code) string id_02 = split[1]; // PNP0303 (SubClass code) diff --git a/Source/OpenTK/Platform/Windows/WinRawMouse.cs b/Source/OpenTK/Platform/Windows/WinRawMouse.cs index 727d3b6a..15ae0430 100644 --- a/Source/OpenTK/Platform/Windows/WinRawMouse.cs +++ b/Source/OpenTK/Platform/Windows/WinRawMouse.cs @@ -110,11 +110,13 @@ namespace OpenTK.Platform.Windows // This is a mouse or a USB mouse device. In the latter case, discover if it really is a // mouse device by qeurying the registry. RegistryKey regkey = FindRegistryKey(name); - string deviceDesc = (string)regkey.GetValue("DeviceDesc"); + if (regkey == null) + continue; - + string deviceDesc = (string)regkey.GetValue("DeviceDesc"); string deviceClass = (string)regkey.GetValue("Class") as string; - if(deviceClass == null){ + if(deviceClass == null) + { // Added to address OpenTK issue 3198 with mouse on Windows 8 string deviceClassGUID = (string)regkey.GetValue("ClassGUID"); RegistryKey classGUIDKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" + deviceClassGUID); @@ -266,10 +268,15 @@ namespace OpenTK.Platform.Windows static RegistryKey FindRegistryKey(string name) { + if (name.Length < 4) + return null; + // remove the \??\ name = name.Substring(4); string[] split = name.Split('#'); + if (split.Length < 3) + return null; string id_01 = split[0]; // ACPI (Class code) string id_02 = split[1]; // PNP0303 (SubClass code) From 28ac3cec0b4cce7757c6e69fa2d1ac8033ea2093 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Thu, 9 Jan 2014 00:14:25 +0100 Subject: [PATCH 229/245] [Win] Fix issue #35 (OpenTK over Remote Desktop) When running over remote desktop without hardware acceleration, there are no GraphicsModes that support desktop composition. This patch adds logic to avoid requesting composition-capable modes when running over RDP. Additionally, it changes the mode selection logic to consider modes that support features partially (e.g. 16bpp color instead of 32bpp), albeit with a heavy penalty over fully supported modes. --- .../Platform/Windows/WinGraphicsMode.cs | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index 9de28341..a5886f98 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -216,15 +216,31 @@ namespace OpenTK.Platform.Windows static bool Compare(int got, int requested, ref int distance) { - if (got < requested) + bool valid = true; + if (got == 0 && requested != 0) { - return false; + // mode does not support the requested feature. + valid = false; + } + else if (got >= requested) + { + // mode supports the requested feature, + // calculate the distance from an "ideal" mode + // that matches this feature exactly. + distance += got - requested; } else { - distance += got - requested; - return true; + // mode supports the requested feature, + // but at a suboptimal level. For example: + // - requsted AA = 8x, got 4x + // - requested color = 32bpp, got 16bpp + // We can still use this mode but only if + // no better mode exists. + const int penalty = 8; + distance += penalty * Math.Abs(got - requested); } + return valid; } static AccelerationType GetAccelerationType(ref PixelFormatDescriptor pfd) @@ -262,8 +278,18 @@ namespace OpenTK.Platform.Windows // Does not appear to be supported by DescribePixelFormat //flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER; } - if (System.Environment.OSVersion.Version.Major >= 6) + + if (System.Environment.OSVersion.Version.Major >= 6 && + requested_acceleration_type != AccelerationType.None) { + // Request a compositor-capable mode when running on + // Vista+ and using hardware acceleration. Without this, + // some modes will cause the compositor to turn off, + // which is very annoying to the user. + // Note: compositor-capable modes require hardware + // acceleration. Don't set this flag when running + // with software acceleration (e.g. over Remote Desktop + // as described in bug https://github.com/opentk/opentk/issues/35) flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION; } From 7f64945079866db5c492c405b6f3c88ec022519e Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Thu, 9 Jan 2014 00:51:09 +0100 Subject: [PATCH 230/245] [OpenTK] Do not raise *Frame events when time = 0 Affects issue #40 --- Source/OpenTK/GameWindow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index e3429237..8012eba3 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -469,7 +469,7 @@ namespace OpenTK bool RaiseUpdateFrame(double time) { - if (time >= TargetUpdatePeriod) + if (time > 0 && time >= TargetUpdatePeriod) { update_args.Time = time; OnUpdateFrameInternal(update_args); @@ -481,7 +481,7 @@ namespace OpenTK bool RaiseRenderFrame(double time) { - if (time >= TargetRenderPeriod) + if (time > 0 && time >= TargetRenderPeriod) { render_args.Time = time; OnRenderFrameInternal(render_args); From a4d2a31386b701436fdd9b2806ed8e9c7dd6d1c4 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Thu, 9 Jan 2014 23:36:28 +0100 Subject: [PATCH 231/245] [Mac] OpenGL 3.x/4.x require core profile flag SDL will fail to construct an OpenGL 3.x/4.x context on Mac OS X, unless ContextProfileFlags.CORE is specified. Fixes issue #44 Upstream enhancement request at https://bugzilla.libsdl.org/show_bug.cgi?id=2342 --- Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs b/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs index a34019f9..c7714b58 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs @@ -157,6 +157,8 @@ namespace OpenTK.Platform.SDL2 int major, int minor, GraphicsContextFlags flags) { + ContextProfileFlags cpflags = 0; + if (mode.AccumulatorFormat.BitsPerPixel > 0) { SDL.GL.SetAttribute(ContextAttribute.ACCUM_ALPHA_SIZE, mode.AccumulatorFormat.Alpha); @@ -203,6 +205,15 @@ namespace OpenTK.Platform.SDL2 { SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MAJOR_VERSION, major); SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MINOR_VERSION, minor); + + // Workaround for https://github.com/opentk/opentk/issues/44 + // Mac OS X desktop OpenGL 3.x/4.x contexts require require + // ContextProfileFlags.Core, otherwise they will fail to construct. + if (Configuration.RunningOnMacOS && major >= 3 && + (flags & GraphicsContextFlags.Embedded) == 0) + { + cpflags |= ContextProfileFlags.CORE; + } } if ((flags & GraphicsContextFlags.Debug) != 0) @@ -223,7 +234,6 @@ namespace OpenTK.Platform.SDL2 */ { - ContextProfileFlags cpflags = 0; if ((flags & GraphicsContextFlags.Embedded) != 0) { cpflags |= ContextProfileFlags.ES; From ef5aedba6f751874e90c7a16de9ff2ec2efe0243 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Fri, 10 Jan 2014 09:24:59 +0100 Subject: [PATCH 232/245] [Win] More robust WGL extension detection Affects issue #42 and issue #45 --- Source/OpenTK/Platform/Windows/WglHelper.cs | 87 ++++++++++--------- .../OpenTK/Platform/Windows/WinGLContext.cs | 12 ++- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WglHelper.cs b/Source/OpenTK/Platform/Windows/WglHelper.cs index fd1c3dee..0e863213 100644 --- a/Source/OpenTK/Platform/Windows/WglHelper.cs +++ b/Source/OpenTK/Platform/Windows/WglHelper.cs @@ -8,6 +8,7 @@ #endregion using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Reflection; @@ -34,6 +35,9 @@ namespace OpenTK.Platform.Windows internal const string Library = "OPENGL32.DLL"; + readonly static Dictionary extensions = + new Dictionary(); + private static Assembly assembly; private static Type wglClass; private static Type delegatesClass; @@ -121,43 +125,61 @@ namespace OpenTK.Platform.Windows #endregion - #region public static partial class Arb - - /// Contains ARB extensions for WGL. - public static partial class Arb + public static bool SupportsExtension(string name) { - /// - /// Checks if a Wgl extension is supported by the given context. - /// - /// The device context. - /// The extension to check. - /// True if the extension is supported by the given context, false otherwise - public static bool SupportsExtension(WinGLContext context, string ext) + return SupportsExtension(Wgl.GetCurrentDC(), name); + } + + /// + /// Checks if a Wgl extension is supported by the given context. + /// + /// The device context. + /// The extension to check. + /// True if the extension is supported by the given context, false otherwise + public static bool SupportsExtension(IntPtr dc, string name) + { + if (extensions.Count == 0) { // We cache this locally, as another thread might create a context which doesn't support this method. // The design is far from ideal, but there's no good solution to this issue as long as we are using // static WGL/GL classes. Fortunately, this issue is extremely unlikely to arise in practice, as you'd // have to create one accelerated and one non-accelerated context in the same application, with the // non-accelerated context coming second. - Wgl.Delegates.GetExtensionsStringARB get = Wgl.Delegates.wglGetExtensionsStringARB; + Wgl.Delegates.GetExtensionsStringARB get_arb = Wgl.Delegates.wglGetExtensionsStringARB; + Wgl.Delegates.GetExtensionsStringEXT get_ext = Wgl.Delegates.wglGetExtensionsStringEXT; + IntPtr str_ptr = + get_arb != null ? get_arb(dc) : + get_ext != null ? get_ext() : + IntPtr.Zero; - if (get != null) + if (str_ptr != IntPtr.Zero) { - string[] extensions = null; + string str; + unsafe { - extensions = new string((sbyte*)get(context.DeviceContext)) - .Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + str = new string((sbyte*)str_ptr); } - if (extensions == null || extensions.Length == 0) - return false; - foreach (string s in extensions) - if (s == ext) - return true; + foreach (string ext in str.Split(' ')) + { + extensions.Add(ext, true); + } } - return false; } + + if (extensions.Count > 0) + { + return extensions.ContainsKey(name); + } + return false; + } + + #region public static partial class Arb + + /// Contains ARB extensions for WGL. + public static partial class Arb + { } #endregion @@ -167,27 +189,6 @@ namespace OpenTK.Platform.Windows /// Contains EXT extensions for WGL. public static partial class Ext { - private static string[] extensions; - /// - /// Checks if a Wgl extension is supported by the given context. - /// - /// The extension to check. - /// True if the extension is supported by the given context, false otherwise - public static bool SupportsExtension(string ext) - { - if (Wgl.Delegates.wglGetExtensionsStringEXT != null) - { - if (extensions == null || rebuildExtensionList) - { - extensions = Wgl.Ext.GetExtensionsString().Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - Array.Sort(extensions); - rebuildExtensionList = false; - } - - return Array.BinarySearch(extensions, ext) != -1; - } - return false; - } } #endregion diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 837c3e76..af55d3e5 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -31,6 +31,7 @@ namespace OpenTK.Platform.Windows IntPtr device_context; bool vsync_supported; + bool vsync_tear_supported; readonly WinGraphicsMode ModeSelector; @@ -325,7 +326,13 @@ namespace OpenTK.Platform.Windows lock (LoadLock) { if (vsync_supported) + { + if (value < 0 && !vsync_tear_supported) + { + value = 1; + } Wgl.Ext.SwapInterval(value); + } } } } @@ -339,8 +346,11 @@ namespace OpenTK.Platform.Windows lock (LoadLock) { Wgl.LoadAll(); - vsync_supported = Wgl.Arb.SupportsExtension(this, "WGL_EXT_swap_control") && + vsync_supported = + Wgl.SupportsExtension(DeviceContext, "WGL_EXT_swap_control") && Wgl.Load("wglGetSwapIntervalEXT") && Wgl.Load("wglSwapIntervalEXT"); + vsync_tear_supported = + Wgl.SupportsExtension(DeviceContext, "WGL_EXT_swap_tear"); } base.LoadAll(); From bdfcf43e0b6b18d6bf21b371f5b5c6618c3b757a Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Fri, 10 Jan 2014 15:41:57 +0100 Subject: [PATCH 233/245] [Win] More robust pixel format selection This patch adds more robust checks for WGL_ARB_pixel_format and WGL_ARB_multisample before using the relevant extensions, and adds checks whether Wgl.Arb.ChoosePixelFormat() returns a valid pixel format before trying to use it (thanks to Repetier for catching this edge case.) Additionally, the ChoosePixelFormatPFD code-path now heavily penalizes single-buffered modes when the user requests a double-buffered mode. Affects issues #42 and #45 --- .../Platform/Windows/WinGraphicsMode.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs index a5886f98..1fc3954d 100644 --- a/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs +++ b/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs @@ -99,7 +99,8 @@ namespace OpenTK.Platform.Windows GraphicsMode ChoosePixelFormatARB(IntPtr device, GraphicsMode mode) { GraphicsMode created_mode = null; - if (Wgl.Delegates.wglChoosePixelFormatARB != null) + if (Wgl.SupportsExtension("WGL_ARB_pixel_format") && + Wgl.Delegates.wglChoosePixelFormatARB != null) { List attributes = new List(); attributes.Add((int)WGL_ARB_pixel_format.AccelerationArb); @@ -168,7 +169,8 @@ namespace OpenTK.Platform.Windows attributes.Add(mode.AccumulatorFormat.Alpha); } - if (mode.Samples > 0) + if (mode.Samples > 0 && + Wgl.SupportsExtension("WGL_ARB_multisample")) { attributes.Add((int)WGL_ARB_multisample.SampleBuffersArb); attributes.Add(1); @@ -193,7 +195,8 @@ namespace OpenTK.Platform.Windows int[] format = new int[1]; int count; - if (Wgl.Arb.ChoosePixelFormat(device, attributes.ToArray(), null, format.Length, format, out count)) + if (Wgl.Arb.ChoosePixelFormat(device, attributes.ToArray(), null, format.Length, format, out count) + && count > 0) { created_mode = DescribePixelFormatARB(device, format[0]); } @@ -271,13 +274,6 @@ namespace OpenTK.Platform.Windows { 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 && requested_acceleration_type != AccelerationType.None) @@ -304,6 +300,9 @@ namespace OpenTK.Platform.Windows valid &= GetAccelerationType(ref pfd) == requested_acceleration_type; valid &= (pfd.Flags & flags) == flags; valid &= pfd.PixelType == PixelType.RGBA; // indexed modes not currently supported + // heavily penalize single-buffered modes when the user requests double buffering + if ((pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) == 0 && mode.Buffers > 1) + dist += 1000; 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); From aff97198726d225e31122807ecd154a4c7045e54 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Fri, 10 Jan 2014 16:55:20 +0100 Subject: [PATCH 234/245] [SDL2] Implemented GetWindowWMInfo --- Source/OpenTK/Platform/SDL2/Sdl2.cs | 99 ++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 5dc31dd9..d38039be 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -94,7 +94,6 @@ namespace OpenTK.Platform.SDL2 [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)] public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags); - //public static extern IntPtr SDL_CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags); [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindowFrom", ExactSpelling = true)] @@ -420,6 +419,34 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_WarpMouseInWindow", ExactSpelling = true)] public static extern void WarpMouseInWindow(IntPtr window, int x, int y); + #region SysWM + + /// + /// Retrieves driver-dependent window information. + /// + /// + /// The window about which information is being requested. + /// + /// + /// Returns driver-dependent information about the specified window. + /// + /// + /// True, if the function is implemented and the version number of the info struct is valid; + /// false, otherwise. + /// + public static bool GetWindowWMInfo(IntPtr window, out SysWMInfo info) + { + info = new SysWMInfo(); + info.Version = GetVersion(); + return GetWindowWMInfoInternal(window, ref info); + } + + [SuppressUnmanagedCodeSecurity] + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetWindowWMInfo", ExactSpelling = true)] + static extern bool GetWindowWMInfoInternal(IntPtr window, ref SysWMInfo info); + + #endregion + public partial class GL { [SuppressUnmanagedCodeSecurity] @@ -1200,6 +1227,17 @@ namespace OpenTK.Platform.SDL2 JOYSTICK | HAPTIC | GAMECONTROLLER } + enum SysWMType + { + Unknown = 0, + Windows, + X11, + Wayland, + DirectFB, + Cocoa, + UIKit, + } + enum WindowEventID : byte { NONE, @@ -1517,6 +1555,65 @@ namespace OpenTK.Platform.SDL2 public int Height; } + struct SysWMInfo + { + public Version Version; + public SysWMType Subsystem; + public SysInfo Info; + + [StructLayout(LayoutKind.Explicit)] + public struct SysInfo + { + [FieldOffset(0)] + public WindowsInfo Windows; + [FieldOffset(0)] + public X11Info X11; + [FieldOffset(0)] + public WaylandInfo Wayland; + [FieldOffset(0)] + public DirectFBInfo DirectFB; + [FieldOffset(0)] + public CocoaInfo Cocoa; + [FieldOffset(0)] + public UIKitInfo UIKit; + + public struct WindowsInfo + { + public IntPtr Window; + } + + public struct X11Info + { + public IntPtr Display; + public IntPtr Window; + } + + public struct WaylandInfo + { + public IntPtr Display; + public IntPtr Surface; + public IntPtr ShellSurface; + } + + public struct DirectFBInfo + { + public IntPtr Dfb; + public IntPtr Window; + public IntPtr Surface; + } + + public struct CocoaInfo + { + public IntPtr Window; + } + + public struct UIKitInfo + { + public IntPtr Window; + } + } + } + struct TextEditingEvent { public const int TextSize = 32; From 19eb72b3a9afe06536f346a822aac4e57841e5d4 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Fri, 10 Jan 2014 16:56:26 +0100 Subject: [PATCH 235/245] [OpenTK] Fixed Utilities.CreateSdl2WindowInfo Utilities.CreateSdl2WindowInfo should store the specified windowHandle directly instead of trying to call SDL.SDL.CreateWindowFrom. --- Source/OpenTK/Platform/Utilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/Utilities.cs b/Source/OpenTK/Platform/Utilities.cs index 67c92a78..cbf0e811 100644 --- a/Source/OpenTK/Platform/Utilities.cs +++ b/Source/OpenTK/Platform/Utilities.cs @@ -301,7 +301,7 @@ namespace OpenTK.Platform public static IWindowInfo CreateSdl2WindowInfo(IntPtr windowHandle) { return new OpenTK.Platform.SDL2.Sdl2WindowInfo( - SDL2.SDL.CreateWindowFrom(windowHandle), null); + windowHandle, null); } #endregion From c31f64f7e1508d7aad08a043692aa123a9cebdbf Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sat, 11 Jan 2014 01:46:38 +0100 Subject: [PATCH 236/245] [OpenTK] Frameskip needs TargetUpdateFrequency!=0 --- Source/OpenTK/GameWindow.cs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 8012eba3..a4a61314 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -441,20 +441,23 @@ namespace OpenTK { // Raise UpdateFrame events until we catch up with our target update rate. double update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); - if (RaiseUpdateFrame(update_elapsed)) + if (update_elapsed > 0) { - update_period = update_elapsed; - update_timestamp = timestamp; - timestamp = watch.Elapsed.TotalSeconds; - update_time = timestamp - update_timestamp; + if (RaiseUpdateFrame(update_elapsed)) + { + update_period = update_elapsed; + update_timestamp = timestamp; + timestamp = watch.Elapsed.TotalSeconds; + update_time = timestamp - update_timestamp; + } + else + { + // We have executed enough UpdateFrame events to catch up. + // Break and issue a RenderFrame event. + break; + } } - else - { - // We have executed enough UpdateFrame events to catch up. - // Break and issue a RenderFrame event. - break; - } - } while (++frameskip < max_frameskip); + } while (TargetRenderFrequency > 0 && ++frameskip < max_frameskip); timestamp = watch.Elapsed.TotalSeconds; double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); From fca9f930e466a48448ccc159352281a7d68153d8 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Sun, 12 Jan 2014 21:37:18 +0100 Subject: [PATCH 237/245] [OpenTK] Fix UpdateFrame loop condition Multiple UpdateFrame events should be raised to match the desired TargetUpdateFrequency, when TargetUpdateFrequency > 0. The loop would incorrectly check for TargetRenderFrequency instead. Affects issue #43 --- Source/OpenTK/GameWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index a4a61314..ad8ed413 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -457,7 +457,7 @@ namespace OpenTK break; } } - } while (TargetRenderFrequency > 0 && ++frameskip < max_frameskip); + } while (TargetUpdateFrequency > 0 && ++frameskip < max_frameskip); timestamp = watch.Elapsed.TotalSeconds; double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); From 7afe48c9798aa3e5eb5124ba0a978fdd81985d79 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Sun, 12 Jan 2014 22:05:15 +0100 Subject: [PATCH 238/245] [Audio] Don't crash when Alc.GetString() returns null MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alc.GetString() could crash if the unmanaged code returned null due to any kind of failure. This is now fixed and better documented. Additionally, the array overload for Alc.GetString() will now correctly forward the ‘device’ parameter to unmanaged code. --- Source/OpenTK/Audio/OpenAL/Alc/Alc.cs | 75 ++++++++++++++++++++------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs b/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs index 38767a15..371bd81f 100644 --- a/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs +++ b/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Security; @@ -262,7 +263,13 @@ namespace OpenTK.Audio.OpenAL /// A string containing the name of the Device. public static string GetString(IntPtr device, AlcGetString param) { - return Marshal.PtrToStringAnsi(GetStringPrivate(device, param)); + IntPtr pstr = GetStringPrivate(device, param); + string str = String.Empty; + if (pstr != IntPtr.Zero) + { + str = Marshal.PtrToStringAnsi(pstr); + } + return str; } /// This function returns a List of strings related to the context. @@ -277,26 +284,54 @@ namespace OpenTK.Audio.OpenAL public static IList GetString(IntPtr device, AlcGetStringList param) { List result = new List(); - IntPtr t = GetStringPrivate(IntPtr.Zero, (AlcGetString)param); - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - byte b; - int offset = 0; - do - { - b = Marshal.ReadByte(t, offset++); - if (b != 0) - sb.Append((char)b); - if (b == 0) - { - result.Add(sb.ToString()); - if (Marshal.ReadByte(t, offset) == 0) // offset already properly increased through ++ - break; // 2x null - else - sb.Remove(0, sb.Length); // 1x null - } - } while (true); - return (IList)result; + // We cannot use Marshal.PtrToStringAnsi(), + // because alcGetString is defined to return either a nul-terminated string, + // or an array of nul-terminated strings terminated by an extra nul. + // Marshal.PtrToStringAnsi() will fail in the latter case (it will only + // return the very first string in the array.) + // We'll have to marshal this ourselves. + IntPtr t = GetStringPrivate(device, (AlcGetString)param); + if (t != IntPtr.Zero) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + byte b; + int offset = 0; + do + { + b = Marshal.ReadByte(t, offset++); + if (b != 0) + { + sb.Append((char)b); + } + else + { + // One string from the array is complete + result.Add(sb.ToString()); + + // Check whether the array has finished + // Note: offset already been increased through offset++ above + if (Marshal.ReadByte(t, offset) == 0) + { + // 2x consecutive nuls, we've read the whole array + break; + } + else + { + // Another string is starting, clear the StringBuilder + sb.Remove(0, sb.Length); + } + } + } + while (true); + } + else + { + Debug.Print("[Audio] Alc.GetString({0}, {1}) returned null.", + device, param); + } + + return result; } [DllImport(Alc.Lib, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = Alc.Style, CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity()] From b3554bb74c1c41a9ccb91eae0020a2b1e0e8c89d Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 13 Jan 2014 11:22:33 +0100 Subject: [PATCH 239/245] [OpenTK] Simplified update and render loops --- Source/OpenTK/GameWindow.cs | 93 +++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index ad8ed413..4a9cb092 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -433,64 +433,65 @@ namespace OpenTK void DispatchUpdateAndRenderFrame(object sender, EventArgs e) { - const int max_frameskip = 10; - int frameskip = 0; double timestamp = watch.Elapsed.TotalSeconds; + double elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); - do + // Calculate how many update events we need to execute in order to reach + // our desired TargetUpdateFrequency + int update_count = TargetUpdatePeriod != 0 ? + (int)(elapsed / TargetUpdatePeriod) : + 1; + + while (update_count > 0) { // Raise UpdateFrame events until we catch up with our target update rate. - double update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); - if (update_elapsed > 0) + if (elapsed > 0) { - if (RaiseUpdateFrame(update_elapsed)) - { - update_period = update_elapsed; - update_timestamp = timestamp; - timestamp = watch.Elapsed.TotalSeconds; - update_time = timestamp - update_timestamp; - } - else - { - // We have executed enough UpdateFrame events to catch up. - // Break and issue a RenderFrame event. - break; - } + RaiseUpdateFrame(elapsed, ref timestamp); + --update_count; } - } while (TargetUpdateFrequency > 0 && ++frameskip < max_frameskip); + elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); + } + //timestamp = watch.Elapsed.TotalSeconds; + elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); + if (elapsed > 0 && elapsed >= TargetRenderPeriod) + { + RaiseRenderFrame(elapsed, ref timestamp); + } + + Thread.Sleep(1); + } + + void RaiseUpdateFrame(double elapsed, ref double timestamp) + { + // Raise UpdateFrame event + update_args.Time = elapsed; + OnUpdateFrameInternal(update_args); + + // Update UpdatePeriod/UpdateFrequency properties + update_period = elapsed; + + // Update UpdateTime property + update_timestamp = timestamp; timestamp = watch.Elapsed.TotalSeconds; - double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); - if (RaiseRenderFrame(render_elapsed)) - { - render_period = render_elapsed; - render_timestamp = timestamp; - timestamp = watch.Elapsed.TotalSeconds; - render_time = timestamp - render_timestamp; - } - } - - bool RaiseUpdateFrame(double time) - { - if (time > 0 && time >= TargetUpdatePeriod) - { - update_args.Time = time; - OnUpdateFrameInternal(update_args); - return true; - } - return false; + update_time = timestamp - update_timestamp; } - bool RaiseRenderFrame(double time) + void RaiseRenderFrame(double elapsed, ref double timestamp) { - if (time > 0 && time >= TargetRenderPeriod) - { - render_args.Time = time; - OnRenderFrameInternal(render_args); - return true; - } - return false; + // Raise RenderFrame event + render_args.Time = elapsed; + OnRenderFrameInternal(render_args); + + // Update RenderPeriod/UpdateFrequency properties + render_period = elapsed; + + // Update RenderTime property + render_timestamp = timestamp; + timestamp = watch.Elapsed.TotalSeconds; + render_time = timestamp - render_timestamp; } #endregion From 3eccb898212ba0c254b8c7fcd8420f89e09a8d2b Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Mon, 13 Jan 2014 11:36:56 +0100 Subject: [PATCH 240/245] [OpenTK] Remove Thread.Sleep() from loop timing --- Source/OpenTK/GameWindow.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 4a9cb092..cc1e3e7c 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -431,10 +431,15 @@ namespace OpenTK } } + double ClampElapsed(double elapsed) + { + return MathHelper.Clamp(elapsed, 0.0, 1.0); + } + void DispatchUpdateAndRenderFrame(object sender, EventArgs e) { double timestamp = watch.Elapsed.TotalSeconds; - double elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); + double elapsed = ClampElapsed(timestamp - update_timestamp); // Calculate how many update events we need to execute in order to reach // our desired TargetUpdateFrequency @@ -450,17 +455,15 @@ namespace OpenTK RaiseUpdateFrame(elapsed, ref timestamp); --update_count; } - elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0); + elapsed = ClampElapsed(timestamp - update_timestamp); } //timestamp = watch.Elapsed.TotalSeconds; - elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0); + elapsed = ClampElapsed(timestamp - render_timestamp); if (elapsed > 0 && elapsed >= TargetRenderPeriod) { RaiseRenderFrame(elapsed, ref timestamp); } - - Thread.Sleep(1); } void RaiseUpdateFrame(double elapsed, ref double timestamp) From 97e49b76b2753bd5f7734193637767ddf91d9ae4 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 13:04:30 +0100 Subject: [PATCH 241/245] [OpenTK] Fix UpdateFrame quantization error The UpdateFrame event rate will now match TargetUpdatePeriod even if vsync is enabled. Previously, it would be quantized to a multiple or integer fraction of of the vsync rate. --- Source/OpenTK/GameWindow.cs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index cc1e3e7c..af55fe04 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -90,6 +90,8 @@ namespace OpenTK double update_timestamp; // timestamp of last UpdateFrame event double render_timestamp; // timestamp of last RenderFrame event + double update_epsilon; // quantization error for UpdateFrame events + VSyncMode vsync; FrameEventArgs update_args = new FrameEventArgs(); @@ -439,26 +441,22 @@ namespace OpenTK void DispatchUpdateAndRenderFrame(object sender, EventArgs e) { double timestamp = watch.Elapsed.TotalSeconds; - double elapsed = ClampElapsed(timestamp - update_timestamp); + double elapsed = 0; - // Calculate how many update events we need to execute in order to reach - // our desired TargetUpdateFrequency - int update_count = TargetUpdatePeriod != 0 ? - (int)(elapsed / TargetUpdatePeriod) : - 1; - - while (update_count > 0) + elapsed = ClampElapsed(timestamp - update_timestamp); + while (elapsed > 0 && elapsed + update_epsilon >= TargetUpdatePeriod) { - // Raise UpdateFrame events until we catch up with our target update rate. - if (elapsed > 0) - { - RaiseUpdateFrame(elapsed, ref timestamp); - --update_count; - } + RaiseUpdateFrame(elapsed, ref timestamp); + + // Calculate difference (positive or negative) between + // actual elapsed time and target elapsed time. We must + // compensate for this difference. + update_epsilon += elapsed - TargetUpdatePeriod; + + // Prepare for next loop elapsed = ClampElapsed(timestamp - update_timestamp); } - //timestamp = watch.Elapsed.TotalSeconds; elapsed = ClampElapsed(timestamp - render_timestamp); if (elapsed > 0 && elapsed >= TargetRenderPeriod) { From 7b98255626876870cc921693fde5c0ef384a604a Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 13:27:09 +0100 Subject: [PATCH 242/245] [Examples] Improved timing display GameWindowStates will now display the average fps and draw three moving boxes based on different timing methods. If the timing implementation in OpenTK is working correctly, all three boxes should be moving at the same speed. --- .../Examples/OpenTK/Test/GameWindowStates.cs | 145 +++++++++++++++--- 1 file changed, 122 insertions(+), 23 deletions(-) diff --git a/Source/Examples/OpenTK/Test/GameWindowStates.cs b/Source/Examples/OpenTK/Test/GameWindowStates.cs index 10ae3141..f5cbcfea 100644 --- a/Source/Examples/OpenTK/Test/GameWindowStates.cs +++ b/Source/Examples/OpenTK/Test/GameWindowStates.cs @@ -25,9 +25,23 @@ namespace Examples.Tests int texture; bool mouse_in_window = false; bool viewport_changed = true; + + // time drift Stopwatch watch = new Stopwatch(); double update_time, render_time; + // timing information + double timestamp; + int update_count; + int update_fps; + int render_count; + int render_fps; + + // position of moving objects on screen + double variable_update_timestep_pos = -1; + double variable_refresh_timestep_pos = -1; + double fixed_update_timestep_pos = -1; + public GameWindowStates() : base(800, 600, GraphicsMode.Default) { @@ -35,10 +49,10 @@ namespace Examples.Tests Keyboard.KeyRepeat = true; KeyDown += KeyDownHandler; KeyPress += KeyPressHandler; - + MouseEnter += delegate { mouse_in_window = true; }; MouseLeave += delegate { mouse_in_window = false; }; - + Mouse.Move += MouseMoveHandler; Mouse.ButtonDown += MouseButtonHandler; Mouse.ButtonUp += MouseButtonHandler; @@ -110,7 +124,7 @@ namespace Examples.Tests { return val > max ? max : val < min ? min : val; } - + static float DrawString(Graphics gfx, string str, int line) { return DrawString(gfx, str, line, 0); @@ -217,6 +231,8 @@ namespace Examples.Tests { double clock_time = watch.Elapsed.TotalSeconds; update_time += e.Time; + timestamp += e.Time; + update_count++; using (Graphics gfx = Graphics.FromImage(TextBitmap)) { @@ -247,22 +263,47 @@ namespace Examples.Tests // Timing information line++; DrawString(gfx, "Timing:", line++); - DrawString(gfx, String.Format("Frequency: update ({0:f2}/{1:f2}); render ({2:f2}/{3:f2})", - UpdateFrequency, TargetUpdateFrequency, RenderFrequency, TargetRenderFrequency), line++); - DrawString(gfx, String.Format("Period: update ({0:f4}/{1:f4}); render ({2:f4}/{3:f4})", - UpdatePeriod, TargetUpdatePeriod, RenderPeriod, TargetRenderPeriod), line++); + DrawString(gfx, + String.Format("Frequency: update {4} ({0:f2}/{1:f2}); render {5} ({2:f2}/{3:f2})", + UpdateFrequency, TargetUpdateFrequency, + RenderFrequency, TargetRenderFrequency, + update_fps, render_fps), + line++); + DrawString(gfx, + String.Format("Period: update {4:N4} ({0:f4}/{1:f4}); render {5:N4} ({2:f4}/{3:f4})", + UpdatePeriod, TargetUpdatePeriod, + RenderPeriod, TargetRenderPeriod, + 1.0 / update_fps, 1.0 / render_fps), + line++); DrawString(gfx, String.Format("Time: update {0:f4}; render {1:f4}", - UpdateTime, RenderTime), line++); + UpdateTime, RenderTime), line++); DrawString(gfx, String.Format("Drift: clock {0:f4}; update {1:f4}; render {2:f4}", clock_time, clock_time - update_time, clock_time - render_time), line++); DrawString(gfx, String.Format("Text: {0}", TypedText.ToString()), line++); + if (timestamp >= 1) + { + timestamp -= 1; + update_fps = update_count; + render_fps = render_count; + update_count = 0; + render_count = 0; + + } + // Input information line = DrawKeyboards(gfx, line); line = DrawMice(gfx, line); line = DrawJoysticks(gfx, line); line = DrawLegacyJoysticks(gfx, Joysticks, line); } + + fixed_update_timestep_pos += TargetUpdatePeriod; + variable_update_timestep_pos += e.Time; + if (fixed_update_timestep_pos >= 1) + fixed_update_timestep_pos -= 2; + if (variable_update_timestep_pos >= 1) + variable_update_timestep_pos -= 2; } int DrawJoysticks(Graphics gfx, int line) @@ -323,7 +364,31 @@ namespace Examples.Tests protected override void OnRenderFrame(FrameEventArgs e) { render_time += e.Time; + render_count++; + GL.Clear(ClearBufferMask.ColorBufferBit); + + if (viewport_changed) + { + viewport_changed = false; + GL.Viewport(0, 0, Width, Height); + } + + DrawText(); + + DrawMovingObjects(); + + variable_refresh_timestep_pos += e.Time; + if (variable_refresh_timestep_pos >= 1) + variable_refresh_timestep_pos -= 2; + + SwapBuffers(); + } + + // Uploads our text Bitmap to an OpenGL texture + // and displays is to screen. + private void DrawText() + { System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits( new System.Drawing.Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); @@ -331,29 +396,63 @@ namespace Examples.Tests PixelType.UnsignedByte, data.Scan0); TextBitmap.UnlockBits(data); - if (viewport_changed) - { - viewport_changed = false; - - GL.Viewport(0, 0, Width, Height); - - Matrix4 ortho_projection = Matrix4.CreateOrthographicOffCenter(0, Width, Height, 0, -1, 1); - GL.MatrixMode(MatrixMode.Projection); - GL.LoadMatrix(ref ortho_projection); - } - - GL.Clear(ClearBufferMask.ColorBufferBit); + Matrix4 text_projection = Matrix4.CreateOrthographicOffCenter(0, Width, Height, 0, -1, 1); + GL.MatrixMode(MatrixMode.Projection); + GL.LoadMatrix(ref text_projection); + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadIdentity(); + GL.Color4(Color4.White); + GL.Enable(EnableCap.Texture2D); GL.Begin(PrimitiveType.Quads); - GL.TexCoord2(0, 0); GL.Vertex2(0, 0); GL.TexCoord2(1, 0); GL.Vertex2(TextBitmap.Width, 0); GL.TexCoord2(1, 1); GL.Vertex2(TextBitmap.Width, TextBitmap.Height); GL.TexCoord2(0, 1); GL.Vertex2(0, TextBitmap.Height); - GL.End(); + GL.Disable(EnableCap.Texture2D); + } - SwapBuffers(); + // Draws three moving objects, using three different timing methods: + // 1. fixed framerate based on TargetUpdatePeriod + // 2. variable framerate based on UpdateFrame e.Time + // 3. variable framerate based on RenderFrame e.Time + // If the timing implementation is correct, all three objects + // should be moving at the same speed, regardless of the current + // UpdatePeriod and RenderPeriod. + void DrawMovingObjects() + { + Matrix4 thing_projection = Matrix4.CreateOrthographic(2, 2, -1, 1); + GL.MatrixMode(MatrixMode.Projection); + GL.LoadMatrix(ref thing_projection); + + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadIdentity(); + GL.Translate(fixed_update_timestep_pos, -0.2, 0); + GL.Color4(Color4.Red); + DrawRectangle(); + + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadIdentity(); + GL.Translate(variable_update_timestep_pos, -0.4, 0); + GL.Color4(Color4.DarkGoldenrod); + DrawRectangle(); + + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadIdentity(); + GL.Translate(variable_refresh_timestep_pos, -0.8, 0); + GL.Color4(Color4.DarkGreen); + DrawRectangle(); + } + + private void DrawRectangle() + { + GL.Begin(PrimitiveType.Quads); + GL.Vertex2(-0.05, -0.05); + GL.Vertex2(+0.05, -0.05); + GL.Vertex2(+0.05, +0.05); + GL.Vertex2(-0.05, +0.05); + GL.End(); } public static void Main() From 95d71bc0ccc1b1ad25a9afda81b56efd9f14932c Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 13:33:41 +0100 Subject: [PATCH 243/245] [OpenTK] Respect a TargetUpdatePeriod of zero --- Source/OpenTK/GameWindow.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index af55fe04..92002162 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -455,6 +455,15 @@ namespace OpenTK // Prepare for next loop elapsed = ClampElapsed(timestamp - update_timestamp); + + if (TargetUpdatePeriod <= Double.Epsilon) + { + // According to the TargetUpdatePeriod documentation, + // a TargetUpdatePeriod of zero means we will raise + // UpdateFrame events as fast as possible (one event + // per ProcessEvents() call) + break; + } } elapsed = ClampElapsed(timestamp - render_timestamp); From 1f44cf27a14a77c544f0f8bef96cf8c62e752a30 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 13:55:24 +0100 Subject: [PATCH 244/245] [OpenTK] Do not hang when update rate too high OpenTK will now detect when an UpdateFrame handler is consistently taking too long to finish, and stop raising UpdateFrame events. This gives ProcessEvents() a chance to execute and will protect the application from hanging up. --- Source/OpenTK/GameWindow.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 92002162..5cd8b81a 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -92,6 +92,8 @@ namespace OpenTK double update_epsilon; // quantization error for UpdateFrame events + bool is_running_slowly; // true, when UpdatePeriod cannot reach TargetUpdatePeriod + VSyncMode vsync; FrameEventArgs update_args = new FrameEventArgs(); @@ -440,6 +442,7 @@ namespace OpenTK void DispatchUpdateAndRenderFrame(object sender, EventArgs e) { + int is_running_slowly_retries = 4; double timestamp = watch.Elapsed.TotalSeconds; double elapsed = 0; @@ -464,6 +467,14 @@ namespace OpenTK // per ProcessEvents() call) break; } + + is_running_slowly = update_epsilon >= TargetUpdatePeriod; + if (is_running_slowly && --is_running_slowly_retries == 0) + { + // If UpdateFrame consistently takes longer than TargetUpdateFrame + // stop raising events to avoid hanging inside the UpdateFrame loop. + break; + } } elapsed = ClampElapsed(timestamp - render_timestamp); From 0c9b612bff7d88af8416c18147f9bc7d537a18ce Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 14:20:38 +0100 Subject: [PATCH 245/245] [OpenTK] Increase max Update/RenderFrame rates Given the new 144Hz monitors on the market today, it makes sense to increase the Update/RenderFrame limit from 200Hz to 500Hz. --- Source/OpenTK/GameWindow.cs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 5cd8b81a..559c90b1 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -75,6 +75,8 @@ namespace OpenTK { #region --- Fields --- + const double MaxFrequency = 500.0; // Frequency cap for Update/RenderFrame events + readonly Stopwatch watch = new Stopwatch(); IGraphicsContext glContext; @@ -681,7 +683,7 @@ namespace OpenTK /// /// /// A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities). - /// Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz. + /// Values lower than 1.0Hz are clamped to 0.0. Values higher than 500.0Hz are clamped to 200.0Hz. /// public double TargetRenderFrequency { @@ -699,11 +701,11 @@ namespace OpenTK { TargetRenderPeriod = 0.0; } - else if (value <= 200.0) + else if (value <= MaxFrequency) { TargetRenderPeriod = 1.0 / value; } - else Debug.Print("Target render frequency clamped to 200.0Hz."); // TODO: Where is it actually performed? + else Debug.Print("Target render frequency clamped to {0}Hz.", MaxFrequency); } } @@ -716,7 +718,7 @@ namespace OpenTK /// /// /// A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities). - /// Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0. + /// Values lower than 0.002 seconds (500Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0. /// public double TargetRenderPeriod { @@ -728,7 +730,7 @@ namespace OpenTK set { EnsureUndisposed(); - if (value <= 0.005) + if (value <= 1 / MaxFrequency) { target_render_period = 0.0; } @@ -749,7 +751,7 @@ namespace OpenTK /// /// /// A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities). - /// Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz. + /// Values lower than 1.0Hz are clamped to 0.0. Values higher than 500.0Hz are clamped to 500.0Hz. /// public double TargetUpdateFrequency { @@ -767,11 +769,11 @@ namespace OpenTK { TargetUpdatePeriod = 0.0; } - else if (value <= 200.0) + else if (value <= MaxFrequency) { TargetUpdatePeriod = 1.0 / value; } - else Debug.Print("Target update frequency clamped to 200.0Hz."); // TODO: Where is it actually performed? + else Debug.Print("Target render frequency clamped to {0}Hz.", MaxFrequency); } } @@ -784,7 +786,7 @@ namespace OpenTK /// /// /// A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities). - /// Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0. + /// Values lower than 0.002 seconds (500Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0. /// public double TargetUpdatePeriod { @@ -796,7 +798,7 @@ namespace OpenTK set { EnsureUndisposed(); - if (value <= 0.005) + if (value <= 1 / MaxFrequency) { target_update_period = 0.0; } @@ -804,7 +806,7 @@ namespace OpenTK { target_update_period = value; } - else Debug.Print("Target update period clamped to 1.0 seconds."); // TODO: Where is it actually performed? + else Debug.Print("Target update period clamped to 1.0 seconds."); } }