diff --git a/README.md b/README.md index 954425db..91325777 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,78 @@ OpenTK ====== - + The Open Toolkit is an advanced, low-level C# library that wraps OpenGL, OpenGL ES and OpenAL. It is suitable for games, scientific applications and any other project that requires 3d graphics, audio or compute functionality. - -This is the official GIT repository of the project: - -https://github.com/opentk/opentk - - + +Project website: http://www.opentk.com/ + +Official git repository: https://github.com/opentk/opentk + + Features ======== - -- Create cutting-edge graphics with OpenGL 4.4 and OpenGL ES 3.0. - -- Integrate 3d into Windows.Forms, WPF or GTK# applications. - -- Write once run everywhere: support for Windows, Mac OS X, Linux, Xamarin.Android and Xamarin.iOS. - -- Use strong types and inline documentation to improve your code flow and catch errors sooner. - - + +- Create cutting-edge graphics with OpenGL 4.4 and OpenGL ES 3.0 +- Spice up your GUI with 3d graphics +- Improve your code flow with strong types and inline documentation +- Write once run everywhere! + +OpenTK is available on Windows, Linux, Mac OS X, *BSD, SteamOS, Android and iOS. It can be used standalone or integrated into a GUI (Windows.Forms, WPF, GTK+, Qt, VTK, ...) + +Binaries and NuGet packages available at http://www.opentk.com + + +Roadmap +======= + +- Merge opentk/opentk (upstream) with mono/opentk (Xamarin) +- Add Portable Class Library (PCL) target +- Add multitouch API +- Add new platforms: NaCL, emscripten, Windows Metro, Ouya, Raspberry PI +- Improve OpenCL bindings +- Improve math library + + +Contributing +============ + +1. Install git and a C# IDE (see requirements section below) +2. Fork the _develop_ branch of https://github.com/opentk/opentk +3. Commit your changes in small, incremental steps with clear descriptions +4. When ready, issue a Pull Request (PR) against the _develop_ branch of https://github.com/opentk/opentk + +For details on coding style and best practices, refer to https://github.com/opentk/opentk/wiki/Contributing + + Requirements ============ - -OpenTK is designed to be used in an IDE with auto-completion and documentation tooltips: -- Visual Studio 2005 or higher -- Xamarin Studio 2.x or higher -- MonoDevelop 2.x or higher -- SharpDevelop 3.x or higher - -You can develop on your favorite operating system. Compiled binaries can be deployed without recompilation on: -- Windows -- Linux -- Mac OS X - -For: -- Android -- iOS -you will need to recompile your code with Xamarin (http://xamarin.com/download) - - + +- Windows (XP/Vista/7/8), Linux, Mac OS X, *BSD, SteamOS, Android or iOS +- For graphics, OpenGL drivers or a suitable emulator, such as [ANGLE](https://github.com/opentk/opentk/tree/Dependencies/Readme.txt) +- For audio, OpenAL drivers or [OpenAL Soft](https://github.com/opentk/opentk/tree/Dependencies/Readme.txt) +- To develop desktop applications: Visual Studio, Xamarin Studio, MonoDevelop or SharpDevelop +- To develop Android applications: Xamarin Studio or the Xamarin Extensions for Visual Studio +- To develop iOS applications: Xamarin Studio and XCode + + Documentation ============= - -The Documentation/ folder contains extensive documentation on OpenGL, OpenGL ES and OpenAL. Start with these: - -- OpenGL 4.4 API Reference.pdf -or -- OpenGL ES 3.0 API Reference.pdf - -Your favorite IDE will display inline documentation for all OpenTK APIs. Tutorials can be found in the [OpenTK Manual](http://www.opentk.com/doc) - - + +Your favorite IDE will display inline documentation for all OpenTK APIs. Additional information can be found in the [OpenTK Manual](http://www.opentk.com/doc) and in the [opentk/Documentation/](https://github.com/opentk/opentk/tree/develop/Documentation) folder. + +Technical documentation about the implementation of OpenTK can be found in the [Technical Wiki](https://github.com/opentk/opentk/wiki). + + Need Help? ========== - -The community hangs out at the [OpenTK forums](http://www.opentk.com/forum) - -If you hit a bug, post an issue on https://github.com/opentk/opentk/issues - - + +Post your questions at the [OpenTK forums](http://www.opentk.com/forum). + +Report bugs at https://github.com/opentk/opentk/issues + + License ======= - -The Open Toolkit is distributed under the permissive MIT/X11 license and is absolutely free. \ No newline at end of file + +The Open Toolkit is distributed under the permissive MIT/X11 license and is absolutely free. + +http://www.opentk.com/project/license 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 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; } /// 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); + } } } diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index d9b969c9..8c3931fc 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -166,6 +166,7 @@ + Code @@ -798,6 +799,8 @@ + + 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/LegacyJoystickDriver.cs b/Source/OpenTK/Platform/LegacyJoystickDriver.cs new file mode 100644 index 00000000..4b2f39cc --- /dev/null +++ b/Source/OpenTK/Platform/LegacyJoystickDriver.cs @@ -0,0 +1,108 @@ +#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 + joysticks[i] = new LegacyJoystickDevice(i, caps.AxisCount, caps.ButtonCount); + //device.Description = Joystick.GetName(i); + joysticks[i].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)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)caps.ButtonCount; button_index++) + { + JoystickButton button = JoystickButton.Button0 + button_index; + joysticks[i].SetButton(button, state.GetButton(button) == ButtonState.Pressed); + } + } + } + + #region IJoystickDriver Members + + public IList Joysticks + { + get + { + Poll(); + return joysticks_readonly; + } + } + + #endregion + } +} + 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/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 + } +} + 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/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 +} 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 } } 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() diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/X11/X11Joystick.cs index cbab7099..5aaf60d3 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 // @@ -28,20 +28,31 @@ 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 Guid Guid; + public int FileDescriptor; + public JoystickState State; + } - 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,74 +62,96 @@ namespace OpenTK.Platform.X11 public X11Joystick() { - sticks_readonly = sticks.AsReadOnly(); + watcher.Created += JoystickAdded; + watcher.Deleted += JoystickRemoved; + watcher.EnableRaisingEvents = true; - int number = 0, max_sticks = 25; - while (number < max_sticks) - { - 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); - sticks.Add(stick); - } - } + watcher_legacy.Created += JoystickAdded; + watcher_legacy.Deleted += JoystickRemoved; + watcher_legacy.EnableRaisingEvents = true; - number = 0; - while (number < max_sticks) - { - 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); - sticks.Add(stick); - } - } + OpenJoysticks(); } #endregion - #region IJoystickDriver + #region Private Members - public int DeviceCount + void OpenJoysticks() { - get { return sticks.Count; } - } - - public IList Joysticks - { - get { Poll(); return sticks_readonly; } - } - - public void Poll() - { - JoystickEvent e; - - foreach (JoystickDevice js in sticks) + lock (sync) { - unsafe + foreach (string file in Directory.GetFiles(JoystickPath)) { - while ((long)UnsafeNativeMethods.read(js.Id, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0) + JoystickDevice stick = OpenJoystick(file); + if (stick != null) { - e.Type &= ~JoystickEventType.Init; + //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", + //number, stick.Axis.Count, stick.Button.Count, JoystickPath); + sticks.Add(stick); + } + } - switch (e.Type) + foreach (string file in Directory.GetFiles(JoystickPathLegacy)) + { + 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); + } + } + } + } + + 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) + { + OpenJoystick(e.FullPath); + } + } + + 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) { - 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; + break; } } + + if (i == sticks.Count) + { + Debug.Print("[Evdev] Joystick id {0} does not exist.", number); + } + else + { + CloseJoystick(sticks[i]); + } } } } @@ -127,51 +160,219 @@ namespace OpenTK.Platform.X11 #region Private Members - JoystickDevice OpenJoystick(string base_path, int number) + Guid CreateGuid(JoystickDevice js, string path, int number) { - string path = base_path + number.ToString(); - JoystickDevice stick = null; + 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(); - int fd = -1; try { - fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock); - if (fd == -1) - return null; + EventInputId id; + if (UnsafeNativeMethods.ioctl(event_fd, EvdevInputId.Id, out id) < 0) + return new Guid(); - // 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; + int i = 0; + byte[] bus = BitConverter.GetBytes(id.BusType); + bytes[i++] = bus[0]; + bytes[i++] = bus[1]; + bytes[i++] = 0; + bytes[i++] = 0; - // Get number of joystick axes - int axes = 0; - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes); + 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]; + } + } - // 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(); - - Debug.Print("Found joystick on path {0}", path); + return new Guid(bytes); } finally { - if (stick == null && fd != -1) - UnsafeNativeMethods.close(fd); + UnsafeNativeMethods.close(event_fd); + } +#endif + } + + 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 = CreateGuid(stick, path, number); + + // 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; + + unsafe + { + while ((long)UnsafeNativeMethods.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0) + { + e.Type &= ~JoystickEventType.Init; + + switch (e.Type) + { + 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; + + case JoystickEventType.Button: + js.Details.State.SetButton((JoystickButton)e.Number, e.Value != 0); + break; + } + + js.Details.State.SetPacketNumber(unchecked((int)e.Time)); + } + } + } + + bool IsValid(int index) + { + return index_to_stick.ContainsKey(index); + } + #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 @@ -196,8 +397,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 @@ -213,6 +414,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); @@ -243,9 +447,9 @@ namespace OpenTK.Platform.X11 { } - foreach (JoystickDevice js in sticks) + foreach (JoystickDevice js in sticks) { - UnsafeNativeMethods.close(js.Id); + CloseJoystick(js); } disposed = true; @@ -259,44 +463,39 @@ 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) { + 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) { + if (IsValid(index)) + { + JoystickDevice js = sticks[index_to_stick[index]]; + return js.Details.Guid; + } return new Guid(); }