From a94a2937323d90476779f50dee5fe99bf17ec0b9 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 6 Jan 2014 14:25:05 +0100 Subject: [PATCH 01/14] [Platform] Implemented legacy IJoystickDriver LegacyJoystickDriver implements the legacy IJoystickDriver interface (GameWindow.Joysticks) in terms of the new IJoystickDriver2 interface (OpenTK.Input.Joystick). This removes a large chunk of code from each platform backend, as they no longer need to implement IJoystickDriver themselves. Additionally, it adds support for device hot plugging which was previously missing. --- Source/OpenTK/OpenTK.csproj | 1 + .../OpenTK/Platform/LegacyJoystickDriver.cs | 106 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 Source/OpenTK/Platform/LegacyJoystickDriver.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index d9b969c9..b30dd262 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -798,6 +798,7 @@ + diff --git a/Source/OpenTK/Platform/LegacyJoystickDriver.cs b/Source/OpenTK/Platform/LegacyJoystickDriver.cs new file mode 100644 index 00000000..1f8e6c3e --- /dev/null +++ b/Source/OpenTK/Platform/LegacyJoystickDriver.cs @@ -0,0 +1,106 @@ +#region License +// +// LegacyJoystickDriver.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; +using OpenTK.Input; + +namespace OpenTK.Platform +{ + internal class LegacyJoystickDriver : IJoystickDriver + { + static readonly string ConnectedName = "Connected Joystick"; + static readonly string DisconnectedName = "Disconnected Joystick"; + readonly List joysticks = new List(); + readonly IList joysticks_readonly; + + class LegacyJoystickDevice : JoystickDevice + { + public LegacyJoystickDevice(int id, int axes, int buttons) + : base(id, axes, buttons) + { } + } + + internal LegacyJoystickDriver() + { + joysticks_readonly = joysticks.AsReadOnly(); + for (int i = 0; i < 4; i++) + { + joysticks.Add(new LegacyJoystickDevice(i, 0, 0)); + joysticks[i].Description = DisconnectedName; + } + } + + public void Poll() + { + for (int i = 0; i < 4; i++) + { + JoystickCapabilities caps = Joystick.GetCapabilities(i); + if (caps.IsConnected && joysticks[i].Description == DisconnectedName) + { + // New joystick connected + JoystickDevice device = new LegacyJoystickDevice(i, caps.AxisCount, caps.ButtonCount); + //device.Description = Joystick.GetName(i); + device.Description = ConnectedName; + } + else if (!caps.IsConnected && joysticks[i].Description != DisconnectedName) + { + // Joystick disconnected + joysticks[i] = new LegacyJoystickDevice(i, 0, 0); + joysticks[i].Description = DisconnectedName; + } + + JoystickState state = Joystick.GetState(i); + for (int axis_index = 0; axis_index < (int)JoystickAxis.Last; axis_index++) + { + JoystickAxis axis = JoystickAxis.Axis0 + axis_index; + joysticks[i].SetAxis(axis, state.GetAxis(axis)); + } + for (int button_index = 0; button_index < (int)JoystickButton.Last; button_index++) + { + JoystickButton button = JoystickButton.Button0 + button_index; + joysticks[i].SetButton(button, state.GetButton(button) == ButtonState.Pressed); + } + } + } + + #region IJoystickDriver Members + + public IList Joysticks + { + get + { + return joysticks_readonly; + } + } + + #endregion + } +} + From d84be0d594793552b81166cc498a7a6e7618a8ea Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 6 Jan 2014 14:47:58 +0100 Subject: [PATCH 02/14] [OpenTK] Added base IPlatformFactory implementation PlatformFactoryBase provides a common base interface for platform backends. Platform backends should inherit from PlatformFactoryBase in order to reduce code duplication. --- Source/OpenTK/OpenTK.csproj | 1 + Source/OpenTK/Platform/PlatformFactoryBase.cs | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 Source/OpenTK/Platform/PlatformFactoryBase.cs diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index b30dd262..0582de96 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -799,6 +799,7 @@ + diff --git a/Source/OpenTK/Platform/PlatformFactoryBase.cs b/Source/OpenTK/Platform/PlatformFactoryBase.cs new file mode 100644 index 00000000..d85c645b --- /dev/null +++ b/Source/OpenTK/Platform/PlatformFactoryBase.cs @@ -0,0 +1,120 @@ +#region License +// +// PlatformFactoryBase.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2014 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Diagnostics; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace OpenTK.Platform +{ + /// \internal + /// + /// Implements IPlatformFactory functionality that is common + /// for all platform backends. IPlatformFactory implementations + /// should inherit from this class. + /// + abstract class PlatformFactoryBase : IPlatformFactory + { + protected bool IsDisposed; + + public PlatformFactoryBase() + { + } + + #region IPlatformFactory Members + + public abstract INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device); + + public abstract IDisplayDeviceDriver CreateDisplayDeviceDriver(); + + public abstract IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags); + + public virtual IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + { + throw new NotImplementedException(); + } + + public abstract GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext(); + + public virtual IGraphicsMode CreateGraphicsMode() + { + throw new NotSupportedException(); + } + + public abstract IKeyboardDriver2 CreateKeyboardDriver(); + + public abstract IMouseDriver2 CreateMouseDriver(); + + public virtual IGamePadDriver CreateGamePadDriver() + { + return new MappedGamePadDriver(); + } + + public abstract IJoystickDriver2 CreateJoystickDriver(); + + public virtual IJoystickDriver CreateLegacyJoystickDriver() + { + return new LegacyJoystickDriver(); + } + + #endregion + + #region IDisposable implementation + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool manual) + { + if (!IsDisposed) + { + if (manual) + { + } + else + { + Debug.Print("[OpenTK] {0} leaked, did you forget to call Dispose()?", GetType()); + } + IsDisposed = true; + } + } + + ~PlatformFactoryBase() + { + Dispose(false); + } + + #endregion + } +} + From 94c3c24bfbabc971367c335aa280cb78a4c86ec5 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 6 Jan 2014 14:49:05 +0100 Subject: [PATCH 03/14] [OpenTK] Platform backends should inherit from PlatformFactoryBase This reduces code duplication wrt deprecated interfaces and common support code between backends. --- Source/OpenTK/Platform/Factory.cs | 75 ++++++-------------- Source/OpenTK/Platform/IPlatformFactory.cs | 2 + Source/OpenTK/Platform/MacOS/MacOSFactory.cs | 55 ++++---------- Source/OpenTK/Platform/SDL2/Sdl2Factory.cs | 52 ++++---------- Source/OpenTK/Platform/Windows/WinFactory.cs | 48 ++++--------- Source/OpenTK/Platform/X11/X11Factory.cs | 58 +++------------ 6 files changed, 73 insertions(+), 217 deletions(-) diff --git a/Source/OpenTK/Platform/Factory.cs b/Source/OpenTK/Platform/Factory.cs index e49eb36b..571deb44 100644 --- a/Source/OpenTK/Platform/Factory.cs +++ b/Source/OpenTK/Platform/Factory.cs @@ -33,6 +33,7 @@ using System.Text; namespace OpenTK.Platform { using Graphics; + using Input; sealed class Factory : IPlatformFactory { @@ -134,27 +135,32 @@ namespace OpenTK.Platform return default_implementation.CreateGraphicsMode(); } - public OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() + public IKeyboardDriver2 CreateKeyboardDriver() { return default_implementation.CreateKeyboardDriver(); } - public OpenTK.Input.IMouseDriver2 CreateMouseDriver() + public IMouseDriver2 CreateMouseDriver() { return default_implementation.CreateMouseDriver(); } - public OpenTK.Input.IGamePadDriver CreateGamePadDriver() + public IGamePadDriver CreateGamePadDriver() { return default_implementation.CreateGamePadDriver(); } - public Input.IJoystickDriver2 CreateJoystickDriver() + public IJoystickDriver2 CreateJoystickDriver() { return default_implementation.CreateJoystickDriver(); } - class UnsupportedPlatform : IPlatformFactory + public IJoystickDriver CreateLegacyJoystickDriver() + { + return default_implementation.CreateLegacyJoystickDriver(); + } + + class UnsupportedPlatform : PlatformFactoryBase { #region Fields @@ -165,92 +171,51 @@ namespace OpenTK.Platform #region IPlatformFactory Members - public INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { throw new PlatformNotSupportedException(error_string); } - public IDisplayDeviceDriver CreateDisplayDeviceDriver() + public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { throw new PlatformNotSupportedException(error_string); } - public IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { throw new PlatformNotSupportedException(error_string); } - public IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { throw new PlatformNotSupportedException(error_string); } - public IGraphicsContext CreateESContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags) + public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { throw new PlatformNotSupportedException(error_string); } - public GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() + public override IGraphicsMode CreateGraphicsMode() { throw new PlatformNotSupportedException(error_string); } - public IGraphicsMode CreateGraphicsMode() + public override IKeyboardDriver2 CreateKeyboardDriver() { throw new PlatformNotSupportedException(error_string); } - public OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() + public override IMouseDriver2 CreateMouseDriver() { throw new PlatformNotSupportedException(error_string); } - public OpenTK.Input.IMouseDriver2 CreateMouseDriver() + public override IJoystickDriver2 CreateJoystickDriver() { throw new PlatformNotSupportedException(error_string); } - public OpenTK.Input.IGamePadDriver CreateGamePadDriver() - { - throw new PlatformNotSupportedException(error_string); - } - - public Input.IJoystickDriver2 CreateJoystickDriver() - { - throw new PlatformNotSupportedException(error_string); - } - - #endregion - - #region IDisposable Members - - void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - { - // nothing to do - } - else - { - Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType()); - } - disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~UnsupportedPlatform() - { - Dispose(false); - } - #endregion } diff --git a/Source/OpenTK/Platform/IPlatformFactory.cs b/Source/OpenTK/Platform/IPlatformFactory.cs index c59654f3..c5abb9c5 100644 --- a/Source/OpenTK/Platform/IPlatformFactory.cs +++ b/Source/OpenTK/Platform/IPlatformFactory.cs @@ -54,5 +54,7 @@ namespace OpenTK.Platform OpenTK.Input.IGamePadDriver CreateGamePadDriver(); Input.IJoystickDriver2 CreateJoystickDriver(); + + Input.IJoystickDriver CreateLegacyJoystickDriver(); } } diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs index 697f2352..8a72db56 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs @@ -35,38 +35,33 @@ namespace OpenTK.Platform.MacOS { using Graphics; - class MacOSFactory : IPlatformFactory + class MacOSFactory : PlatformFactoryBase { - #region Fields - - bool disposed; readonly IInputDriver2 InputDriver = new HIDInput(); - #endregion - #region IPlatformFactory Members - public virtual INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { return new CarbonGLNative(x, y, width, height, title, mode, options, device); } - public virtual IDisplayDeviceDriver CreateDisplayDeviceDriver() + public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { return new QuartzDisplayDeviceDriver(); } - public virtual IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new AglContext(mode, window, shareContext); } - public virtual IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new AglContext(handle, window, shareContext); } - public virtual GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() + public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { return (GraphicsContext.GetCurrentContextDelegate)delegate { @@ -74,27 +69,17 @@ namespace OpenTK.Platform.MacOS }; } - public virtual IGraphicsMode CreateGraphicsMode() - { - throw new NotSupportedException(); - } - - public virtual OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() + public override IKeyboardDriver2 CreateKeyboardDriver() { return InputDriver.KeyboardDriver; } - public virtual OpenTK.Input.IMouseDriver2 CreateMouseDriver() + public override IMouseDriver2 CreateMouseDriver() { return InputDriver.MouseDriver; } - public virtual OpenTK.Input.IGamePadDriver CreateGamePadDriver() - { - return InputDriver.GamePadDriver; - } - - public IJoystickDriver2 CreateJoystickDriver() + public override IJoystickDriver2 CreateJoystickDriver() { return InputDriver.JoystickDriver; } @@ -103,33 +88,19 @@ namespace OpenTK.Platform.MacOS #region IDisposable Members - void Dispose(bool manual) + protected override void Dispose(bool manual) { - if (!disposed) + if (!IsDisposed) { if (manual) { InputDriver.Dispose(); } - else - { - Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType()); - } - disposed = true; + + base.Dispose(manual); } } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~MacOSFactory() - { - Dispose(false); - } - #endregion } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs index b8015f6b..25a3c6d9 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs @@ -32,10 +32,9 @@ using OpenTK.Input; namespace OpenTK.Platform.SDL2 { - class Sdl2Factory : IPlatformFactory + class Sdl2Factory : PlatformFactoryBase { readonly Sdl2InputDriver InputDriver = new Sdl2InputDriver(); - bool disposed; /// /// Gets or sets a value indicating whether to use SDL2 fullscreen-desktop mode @@ -56,27 +55,27 @@ namespace OpenTK.Platform.SDL2 #region IPlatformFactory implementation - public INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + public override 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, InputDriver); } - public IDisplayDeviceDriver CreateDisplayDeviceDriver() + public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { return new Sdl2DisplayDeviceDriver(); } - virtual public IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new Sdl2GraphicsContext(mode, window, shareContext, major, minor, flags); } - public IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { throw new NotImplementedException(); } - public GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() + public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { return (GraphicsContext.GetCurrentContextDelegate)delegate { @@ -84,27 +83,17 @@ namespace OpenTK.Platform.SDL2 }; } - public IGraphicsMode CreateGraphicsMode() - { - return new Sdl2GraphicsMode(); - } - - public IKeyboardDriver2 CreateKeyboardDriver() + public override IKeyboardDriver2 CreateKeyboardDriver() { return InputDriver.KeyboardDriver; } - public IMouseDriver2 CreateMouseDriver() + public override IMouseDriver2 CreateMouseDriver() { return InputDriver.MouseDriver; } - public IGamePadDriver CreateGamePadDriver() - { - return InputDriver.GamePadDriver; - } - - public IJoystickDriver2 CreateJoystickDriver() + public override IJoystickDriver2 CreateJoystickDriver() { return InputDriver.JoystickDriver; } @@ -113,34 +102,19 @@ namespace OpenTK.Platform.SDL2 #region IDisposable Members - void Dispose(bool manual) + protected override void Dispose(bool manual) { - if (!disposed) + if (!IsDisposed) { if (manual) { - Debug.Print("Disposing {0}", GetType()); InputDriver.Dispose(); } - else - { - Debug.WriteLine("Sdl2Factory leaked, did you forget to call Dispose()?"); - } - disposed = true; + + base.Dispose(manual); } } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~Sdl2Factory() - { - Dispose(false); - } - #endregion } } diff --git a/Source/OpenTK/Platform/Windows/WinFactory.cs b/Source/OpenTK/Platform/Windows/WinFactory.cs index 4fa46e2e..877377ab 100644 --- a/Source/OpenTK/Platform/Windows/WinFactory.cs +++ b/Source/OpenTK/Platform/Windows/WinFactory.cs @@ -37,9 +37,8 @@ using OpenTK.Input; namespace OpenTK.Platform.Windows { - class WinFactory : IPlatformFactory + class WinFactory : PlatformFactoryBase { - bool disposed; readonly object SyncRoot = new object(); IInputDriver2 inputDriver; @@ -83,27 +82,27 @@ namespace OpenTK.Platform.Windows #region IPlatformFactory Members - public virtual INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { return new WinGLNative(x, y, width, height, title, options, device); } - public virtual IDisplayDeviceDriver CreateDisplayDeviceDriver() + public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { return new WinDisplayDeviceDriver(); } - public virtual IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new WinGLContext(mode, (WinWindowInfo)window, shareContext, major, minor, flags); } - public virtual IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new WinGLContext(handle, (WinWindowInfo)window, shareContext, major, minor, flags); } - public virtual GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() + public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { return (GraphicsContext.GetCurrentContextDelegate)delegate { @@ -111,27 +110,22 @@ namespace OpenTK.Platform.Windows }; } - public virtual IGraphicsMode CreateGraphicsMode() - { - throw new NotSupportedException(); - } - - public virtual OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() + public override OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() { return InputDriver.KeyboardDriver; } - public virtual OpenTK.Input.IMouseDriver2 CreateMouseDriver() + public override OpenTK.Input.IMouseDriver2 CreateMouseDriver() { return InputDriver.MouseDriver; } - public virtual OpenTK.Input.IGamePadDriver CreateGamePadDriver() + public override OpenTK.Input.IGamePadDriver CreateGamePadDriver() { return InputDriver.GamePadDriver; } - public IJoystickDriver2 CreateJoystickDriver() + public override IJoystickDriver2 CreateJoystickDriver() { return InputDriver.JoystickDriver; } @@ -155,33 +149,19 @@ namespace OpenTK.Platform.Windows #region IDisposable Members - void Dispose(bool manual) + protected override void Dispose(bool manual) { - if (!disposed) + if (!IsDisposed) { if (manual) { InputDriver.Dispose(); } - else - { - Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType()); - } - disposed = true; + + base.Dispose(manual); } } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~WinFactory() - { - Dispose(false); - } - #endregion } } diff --git a/Source/OpenTK/Platform/X11/X11Factory.cs b/Source/OpenTK/Platform/X11/X11Factory.cs index 0fb80058..985f21cb 100644 --- a/Source/OpenTK/Platform/X11/X11Factory.cs +++ b/Source/OpenTK/Platform/X11/X11Factory.cs @@ -28,10 +28,11 @@ using System; using System.Diagnostics; using OpenTK.Graphics; +using OpenTK.Input; namespace OpenTK.Platform.X11 { - class X11Factory : IPlatformFactory + class X11Factory : PlatformFactoryBase { bool disposed; @@ -47,27 +48,27 @@ namespace OpenTK.Platform.X11 #region IPlatformFactory Members - public virtual INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) { return new X11GLNative(x, y, width, height, title, mode, options, device); } - public virtual IDisplayDeviceDriver CreateDisplayDeviceDriver() + public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { return new X11DisplayDevice(); } - public virtual IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new X11GLContext(mode, window, shareContext, directRendering, major, minor, flags); } - public virtual IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) + public override IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { return new X11GLContext(handle, window, shareContext, directRendering, major, minor, flags); } - public virtual GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() + public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { return (GraphicsContext.GetCurrentContextDelegate)delegate { @@ -75,17 +76,17 @@ namespace OpenTK.Platform.X11 }; } - public virtual IGraphicsMode CreateGraphicsMode() + public override IGraphicsMode CreateGraphicsMode() { throw new NotSupportedException(); } - public virtual OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver() + public override IKeyboardDriver2 CreateKeyboardDriver() { return new X11Keyboard(); } - public virtual OpenTK.Input.IMouseDriver2 CreateMouseDriver() + public override IMouseDriver2 CreateMouseDriver() { if (XI2Mouse.IsSupported(IntPtr.Zero)) return new XI2Mouse(); // Requires xorg 1.7 or higher. @@ -93,48 +94,11 @@ namespace OpenTK.Platform.X11 return new X11Mouse(); // Always supported. } - public virtual OpenTK.Input.IGamePadDriver CreateGamePadDriver() + public override IJoystickDriver2 CreateJoystickDriver() { return new X11Joystick(); } - public virtual OpenTK.Input.IJoystickDriver2 CreateJoystickDriver() - { - return new X11Joystick(); - } - - - #endregion - - #region IDisposable Members - - void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - { - // nothing to do - } - else - { - Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType()); - } - disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~X11Factory() - { - Dispose(false); - } - #endregion } } From d467629ad89882566e2b8a8ef7b2329e74f38ded Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 18:15:53 +0100 Subject: [PATCH 04/14] [Linux] Added INotify bindings These are not currently used. --- .../OpenTK/Platform/X11/Bindings/INotify.cs | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 Source/OpenTK/Platform/X11/Bindings/INotify.cs diff --git a/Source/OpenTK/Platform/X11/Bindings/INotify.cs b/Source/OpenTK/Platform/X11/Bindings/INotify.cs new file mode 100644 index 00000000..662b12ab --- /dev/null +++ b/Source/OpenTK/Platform/X11/Bindings/INotify.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace OpenTK.Platform.X11.Bindings +{ + // Fully implemented but not currently used by OpenTK + // See System.IO.FileSystemWatcher for a cross-platform alternative +#if false + class INotify + { + const string lib = ""; + + /// + /// Create and initialize inotify instance + /// + /// + [DllImport(lib, EntryPoint = "inotify_init", ExactSpelling = true)] + public static extern int Init(); + + /// + /// Create and initialize inotify instance with specified flags + /// + /// See + /// A System.Int32 handle to a inotify instance + [DllImport(lib, EntryPoint = "inotify_init1", ExactSpelling = true)] + public static extern int Init(INotifyFlags flags); + + /// + /// Add watch of object pathname to inotify instance fd. Notify about + /// events specified by mask + /// + [DllImport(lib, EntryPoint = "inotify_add_watch", ExactSpelling = true)] + public static extern int AddWatch(int fd, string pathname, INotifyFlags mask); + + /// + /// Remove the watch specified by wd from the inotify instance fd + /// + [DllImport(lib, EntryPoint = "inotify_rm_watch", ExactSpelling = true)] + public static extern int RemoveWatch(int fd, int wd); + } + + /// + /// Describes an INotify event + /// + struct INotifyEvent + { + /// + /// Watch descriptor for wd parameter of INotify methods + /// + public int WatchDescriptor; + + /// + /// Watch mask for mask parameter of INotify methods + /// + public INotifyFlags WatchMask; + + /// + /// Cookie to synchronize two events + /// + public uint Cookie; + + /// + /// Length (including NULs) of name + /// + public uint Length; + + /// + /// + /// + public IntPtr Name; + } + + /// + /// Flags for the parameter of + /// + [Flags] + enum INotifyInitFlags + { + CloExec = 02000000, + NonBlock = 04000 + } + + /// \internal + /// + /// Supported events suitable for MASK parameter of AddWatch. + /// + [Flags] + enum INotifyFlags + { + /// + /// File was accessed + /// + Access = 0x00000001, + + /// + /// File was modified + /// + Modify = 0x00000002, + + /// + /// Metadata changed + /// + Attrib = 0x00000004, + + /// + /// Writable file was closed + /// + CloseWrite = 0x00000008, + + /// + /// Unwritable file closed + /// + CloseNoWrite = 0x00000010, + + /// + /// File closed + /// + Close = CloseWrite | CloseNoWrite, + + /// + /// File was opened + /// + Open = 0x00000020, + + /// + /// File was moved from X + /// + MovedFrom = 0x00000040, + + /// + /// File was moved to Y + /// + MovedTo = 0x00000080, + + /// + /// File was moved + /// + Move = MovedFrom | MovedTo, + + /// + /// Subfile was created + /// + Create = 0x00000100, + + /// + /// Subfile was deleted + /// + Delete = 0x00000200, + + /// + /// Self was deleted + /// + DeleteSelf = 0x00000400, + + /// + /// Self was moved + /// + MoveSelf = 0x00000800, + + /// + /// Backing fs was unmounted + /// + Unmount = 0x00002000, + + /// + /// Event queue overflowed + /// + QueueOverflow = 0x00004000, + + /// + /// File was ignored + /// + Ignored = 0x00008000, + + /// + /// Only watch the path if it is a directory + /// + OnlyDirectory = 0x01000000, + + /// + /// Do not follow symlinks + /// + DontFollow = 0x02000000, + + /// + /// Add to the mask of an already existing watch + /// + MaskAdd = 0x20000000, + + /// + /// Event occurred against dir + /// + IsDirectory = 0x40000000, + + /// + /// Only send event once + /// + Oneshot = 0x80000000, + + /// + /// All events which a program can wait on + /// + AllEvents = + Access | Modify | Attrib | Close | Open | Move | + Create | Delete | DeleteSelf | MoveSelf + } +#endif +} From 4203c14b22f9fa82a0202372069e8d5f1f681df0 Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 18:36:19 +0100 Subject: [PATCH 05/14] [X11] Removed IJoystickDriver from X11Joystick Legacy IJoystickDriver support is now implemented through a LegacyJoystickDriver instance. --- Source/OpenTK/Platform/X11/X11Input.cs | 60 +------------------------- 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs index a8393beb..3847745f 100644 --- a/Source/OpenTK/Platform/X11/X11Input.cs +++ b/Source/OpenTK/Platform/X11/X11Input.cs @@ -24,8 +24,6 @@ namespace OpenTK.Platform.X11 /// internal sealed class X11Input : IInputDriver { - X11Joystick joystick_driver = new X11Joystick(); - //X11WindowInfo window; KeyboardDevice keyboard = new KeyboardDevice(); MouseDevice mouse = new MouseDevice(); List dummy_keyboard_list = new List(1); @@ -96,57 +94,6 @@ namespace OpenTK.Platform.X11 #endregion - #region private void InternalPoll() -#if false - private void InternalPoll() - { - X11.XEvent e = new XEvent(); - try - { - while (!disposed) - { - Functions.XMaskEvent(window.Display, - EventMask.PointerMotionMask | EventMask.PointerMotionHintMask | - EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | - EventMask.KeyPressMask | EventMask.KeyReleaseMask | - EventMask.StructureNotifyMask, ref e); - - if (disposed) - return; - - switch (e.type) - { - case XEventName.KeyPress: - case XEventName.KeyRelease: - keyboardDriver.ProcessKeyboardEvent(ref e.KeyEvent); - break; - - case XEventName.ButtonPress: - case XEventName.ButtonRelease: - mouseDriver.ProcessButton(ref e.ButtonEvent); - break; - - case XEventName.MotionNotify: - mouseDriver.ProcessMotion(ref e.MotionEvent); - break; - - case XEventName.DestroyNotify: - Functions.XPutBackEvent(window.Display, ref e); - Functions.XAutoRepeatOn(window.Display); - return; - } - } - } - catch (ThreadAbortException expt) - { - Functions.XUnmapWindow(window.Display, window.Handle); - Functions.XDestroyWindow(window.Display, window.Handle); - return; - } - } -#endif - #endregion - #region TranslateKey internal bool TranslateKey(ref XKeyEvent e, out Key key) @@ -242,11 +189,9 @@ namespace OpenTK.Platform.X11 #endregion - #region public IList Joysticks - public IList Joysticks { - get { return joystick_driver.Joysticks; } + get { throw new NotImplementedException(); } } #endregion @@ -258,13 +203,10 @@ namespace OpenTK.Platform.X11 /// public void Poll() { - joystick_driver.Poll(); } #endregion - #endregion - #region --- IDisposable Members --- public void Dispose() From b004dc7a0aca54072a14638d2e238897c974cf4a Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 18:36:36 +0100 Subject: [PATCH 06/14] [X11] Added INotify.cs to project files --- Source/OpenTK/OpenTK.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 0582de96..8c3931fc 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -166,6 +166,7 @@ + Code From 4473c8373e6c2aaa5f951fb09e0052b518319a7f Mon Sep 17 00:00:00 2001 From: "Stefanos A." Date: Tue, 14 Jan 2014 18:37:16 +0100 Subject: [PATCH 07/14] [X11] Implemented joystick hotplugging This is a work in progress. --- Source/OpenTK/Platform/X11/X11Joystick.cs | 168 +++++++++++++++------- 1 file changed, 118 insertions(+), 50 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index cbab7099..e8870287 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -28,20 +28,29 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; using System.Text; using OpenTK.Input; namespace OpenTK.Platform.X11 { - struct X11JoyDetails { } + struct X11JoyDetails + { + public bool IsConnected; + } - sealed class X11Joystick : IJoystickDriver, IJoystickDriver2, IGamePadDriver + sealed class X11Joystick : IJoystickDriver2 { #region Fields - List sticks = new List(); - IList sticks_readonly; + readonly object sync = new object(); + + readonly FileSystemWatcher watcher = new FileSystemWatcher(JoystickPath); + readonly FileSystemWatcher watcher_legacy = new FileSystemWatcher(JoystickPathLegacy); + + readonly Dictionary index_to_stick = new Dictionary(); + List> sticks = new List>(); bool disposed; @@ -51,16 +60,29 @@ namespace OpenTK.Platform.X11 public X11Joystick() { - sticks_readonly = sticks.AsReadOnly(); + watcher.Created += JoystickAdded; + watcher.Deleted += JoystickRemoved; + watcher.EnableRaisingEvents = true; + watcher_legacy.Created += JoystickAdded; + watcher_legacy.Deleted += JoystickRemoved; + watcher_legacy.EnableRaisingEvents = true; + } + + #endregion + + #region Private Members + + void OpenJoysticks() + { int number = 0, max_sticks = 25; while (number < max_sticks) { - JoystickDevice stick = OpenJoystick(JoystickPath, number++); + JoystickDevice stick = OpenJoystick(JoystickPath, number++); if (stick != null) { //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", - //number, stick.Axis.Count, stick.Button.Count, JoystickPath); + //number, stick.Axis.Count, stick.Button.Count, JoystickPath); sticks.Add(stick); } } @@ -68,30 +90,98 @@ namespace OpenTK.Platform.X11 number = 0; while (number < max_sticks) { - JoystickDevice stick = OpenJoystick(JoystickPathLegacy, number++); + JoystickDevice stick = OpenJoystick(JoystickPathLegacy, number++); if (stick != null) { //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", - //number, stick.Axis.Count, stick.Button.Count, JoystickPathLegacy); + //number, stick.Axis.Count, stick.Button.Count, JoystickPathLegacy); sticks.Add(stick); } } } + int GetJoystickNumber(string path) + { + if (path.StartsWith("js")) + { + int num; + if (Int32.TryParse(path.Substring(2), out num)) + { + return num; + } + } + return -1; + } + + void JoystickAdded(object sender, FileSystemEventArgs e) + { + lock (sync) + { + string file = Path.GetFileName(e.FullPath); + int number = GetJoystickNumber(file); + if (number != -1) + { + JoystickDevice stick = OpenJoystick(e.FullPath, number); + + // Find the first disconnected joystick (if any) + int i; + for (i = 0; i < sticks.Count; i++) + { + if (!stick.Details.IsConnected) + { + break; + } + } + + // If no disconnected joystick exists, append a new slot + if (i == sticks.Count) + { + sticks.Add(stick); + } + else + { + sticks[i] = stick; + } + + // Map player index to joystick + index_to_stick.Add(index_to_stick.Count, i); + } + } + } + + void JoystickRemoved(object sender, FileSystemEventArgs e) + { + lock (sync) + { + string file = Path.GetFileName(e.FullPath); + int number = GetJoystickNumber(file); + if (number != -1) + { + // Find which joystick id matches this number + int i; + for (i = 0; i < sticks.Count; i++) + { + if (sticks[i].Id == number) + { + break; + } + } + + if (i == sticks.Count) + { + Debug.Print("[Evdev] Joystick id {0} does not exist.", number); + } + else + { + JoystickDevice stick = sticks[i]; + stick.Details.IsConnected = false; + } + } + } + } + #endregion - #region IJoystickDriver - - public int DeviceCount - { - get { return sticks.Count; } - } - - public IList Joysticks - { - get { Poll(); return sticks_readonly; } - } - public void Poll() { JoystickEvent e; @@ -123,13 +213,11 @@ namespace OpenTK.Platform.X11 } } - #endregion - #region Private Members JoystickDevice OpenJoystick(string base_path, int number) { - string path = base_path + number.ToString(); + string path = Path.Combine(base_path, "js" + number.ToString()); JoystickDevice stick = null; int fd = -1; @@ -159,6 +247,10 @@ namespace OpenTK.Platform.X11 UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb); stick.Description = sb.ToString(); + stick.Id = number; + + stick.Details.IsConnected = true; + Debug.Print("Found joystick on path {0}", path); } finally @@ -196,8 +288,8 @@ namespace OpenTK.Platform.X11 Name128 = (2u << 30) | (0x6A << 8) | (0x13 << 0) | (128 << 16) //JSIOCGNAME(128), which is _IOC(_IO_READ, 'j', 0x13, len) } - static readonly string JoystickPath = "/dev/input/js"; - static readonly string JoystickPathLegacy = "/dev/js"; + static readonly string JoystickPath = "/dev/input"; + static readonly string JoystickPathLegacy = "/dev"; [Flags] enum OpenFlags @@ -259,30 +351,6 @@ namespace OpenTK.Platform.X11 #endregion - #region IGamePadDriver Members - - public GamePadCapabilities GetCapabilities(int index) - { - return new GamePadCapabilities(); - } - - public GamePadState GetState(int index) - { - return new GamePadState(); - } - - public string GetName(int index) - { - return String.Empty; - } - - public bool SetVibration(int index, float left, float right) - { - return false; - } - - #endregion - #region IJoystickDriver2 Members JoystickState IJoystickDriver2.GetState(int index) From 4ca8c7876489391d7618cbec5561aa8a682095a6 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 14 Jan 2014 23:40:23 +0100 Subject: [PATCH 08/14] [OpenTK] Suggest alternative to obsolete INativeWindow.InputDriver property --- Source/OpenTK/INativeWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/INativeWindow.cs b/Source/OpenTK/INativeWindow.cs index 9621f6f4..b1df51fa 100644 --- a/Source/OpenTK/INativeWindow.cs +++ b/Source/OpenTK/INativeWindow.cs @@ -129,7 +129,7 @@ namespace OpenTK /// /// This property is deprecated and should not be used. /// - [Obsolete] + [Obsolete("Use OpenTK.Input.Mouse/Keybord/Joystick/GamePad instead.")] OpenTK.Input.IInputDriver InputDriver { get; } /// From fb917a6d8964350151a9dad8eb614df1ec04123d Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 14 Jan 2014 23:51:31 +0100 Subject: [PATCH 09/14] [Linux] Implemented joystick hotplugging --- Source/OpenTK/Platform/X11/X11Joystick.cs | 272 +++++++++++++--------- 1 file changed, 156 insertions(+), 116 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index e8870287..96e71c1a 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -37,7 +37,8 @@ namespace OpenTK.Platform.X11 { struct X11JoyDetails { - public bool IsConnected; + public int FileDescriptor; + public JoystickState State; } sealed class X11Joystick : IJoystickDriver2 @@ -67,6 +68,8 @@ namespace OpenTK.Platform.X11 watcher_legacy.Created += JoystickAdded; watcher_legacy.Deleted += JoystickRemoved; watcher_legacy.EnableRaisingEvents = true; + + OpenJoysticks(); } #endregion @@ -75,27 +78,28 @@ namespace OpenTK.Platform.X11 void OpenJoysticks() { - int number = 0, max_sticks = 25; - while (number < max_sticks) + lock (sync) { - JoystickDevice stick = OpenJoystick(JoystickPath, number++); - if (stick != null) + foreach (string file in Directory.GetFiles(JoystickPath)) { - //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", - //number, stick.Axis.Count, stick.Button.Count, JoystickPath); - sticks.Add(stick); + JoystickDevice stick = OpenJoystick(file); + if (stick != null) + { + //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", + //number, stick.Axis.Count, stick.Button.Count, JoystickPath); + sticks.Add(stick); + } } - } - number = 0; - while (number < max_sticks) - { - JoystickDevice stick = OpenJoystick(JoystickPathLegacy, number++); - if (stick != null) + foreach (string file in Directory.GetFiles(JoystickPathLegacy)) { - //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", - //number, stick.Axis.Count, stick.Button.Count, JoystickPathLegacy); - sticks.Add(stick); + JoystickDevice stick = OpenJoystick(file); + if (stick != null) + { + //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", + //number, stick.Axis.Count, stick.Button.Count, JoystickPathLegacy); + sticks.Add(stick); + } } } } @@ -117,35 +121,7 @@ namespace OpenTK.Platform.X11 { lock (sync) { - string file = Path.GetFileName(e.FullPath); - int number = GetJoystickNumber(file); - if (number != -1) - { - JoystickDevice stick = OpenJoystick(e.FullPath, number); - - // Find the first disconnected joystick (if any) - int i; - for (i = 0; i < sticks.Count; i++) - { - if (!stick.Details.IsConnected) - { - break; - } - } - - // If no disconnected joystick exists, append a new slot - if (i == sticks.Count) - { - sticks.Add(stick); - } - else - { - sticks[i] = stick; - } - - // Map player index to joystick - index_to_stick.Add(index_to_stick.Count, i); - } + OpenJoystick(e.FullPath); } } @@ -173,8 +149,7 @@ namespace OpenTK.Platform.X11 } else { - JoystickDevice stick = sticks[i]; - stick.Details.IsConnected = false; + CloseJoystick(sticks[i]); } } } @@ -182,84 +157,135 @@ namespace OpenTK.Platform.X11 #endregion - public void Poll() + #region Private Members + + JoystickDevice OpenJoystick(string path) + { + JoystickDevice stick = null; + + int number = GetJoystickNumber(Path.GetFileName(path)); + if (number >= 0) + { + int fd = -1; + try + { + fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock); + if (fd == -1) + return null; + + // Check joystick driver version (must be 1.0+) + int driver_version = 0x00000800; + UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Version, ref driver_version); + if (driver_version < 0x00010000) + return null; + + // Get number of joystick axes + int axes = 0; + UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes); + + // Get number of joystick buttons + int buttons = 0; + UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons); + + stick = new JoystickDevice(number, axes, buttons); + + StringBuilder sb = new StringBuilder(128); + UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb); + stick.Description = sb.ToString(); + + stick.Details.FileDescriptor = fd; + stick.Details.State.SetIsConnected(true); + //stick.Details.Guid = + + // Find the first disconnected joystick (if any) + int i; + for (i = 0; i < sticks.Count; i++) + { + if (!sticks[i].Details.State.IsConnected) + { + break; + } + } + + // If no disconnected joystick exists, append a new slot + if (i == sticks.Count) + { + sticks.Add(stick); + } + else + { + sticks[i] = stick; + } + + // Map player index to joystick + index_to_stick.Add(index_to_stick.Count, i); + + Debug.Print("Found joystick on path {0}", path); + } + finally + { + if (stick == null && fd != -1) + UnsafeNativeMethods.close(fd); + } + } + + return stick; + } + + void CloseJoystick(JoystickDevice js) + { + UnsafeNativeMethods.close(js.Details.FileDescriptor); + js.Details.State = new JoystickState(); // clear joystick state + js.Details.FileDescriptor = -1; + + // find and remove the joystick index from index_to_stick + int key = -1; + foreach (int i in index_to_stick.Keys) + { + if (sticks[index_to_stick[i]] == js) + { + key = i; + break; + } + } + + if (index_to_stick.ContainsKey(key)) + { + index_to_stick.Remove(key); + } + } + + void PollJoystick(JoystickDevice js) { JoystickEvent e; - foreach (JoystickDevice js in sticks) + unsafe { - unsafe + while ((long)UnsafeNativeMethods.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0) { - while ((long)UnsafeNativeMethods.read(js.Id, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0) + e.Type &= ~JoystickEventType.Init; + + switch (e.Type) { - e.Type &= ~JoystickEventType.Init; + case JoystickEventType.Axis: + // Flip vertical axes so that +1 point up. + if (e.Number % 2 == 0) + js.Details.State.SetAxis((JoystickAxis)e.Number, e.Value); + else + js.Details.State.SetAxis((JoystickAxis)e.Number, unchecked((short)-e.Value)); + break; - switch (e.Type) - { - case JoystickEventType.Axis: - // Flip vertical axes so that +1 point up. - if (e.Number % 2 == 0) - js.SetAxis((JoystickAxis)e.Number, e.Value / 32767.0f); - else - js.SetAxis((JoystickAxis)e.Number, -e.Value / 32767.0f); - break; - - case JoystickEventType.Button: - js.SetButton((JoystickButton)e.Number, e.Value != 0); - break; - } + case JoystickEventType.Button: + js.Details.State.SetButton((JoystickButton)e.Number, e.Value != 0); + break; } } } } - #region Private Members - - JoystickDevice OpenJoystick(string base_path, int number) + bool IsValid(int index) { - string path = Path.Combine(base_path, "js" + number.ToString()); - JoystickDevice stick = null; - - int fd = -1; - try - { - fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock); - if (fd == -1) - return null; - - // Check joystick driver version (must be 1.0+) - int driver_version = 0x00000800; - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Version, ref driver_version); - if (driver_version < 0x00010000) - return null; - - // Get number of joystick axes - int axes = 0; - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes); - - // Get number of joystick buttons - int buttons = 0; - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons); - - stick = new JoystickDevice(fd, axes, buttons); - - StringBuilder sb = new StringBuilder(128); - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb); - stick.Description = sb.ToString(); - - stick.Id = number; - - stick.Details.IsConnected = true; - - Debug.Print("Found joystick on path {0}", path); - } - finally - { - if (stick == null && fd != -1) - UnsafeNativeMethods.close(fd); - } - - return stick; + return index_to_stick.ContainsKey(index); } #region UnsafeNativeMethods @@ -335,9 +361,9 @@ namespace OpenTK.Platform.X11 { } - foreach (JoystickDevice js in sticks) + foreach (JoystickDevice js in sticks) { - UnsafeNativeMethods.close(js.Id); + CloseJoystick(js); } disposed = true; @@ -355,12 +381,26 @@ namespace OpenTK.Platform.X11 JoystickState IJoystickDriver2.GetState(int index) { + if (IsValid(index)) + { + JoystickDevice js = + sticks[index_to_stick[index]]; + PollJoystick(js); + return js.Details.State; + } return new JoystickState(); } JoystickCapabilities IJoystickDriver2.GetCapabilities(int index) { - return new JoystickCapabilities(); + JoystickCapabilities caps = new JoystickCapabilities(); + if (IsValid(index)) + { + JoystickDevice js = sticks[index_to_stick[index]]; + caps = new JoystickCapabilities( + js.Axis.Count, js.Button.Count, js.Details.State.IsConnected); + } + return caps; } Guid IJoystickDriver2.GetGuid(int index) From 73e3b660380d6f12bf4d800c97681f66b444f142 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Tue, 14 Jan 2014 23:53:24 +0100 Subject: [PATCH 10/14] [OpenTK] Use LegacyJoystickDriver for IJoystickDriver implementation --- Source/OpenTK/GameWindow.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 559c90b1..17bb445d 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -78,6 +78,8 @@ namespace OpenTK const double MaxFrequency = 500.0; // Frequency cap for Update/RenderFrame events readonly Stopwatch watch = new Stopwatch(); + readonly IJoystickDriver LegacyJoystick = + Factory.Default.CreateLegacyJoystickDriver(); IGraphicsContext glContext; @@ -576,9 +578,10 @@ namespace OpenTK /// /// Gets a readonly IList containing all available OpenTK.Input.JoystickDevices. /// + [Obsolete("Use OpenTK.Input.Joystick and GamePad instead")] public IList Joysticks { - get { return InputDriver.Joysticks; } + get { return LegacyJoystick.Joysticks; } } #endregion From df7d7ae8ebff1a04c3d8ab7a6cd27e142906baa4 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 15 Jan 2014 00:57:28 +0100 Subject: [PATCH 11/14] [Linux] Implemented joystick guid detection --- Source/OpenTK/Platform/X11/X11Joystick.cs | 93 ++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index 96e71c1a..103bf9ce 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -1,4 +1,4 @@ -#region License +#region License // // The Open Toolkit Library License // @@ -37,6 +37,7 @@ namespace OpenTK.Platform.X11 { struct X11JoyDetails { + public Guid Guid; public int FileDescriptor; public JoystickState State; } @@ -159,6 +160,72 @@ namespace OpenTK.Platform.X11 #region Private Members + Guid CreateGuid(JoystickDevice js, string path, int number) + { + byte[] bytes = new byte[16]; + for (int i = 0; i < Math.Min(bytes.Length, js.Description.Length); i++) + { + bytes[i] = (byte)js.Description[i]; + } + return new Guid(bytes); + +#if false // Todo: move to /dev/input/event* from /dev/input/js* + string evdev_path = Path.Combine(Path.GetDirectoryName(path), "event" + number); + if (!File.Exists(evdev_path)) + return new Guid(); + + int event_fd = UnsafeNativeMethods.open(evdev_path, OpenFlags.NonBlock); + if (event_fd < 0) + return new Guid(); + + try + { + EventInputId id; + if (UnsafeNativeMethods.ioctl(event_fd, EvdevInputId.Id, out id) < 0) + return new Guid(); + + int i = 0; + byte[] bus = BitConverter.GetBytes(id.BusType); + bytes[i++] = bus[0]; + bytes[i++] = bus[1]; + bytes[i++] = 0; + bytes[i++] = 0; + + if (id.Vendor != 0 && id.Product != 0 && id.Version != 0) + { + byte[] vendor = BitConverter.GetBytes(id.Vendor); + byte[] product = BitConverter.GetBytes(id.Product); + byte[] version = BitConverter.GetBytes(id.Version); + bytes[i++] = vendor[0]; + bytes[i++] = vendor[1]; + bytes[i++] = 0; + bytes[i++] = 0; + bytes[i++] = product[0]; + bytes[i++] = product[1]; + bytes[i++] = 0; + bytes[i++] = 0; + bytes[i++] = version[0]; + bytes[i++] = version[1]; + bytes[i++] = 0; + bytes[i++] = 0; + } + else + { + for (; i < bytes.Length; i++) + { + bytes[i] = (byte)js.Description[i]; + } + } + + return new Guid(bytes); + } + finally + { + UnsafeNativeMethods.close(event_fd); + } +#endif + } + JoystickDevice OpenJoystick(string path) { JoystickDevice stick = null; @@ -195,7 +262,7 @@ namespace OpenTK.Platform.X11 stick.Details.FileDescriptor = fd; stick.Details.State.SetIsConnected(true); - //stick.Details.Guid = + stick.Details.Guid = CreateGuid(stick, path, number); // Find the first disconnected joystick (if any) int i; @@ -290,6 +357,20 @@ namespace OpenTK.Platform.X11 #region UnsafeNativeMethods + struct EvdevInputId + { + public ushort BusType; + public ushort Vendor; + public ushort Product; + public ushort Version; + } + + enum EvdevIoctlCode : uint + { + Id = ((byte)'E' << 8) | (0x02 << 0) //EVIOCGID, which is _IOR('E', 0x02, struct input_id) + } + + struct JoystickEvent { public uint Time; // (u32) event timestamp in milliseconds @@ -331,6 +412,9 @@ namespace OpenTK.Platform.X11 [DllImport("libc", SetLastError = true)] public static extern int ioctl(int d, JoystickIoctlCode request, StringBuilder data); + [DllImport("libc", SetLastError = true)] + public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data); + [DllImport("libc", SetLastError = true)] public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags); @@ -405,6 +489,11 @@ namespace OpenTK.Platform.X11 Guid IJoystickDriver2.GetGuid(int index) { + if (IsValid(index)) + { + JoystickDevice js = sticks[index_to_stick[index]]; + return js.Details.Guid; + } return new Guid(); } From e7f037b0a2b967c25571787f9220491d77ce2f7d Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 15 Jan 2014 01:13:39 +0100 Subject: [PATCH 12/14] [OpenTK] Fixed legacy joystick hotplugging & events --- Source/OpenTK/Platform/X11/X11Joystick.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index 103bf9ce..5aaf60d3 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/X11/X11Joystick.cs @@ -346,6 +346,8 @@ namespace OpenTK.Platform.X11 js.Details.State.SetButton((JoystickButton)e.Number, e.Value != 0); break; } + + js.Details.State.SetPacketNumber(unchecked((int)e.Time)); } } } From e6a9adf4949ef4ab4d58ea40e2a74e3e2a87f6be Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 15 Jan 2014 01:19:45 +0100 Subject: [PATCH 13/14] [OpenTK] Fixed polling --- Source/OpenTK/Platform/LegacyJoystickDriver.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/OpenTK/Platform/LegacyJoystickDriver.cs b/Source/OpenTK/Platform/LegacyJoystickDriver.cs index 1f8e6c3e..4b2f39cc 100644 --- a/Source/OpenTK/Platform/LegacyJoystickDriver.cs +++ b/Source/OpenTK/Platform/LegacyJoystickDriver.cs @@ -65,9 +65,10 @@ namespace OpenTK.Platform if (caps.IsConnected && joysticks[i].Description == DisconnectedName) { // New joystick connected - JoystickDevice device = new LegacyJoystickDevice(i, caps.AxisCount, caps.ButtonCount); + joysticks[i] = new LegacyJoystickDevice(i, caps.AxisCount, caps.ButtonCount); //device.Description = Joystick.GetName(i); - device.Description = ConnectedName; + joysticks[i].Description = ConnectedName; + } else if (!caps.IsConnected && joysticks[i].Description != DisconnectedName) { @@ -77,12 +78,12 @@ namespace OpenTK.Platform } JoystickState state = Joystick.GetState(i); - for (int axis_index = 0; axis_index < (int)JoystickAxis.Last; axis_index++) + for (int axis_index = 0; axis_index < (int)caps.AxisCount; axis_index++) { JoystickAxis axis = JoystickAxis.Axis0 + axis_index; joysticks[i].SetAxis(axis, state.GetAxis(axis)); } - for (int button_index = 0; button_index < (int)JoystickButton.Last; button_index++) + for (int button_index = 0; button_index < (int)caps.ButtonCount; button_index++) { JoystickButton button = JoystickButton.Button0 + button_index; joysticks[i].SetButton(button, state.GetButton(button) == ButtonState.Pressed); @@ -96,6 +97,7 @@ namespace OpenTK.Platform { get { + Poll(); return joysticks_readonly; } } From 44e2576c867e5fe51a17f64c0e97843dea990581 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Wed, 15 Jan 2014 01:24:20 +0100 Subject: [PATCH 14/14] [OpenTK] Protect against invalid axis/button ids --- Source/OpenTK/Input/JoystickDevice.cs | 28 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Source/OpenTK/Input/JoystickDevice.cs b/Source/OpenTK/Input/JoystickDevice.cs index 8e239f56..0467ce2d 100644 --- a/Source/OpenTK/Input/JoystickDevice.cs +++ b/Source/OpenTK/Input/JoystickDevice.cs @@ -130,22 +130,28 @@ namespace OpenTK.Input internal void SetAxis(JoystickAxis axis, float @value) { - move_args.Axis = axis; - move_args.Delta = move_args.Value - @value; - axis_collection[axis] = move_args.Value = @value; - Move(this, move_args); + if ((int)axis < axis_collection.Count) + { + move_args.Axis = axis; + move_args.Delta = move_args.Value - @value; + axis_collection[axis] = move_args.Value = @value; + Move(this, move_args); + } } internal void SetButton(JoystickButton button, bool @value) { - if (button_collection[button] != @value) + if ((int)button < button_collection.Count) { - button_args.Button = button; - button_collection[button] = button_args.Pressed = @value; - if (@value) - ButtonDown(this, button_args); - else - ButtonUp(this, button_args); + if (button_collection[button] != @value) + { + button_args.Button = button; + button_collection[button] = button_args.Pressed = @value; + if (@value) + ButtonDown(this, button_args); + else + ButtonUp(this, button_args); + } } }