diff --git a/Source/Examples/Main.cs b/Source/Examples/Main.cs index 84dba6d7..eb86bd06 100644 --- a/Source/Examples/Main.cs +++ b/Source/Examples/Main.cs @@ -101,6 +101,15 @@ namespace Examples public static void Main(string[] args) { Trace.Listeners.Add(new ConsoleTraceListener()); + Tests.GameWindowStates.Main(); + return; + + using (var gw = new GameWindow()) + { + gw.KeyDown += (sender, e) => gw.Exit(); + gw.Run(60); + } + return; if (args.Length > 0) { diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index d931c244..794abfa5 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -343,9 +343,6 @@ Code - - Code - Code @@ -809,8 +806,13 @@ - + + + Code + + + diff --git a/Source/OpenTK/Platform/Factory.cs b/Source/OpenTK/Platform/Factory.cs index 9a701844..5cc0bfe1 100644 --- a/Source/OpenTK/Platform/Factory.cs +++ b/Source/OpenTK/Platform/Factory.cs @@ -53,6 +53,7 @@ namespace OpenTK.Platform // Create regular platform backend if (Configuration.RunningOnSdl2) Default = new SDL2.Sdl2Factory(); + else if (Configuration.RunningOnLinux) Default = new Linux.LinuxFactory(); else if (Configuration.RunningOnX11) Default = new X11.X11Factory(); else if (Configuration.RunningOnWindows) Default = new Windows.WinFactory(); else if (Configuration.RunningOnMacOS) Default = new MacOS.MacOSFactory(); @@ -70,7 +71,8 @@ namespace OpenTK.Platform } else if (Egl.Egl.IsSupported) { - if (Configuration.RunningOnX11) Embedded = new Egl.EglX11PlatformFactory(); + if (Configuration.RunningOnLinux) Embedded = Default; + else if (Configuration.RunningOnX11) Embedded = new Egl.EglX11PlatformFactory(); else if (Configuration.RunningOnWindows) Embedded = new Egl.EglWinPlatformFactory(); else if (Configuration.RunningOnMacOS) Embedded = new Egl.EglMacPlatformFactory(); else Embedded = new UnsupportedPlatform(); diff --git a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs index 3f4bddb7..7f2bf590 100644 --- a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs +++ b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs @@ -36,20 +36,115 @@ namespace OpenTK.Platform.Linux { const string lib = "libdrm"; + [DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ModeGetCrtc(int fd, uint crtcId); + + [DllImport(lib, EntryPoint = "drmModeFreeConnector", CallingConvention = CallingConvention.Cdecl)] + public static extern void ModeFreeConnector(IntPtr ptr); + + [DllImport(lib, EntryPoint = "drmModeFreeEncoder", CallingConvention = CallingConvention.Cdecl)] + public static extern void ModeFreeEncoder(IntPtr ptr); + + [DllImport(lib, EntryPoint = "drmModeGetConnector", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ModeGetConnector(int fd, uint connector_id); + + [DllImport(lib, EntryPoint = "drmModeGetEncoder", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ModeGetEncoder(int fd, uint encoder_id); + [DllImport(lib, EntryPoint = "drmModeGetResources", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr ModeGetResources(int fd); + public static extern IntPtr ModeGetResources(int fd); + + [DllImport(lib, EntryPoint = "drmModeSetCrtc", CallingConvention = CallingConvention.Cdecl)] + unsafe public static extern int ModeSetCrtc(int fd, uint crtcId, uint bufferId, + uint x, uint y, uint* connectors, int count, ModeInfo* mode); } - struct ModeRes + enum ModeConnection : byte + { + Connected = 1, + Disconnected = 2, + Unknown = 3 + } + + enum ModeSubPixel : byte + { + Unknown = 1, + HorizontalRgb = 2, + HorizontalBgr = 3, + VerticalRgb = 4, + VerticalBgr = 5, + None = 6 + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct ModeConnector + { + public uint connector_id; + public uint encoder_id; + public uint connector_type; + public uint connector_type_id; + public ModeConnection connection; + public uint mmWidth, mmHeight; + public ModeSubPixel subpixel; + + public int count_modes; + public ModeInfo* modes; + + public int count_props; + public uint *props; + public ulong *prop_values; + + public int count_encoders; + public uint *encoders; + } + + struct ModeCrtc + { + public uint crtc_id; + public uint buffer_id; + + public uint x, y; + public uint width, height; + public int mode_valid; + public ModeInfo mode; + + public int gamma_size; + } + + struct ModeEncoder + { + public uint encoder_id; + public uint encoder_type; + public uint crtc_id; + public uint possible_crtcs; + public uint possible_clones; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct ModeInfo + { + public uint clock; + public ushort hdisplay, hsync_start, hsync_end, htotal, hskew; + public ushort vdisplay, vsync_start, vsync_end, vtotal, vscan; + + public uint vrefresh; // refresh rate * 1000 + + public uint flags; + public uint type; + public fixed sbyte name[32]; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct ModeRes { public int count_fbs; - public IntPtr fbs; //uint* + public uint* fbs; public int count_crtcs; - public IntPtr crtcs; //uint* + public uint* crtcs; public int count_connectors; - public IntPtr connectors; //uint* + public uint* connectors; public int count_encoders; - public IntPtr encoders; //uint* + public uint* encoders; public int min_width, max_width; public int min_height, max_height; } diff --git a/Source/OpenTK/Platform/Linux/Bindings/Linux.cs b/Source/OpenTK/Platform/Linux/Bindings/Libc.cs similarity index 53% rename from Source/OpenTK/Platform/Linux/Bindings/Linux.cs rename to Source/OpenTK/Platform/Linux/Bindings/Libc.cs index 2640115d..c571e6d8 100644 --- a/Source/OpenTK/Platform/Linux/Bindings/Linux.cs +++ b/Source/OpenTK/Platform/Linux/Bindings/Libc.cs @@ -29,22 +29,62 @@ using System; using System.Runtime.InteropServices; +using System.Text; namespace OpenTK.Platform.Linux { - class Linux + class Libc { - [DllImport("glib", EntryPoint = "open", CallingConvention = CallingConvention.Cdecl)] - public static extern int Open(string pathname, OpenFlags flags); + const string lib = "libc"; + + [DllImport(lib)] + public static extern int ioctl(int d, JoystickIoctlCode request, ref int data); + + [DllImport(lib)] + public static extern int ioctl(int d, JoystickIoctlCode request, StringBuilder data); + + [DllImport(lib)] + public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data); + + [DllImport(lib)] + public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags); + + [DllImport(lib)] + public static extern int close(int fd); + + [DllImport(lib)] + unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count); } [Flags] enum OpenFlags { - ReadOnly = 0, - WriteOnly = 1, - ReadWrite = 2, - CloseOnExec = 4096 + ReadOnly = 0x0000, + WriteOnly = 0x0001, + ReadWrite = 0x0002, + NonBlock = 0x0800, + CloseOnExec = 0x0080000 + } + + enum EvdevIoctlCode : uint + { + Id = ((byte)'E' << 8) | (0x02 << 0) //EVIOCGID, which is _IOR('E', 0x02, struct input_id) + } + + [Flags] + enum JoystickEventType : byte + { + Button = 0x01, // button pressed/released + Axis = 0x02, // joystick moved + Init = 0x80 // initial state of device + } + + enum JoystickIoctlCode : uint + { + Version = 0x80046a01, + Axes = 0x80016a11, + Buttons = 0x80016a12, + Name128 = (2u << 30) | (0x6A << 8) | (0x13 << 0) | (128 << 16) //JSIOCGNAME(128), which is _IOC(_IO_READ, 'j', 0x13, len) } [StructLayout(LayoutKind.Sequential)] @@ -64,5 +104,21 @@ namespace OpenTK.Platform.Linux public IntPtr mtime; /* time of last modification */ public IntPtr ctime; /* time of last status change */ } + + struct EvdevInputId + { + public ushort BusType; + public ushort Vendor; + public ushort Product; + public ushort Version; + } + + struct JoystickEvent + { + public uint Time; // (u32) event timestamp in milliseconds + public short Value; // (s16) value + public JoystickEventType Type; // (u8) event type + public byte Number; // (u8) axis/button number + } } diff --git a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs new file mode 100644 index 00000000..8feefe42 --- /dev/null +++ b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs @@ -0,0 +1,282 @@ +#region License +// +// LinuxDisplayDriver.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 System.Diagnostics; +using OpenTK; +using OpenTK.Graphics; + +namespace OpenTK.Platform.Linux +{ + class LinuxDisplayDriver : DisplayDeviceBase + { + unsafe class LinuxDisplay + { + public ModeConnector* Connector; + public ModeEncoder* Encoder; + public ModeCrtc* Crtc; + public ModeInfo Mode + { + get + { + if (Crtc == null) + throw new InvalidOperationException(); + + return Crtc->mode; + } + } + public ModeInfo OriginalMode; + + public int Id + { + get + { + if (Crtc == null) + throw new InvalidOperationException(); + + return (int)Crtc->crtc_id; + } + } + + public LinuxDisplay(ModeConnector* c, ModeEncoder* e, ModeCrtc* r) + { + Connector = c; + Encoder = e; + Crtc = r; + OriginalMode = Crtc->mode; // in case we change resolution later on + } + } + + readonly int FD; + readonly GbmDevice Device; + readonly Dictionary DisplayIds = + new Dictionary(); + + public LinuxDisplayDriver(int fd) + { + FD = fd; + QueryDisplays(); + } + + void QueryDisplays() + { + unsafe + { + lock (this) + { + AvailableDevices.Clear(); + DisplayIds.Clear(); + + ModeRes* resources = (ModeRes*)Drm.ModeGetResources(FD); + if (resources == null) + { + throw new NotSupportedException("[KMS] DRM ModeGetResources failed"); + } + Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors); + + // Search for a valid connector + ModeConnector* connector = null; + for (int i = 0; i < resources->count_connectors; i++) + { + connector = (ModeConnector*)Drm.ModeGetConnector(FD, + *(resources->connectors + i)); + if (connector != null) + { + if (connector->connection == ModeConnection.Connected && + connector->count_modes > 0) + { + // Connector found! + AddDisplay(connector); + } + else + { + // This is not the display we are looking for + Drm.ModeFreeConnector((IntPtr)connector); + connector = null; + } + } + } + + if (AvailableDevices.Count == 0) + { + Debug.Print("[KMS] Failed to find any active displays"); + } + } + } + } + + unsafe void AddDisplay(ModeConnector* c) + { + // Find corresponding encoder + ModeEncoder* encoder = null; + for (int i = 0; i < c->count_encoders && encoder == null; i++) + { + ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder( + FD, *(c->encoders + i)); + if (e != null) + { + if (e->encoder_id == c->encoder_id) + { + encoder = e; + } + else + { + Drm.ModeFreeEncoder((IntPtr)e); + } + } + } + + if (encoder != null) + { + Debug.Print("[KMS] Encoder {0} found for connector {1}", + encoder->encoder_id, c->connector_id); + } + else + { + Debug.Print("[KMS] Failed to find encoder for connector {0}", c->connector_id); + return; + } + + ModeCrtc* crtc = (ModeCrtc*)Drm.ModeGetCrtc(FD, encoder->crtc_id); + if (crtc != null) + { + Debug.Print("[KMS] CRTC {0} found for encoder {1}", + encoder->crtc_id, encoder->encoder_id); + } + else + { + Debug.Print("[KMS] Failed to find crtc {0} for encoder {1}", + encoder->crtc_id, encoder->encoder_id); + return; + } + + LinuxDisplay display = new LinuxDisplay(c, encoder, crtc); + if (!DisplayIds.ContainsKey(display.Id)) + { + DisplayIds.Add(display.Id, AvailableDevices.Count); + } + + List modes = new List(); + for (int i = 0; i < display.Connector->count_modes; i++) + { + ModeInfo* mode = display.Connector->modes + i; + DisplayResolution res = GetDisplayResolution(mode); + modes.Add(res); + } + ModeInfo current_mode = display.Mode; + DisplayResolution current = GetDisplayResolution(¤t_mode); + + // Note: since we are not running a display manager, we are free + // to choose the display layout for multiple displays ourselves. + // We choose the simplest layout: displays are laid out side-by-side + // from left to right. Primary display is the first display we encounter. + System.Drawing.Rectangle bounds = + new System.Drawing.Rectangle( + AvailableDevices.Count == 0 ? 0 : AvailableDevices[0].Bounds.Right, + 0, + current.Width, + current.Height); + + DisplayDevice device = new DisplayDevice( + current, + AvailableDevices.Count == 0, + modes, + bounds, + display); + + if (AvailableDevices.Count == 0) + { + Primary = device; + } + + AvailableDevices.Add(device); + } + + unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode) + { + if (mode == null) + throw new ArgumentNullException(); + + return new DisplayResolution( + 0, 0, + mode->htotal, mode->vtotal, + 32, // This is actually part of the framebuffer, not the DisplayResolution + mode->vrefresh / 1000.0f); + } + + unsafe static ModeInfo* GetModeInfo(LinuxDisplay display, DisplayResolution resolution) + { + for (int i = 0; i < display.Connector->count_modes; i++) + { + ModeInfo* mode = display.Connector->modes + i; + if (mode != null && + mode->htotal == resolution.Width && + mode->vtotal == resolution.Height) + { + return mode; + } + } + return null; + } + + #region IDisplayDeviceDriver + + public override bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution) + { + unsafe + { + LinuxDisplay display = (LinuxDisplay)device.Id; + ModeInfo* mode = GetModeInfo(display, resolution); + uint connector_id = display.Connector->connector_id; + if (mode != null) + { + return Drm.ModeSetCrtc(FD, (uint)display.Id, 0, 0, 0, + &connector_id, 1, mode) == 0; + } + return false; + } + } + + public override bool TryRestoreResolution(DisplayDevice device) + { + unsafe + { + LinuxDisplay display = (LinuxDisplay)device.Id; + uint connector_id = display.Connector->connector_id; + ModeInfo mode = display.OriginalMode; + return Drm.ModeSetCrtc(FD, (uint)display.Id, 0, 0, 0, + &connector_id, 1, &mode) == 0; + } + } + + #endregion + } +} + diff --git a/Source/OpenTK/Platform/Linux/LinuxFactory.cs b/Source/OpenTK/Platform/Linux/LinuxFactory.cs index f998a071..f8b1096d 100644 --- a/Source/OpenTK/Platform/Linux/LinuxFactory.cs +++ b/Source/OpenTK/Platform/Linux/LinuxFactory.cs @@ -32,70 +32,93 @@ using System.Diagnostics; using System.Runtime.InteropServices; using OpenTK.Graphics; using OpenTK.Input; +using OpenTK.Platform.Egl; namespace OpenTK.Platform.Linux { + using Egl = OpenTK.Platform.Egl.Egl; + // Linux KMS platform class LinuxFactory : PlatformFactoryBase { int fd; - GbmDevice device; + GbmDevice gbm_device; IntPtr display; + IJoystickDriver2 JoystickDriver; + IDisplayDeviceDriver DisplayDriver; + public LinuxFactory() + { + SetupEgl(); + } + + #region Private Members + + void SetupEgl() { // Todo: support multi-GPU systems string gpu = "/dev/dri/card0"; - fd = Linux.Open(gpu, OpenFlags.ReadOnly | OpenFlags.CloseOnExec); + fd = Libc.open(gpu, OpenFlags.ReadWrite | OpenFlags.CloseOnExec); if (fd < 0) { - throw new NotSupportedException("No KMS-capable GPU available"); + throw new NotSupportedException("[KMS] No KMS-capable GPU available"); } - Debug.Print("GPU {0} opened as fd:{1}", gpu, fd); + Debug.Print("[KMS] GPU '{0}' opened as fd:{1}", gpu, fd); IntPtr dev = Gbm.CreateDevice(fd); if (dev == IntPtr.Zero) { - throw new NotSupportedException("Failed to create GBM device"); + throw new NotSupportedException("[KMS] Failed to create GBM device"); } - device = (GbmDevice)Marshal.PtrToStructure(dev, typeof(GbmDevice)); - Debug.Print("GBM {0:x} '{1}' created successfully", - dev, Marshal.PtrToStringAnsi(device.name)); + gbm_device = (GbmDevice)Marshal.PtrToStructure(dev, typeof(GbmDevice)); + Debug.Print("[KMS] GBM {0:x} created successfully", dev); - display = Egl.Egl.GetDisplay(dev); + display = Egl.GetDisplay(dev); if (display == IntPtr.Zero) { - throw new NotSupportedException("Failed to create EGL display"); + throw new NotSupportedException("[KMS] Failed to create EGL display"); } - Debug.Print("EGL display {0:x} created successfully", display); + Debug.Print("[KMS] EGL display {0:x} created successfully", display); int major, minor; - if (!Egl.Egl.Initialize(display, out major, out minor)) + if (!Egl.Initialize(display, out major, out minor)) { - int error = Egl.Egl.GetError(); - throw new NotSupportedException("Failed to initialize EGL display. Error code: " + error); + int error = Egl.GetError(); + throw new NotSupportedException("[KMS] Failed to initialize EGL display. Error code: " + error); } - Debug.Print("EGL {0}.{1} initialized successfully on display {2:x}", major, minor, display); + Debug.Print("[KMS] EGL {0}.{1} initialized successfully on display {2:x}", major, minor, display); } - public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) + #endregion + + #region IPlatformFactory Members + + public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice display_device) { - return new LinuxNativeWindow(x, y, width, height, title, mode, options, device); + return new LinuxNativeWindow(display, gbm_device, width, height, title, mode, options, display_device); } public override IDisplayDeviceDriver CreateDisplayDeviceDriver() { - throw new NotImplementedException(); + lock (this) + { + DisplayDriver = DisplayDriver ?? new LinuxDisplayDriver(fd); + return DisplayDriver; + } } public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) { - throw new NotImplementedException(); + return new EglUnixContext(mode, (EglWindowInfo)window, shareContext, major, minor, flags); } public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() { - throw new NotImplementedException(); + return (GraphicsContext.GetCurrentContextDelegate)delegate + { + return new ContextHandle(Egl.GetCurrentContext()); + }; } public override IKeyboardDriver2 CreateKeyboardDriver() @@ -110,8 +133,14 @@ namespace OpenTK.Platform.Linux public override IJoystickDriver2 CreateJoystickDriver() { - throw new NotImplementedException(); + lock (this) + { + JoystickDriver = JoystickDriver ?? new LinuxJoystick(); + return JoystickDriver; + } } + + #endregion } } diff --git a/Source/OpenTK/Platform/X11/X11Joystick.cs b/Source/OpenTK/Platform/Linux/LinuxJoystick.cs similarity index 75% rename from Source/OpenTK/Platform/X11/X11Joystick.cs rename to Source/OpenTK/Platform/Linux/LinuxJoystick.cs index 4b3fdc8c..51992797 100644 --- a/Source/OpenTK/Platform/X11/X11Joystick.cs +++ b/Source/OpenTK/Platform/Linux/LinuxJoystick.cs @@ -33,9 +33,9 @@ using System.Runtime.InteropServices; using System.Text; using OpenTK.Input; -namespace OpenTK.Platform.X11 +namespace OpenTK.Platform.Linux { - struct X11JoyDetails + struct LinuxJoyDetails { public Guid Guid; public int FileDescriptor; @@ -43,7 +43,7 @@ namespace OpenTK.Platform.X11 } // Note: despite what the name says, this class is Linux-specific. - sealed class X11Joystick : IJoystickDriver2 + sealed class LinuxJoystick : IJoystickDriver2 { #region Fields @@ -52,7 +52,7 @@ namespace OpenTK.Platform.X11 readonly FileSystemWatcher watcher = new FileSystemWatcher(); readonly Dictionary index_to_stick = new Dictionary(); - List> sticks = new List>(); + List> sticks = new List>(); bool disposed; @@ -60,7 +60,7 @@ namespace OpenTK.Platform.X11 #region Constructors - public X11Joystick() + public LinuxJoystick() { string path = Directory.Exists(JoystickPath) ? JoystickPath : @@ -89,7 +89,7 @@ namespace OpenTK.Platform.X11 { foreach (string file in Directory.GetFiles(path)) { - JoystickDevice stick = OpenJoystick(file); + JoystickDevice stick = OpenJoystick(file); if (stick != null) { //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", @@ -155,7 +155,7 @@ namespace OpenTK.Platform.X11 #region Private Members - Guid CreateGuid(JoystickDevice js, string path, int number) + 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++) @@ -221,9 +221,9 @@ namespace OpenTK.Platform.X11 #endif } - JoystickDevice OpenJoystick(string path) + JoystickDevice OpenJoystick(string path) { - JoystickDevice stick = null; + JoystickDevice stick = null; int number = GetJoystickNumber(Path.GetFileName(path)); if (number >= 0) @@ -231,28 +231,28 @@ namespace OpenTK.Platform.X11 int fd = -1; try { - fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock); + fd = Libc.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); + Libc.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); + Libc.ioctl(fd, JoystickIoctlCode.Axes, ref axes); // Get number of joystick buttons int buttons = 0; - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons); + Libc.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons); - stick = new JoystickDevice(number, axes, buttons); + stick = new JoystickDevice(number, axes, buttons); StringBuilder sb = new StringBuilder(128); - UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb); + Libc.ioctl(fd, JoystickIoctlCode.Name128, sb); stick.Description = sb.ToString(); stick.Details.FileDescriptor = fd; @@ -287,16 +287,16 @@ namespace OpenTK.Platform.X11 finally { if (stick == null && fd != -1) - UnsafeNativeMethods.close(fd); + Libc.close(fd); } } return stick; } - void CloseJoystick(JoystickDevice js) + void CloseJoystick(JoystickDevice js) { - UnsafeNativeMethods.close(js.Details.FileDescriptor); + Libc.close(js.Details.FileDescriptor); js.Details.State = new JoystickState(); // clear joystick state js.Details.FileDescriptor = -1; @@ -317,13 +317,13 @@ namespace OpenTK.Platform.X11 } } - void PollJoystick(JoystickDevice js) + void PollJoystick(JoystickDevice js) { JoystickEvent e; unsafe { - while ((long)UnsafeNativeMethods.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0) + while ((long)Libc.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0) { e.Type &= ~JoystickEventType.Init; @@ -352,78 +352,9 @@ namespace OpenTK.Platform.X11 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 - public short Value; // (s16) value - public JoystickEventType Type; // (u8) event type - public byte Number; // (u8) axis/button number - } - - [Flags] - enum JoystickEventType : byte - { - Button = 0x01, // button pressed/released - Axis = 0x02, // joystick moved - Init = 0x80 // initial state of device - } - - enum JoystickIoctlCode : uint - { - Version = 0x80046a01, - Axes = 0x80016a11, - Buttons = 0x80016a12, - 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"; static readonly string JoystickPathLegacy = "/dev"; - [Flags] - enum OpenFlags - { - NonBlock = 0x00000800 - } - - static class UnsafeNativeMethods - { - [DllImport("libc", SetLastError = true)] - public static extern int ioctl(int d, JoystickIoctlCode request, ref int data); - - [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); - - [DllImport("libc", SetLastError = true)] - public static extern int close(int fd); - - [DllImport("libc", SetLastError = true)] - unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count); - } - - #endregion - #endregion #region IDisposable Members @@ -443,7 +374,7 @@ namespace OpenTK.Platform.X11 } watcher.Dispose(); - foreach (JoystickDevice js in sticks) + foreach (JoystickDevice js in sticks) { CloseJoystick(js); } @@ -452,7 +383,7 @@ namespace OpenTK.Platform.X11 } } - ~X11Joystick() + ~LinuxJoystick() { Dispose(false); } @@ -465,7 +396,7 @@ namespace OpenTK.Platform.X11 { if (IsValid(index)) { - JoystickDevice js = + JoystickDevice js = sticks[index_to_stick[index]]; PollJoystick(js); return js.Details.State; @@ -478,7 +409,7 @@ namespace OpenTK.Platform.X11 JoystickCapabilities caps = new JoystickCapabilities(); if (IsValid(index)) { - JoystickDevice js = sticks[index_to_stick[index]]; + JoystickDevice js = sticks[index_to_stick[index]]; caps = new JoystickCapabilities( js.Axis.Count, js.Button.Count, @@ -492,7 +423,7 @@ namespace OpenTK.Platform.X11 { if (IsValid(index)) { - JoystickDevice js = sticks[index_to_stick[index]]; + JoystickDevice js = sticks[index_to_stick[index]]; return js.Details.Guid; } return new Guid(); diff --git a/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs b/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs index a44cb9bb..6d926923 100644 --- a/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs +++ b/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs @@ -28,42 +28,74 @@ #endregion using System; +using System.Drawing; +using OpenTK.Graphics; +using OpenTK.Platform.Egl; namespace OpenTK.Platform.Linux { + using Egl = OpenTK.Platform.Egl.Egl; + class LinuxNativeWindow : NativeWindowBase { + LinuxWindowInfo window_info; + string title; + Icon icon; + bool exists; + Rectangle bounds; + Size client_size; + + public LinuxNativeWindow(IntPtr display, GbmDevice device, + int width, int height, string title, GraphicsMode mode, GameWindowFlags options, + DisplayDevice display_device) + { + Title = title; + bounds = new Rectangle(0, 0, width, height); + client_size = bounds.Size; + + //window_info = new LinuxWindowInfo( + // Egl.CreateWindowSurface( + + exists = true; + } + #region INativeWindow Members public override void Close() { - throw new NotImplementedException(); + exists = false; } - public override System.Drawing.Point PointToClient(System.Drawing.Point point) + public override Point PointToClient(Point point) { - throw new NotImplementedException(); + // Todo + return point; } - public override System.Drawing.Point PointToScreen(System.Drawing.Point point) + public override Point PointToScreen(Point point) { - throw new NotImplementedException(); + // Todo + return point; } protected override void Dispose(bool disposing) { - throw new NotImplementedException(); + // Todo } - public override System.Drawing.Icon Icon + public override Icon Icon { get { - throw new NotImplementedException(); + return icon; } set { - throw new NotImplementedException(); + if (icon != value) + { + icon = value; + OnIconChanged(EventArgs.Empty); + } } } @@ -71,11 +103,15 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return title; } set { - throw new NotImplementedException(); + if (title != value) + { + title = value; + OnTitleChanged(EventArgs.Empty); + } } } @@ -83,7 +119,7 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return true; } } @@ -91,11 +127,10 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return true; } set { - throw new NotImplementedException(); } } @@ -103,7 +138,7 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return exists; } } @@ -111,7 +146,7 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return window_info; } } @@ -119,11 +154,10 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return WindowState.Fullscreen; } set { - throw new NotImplementedException(); } } @@ -131,35 +165,32 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return WindowBorder.Hidden; } set { - throw new NotImplementedException(); } } - public override System.Drawing.Rectangle Bounds + public override Rectangle Bounds { get { - throw new NotImplementedException(); + return bounds; } set { - throw new NotImplementedException(); } } - public override System.Drawing.Size ClientSize + public override Size ClientSize { get { - throw new NotImplementedException(); + return client_size; } set { - throw new NotImplementedException(); } } @@ -167,11 +198,10 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return false; } set { - throw new NotImplementedException(); } } @@ -179,18 +209,14 @@ namespace OpenTK.Platform.Linux { get { - throw new NotImplementedException(); + return MouseCursor.Empty; } set { - throw new NotImplementedException(); } } #endregion - - - } } diff --git a/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs b/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs new file mode 100644 index 00000000..df564285 --- /dev/null +++ b/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs @@ -0,0 +1,43 @@ +#region License +// +// LinuxWindowInfo.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 OpenTK.Platform.Egl; + +namespace OpenTK.Platform.Linux +{ + class LinuxWindowInfo : EglWindowInfo + { + public LinuxWindowInfo(IntPtr handle, IntPtr display, IntPtr surface) + : base(handle, display, surface) + { + } + } +} + diff --git a/Source/OpenTK/Platform/X11/X11Input.cs b/Source/OpenTK/Platform/X11/X11Input.cs index 50bdf4a3..246501c7 100644 --- a/Source/OpenTK/Platform/X11/X11Input.cs +++ b/Source/OpenTK/Platform/X11/X11Input.cs @@ -37,7 +37,7 @@ namespace OpenTK.Platform.X11 { readonly X11Mouse mouse = new X11Mouse(); readonly X11Keyboard keyboard = new X11Keyboard(); - readonly X11Joystick joystick = new X11Joystick(); + readonly Linux.LinuxJoystick joystick = new Linux.LinuxJoystick(); readonly IGamePadDriver gamepad = new MappedGamePadDriver(); internal X11Input() diff --git a/Source/OpenTK/Platform/X11/XI2Input.cs b/Source/OpenTK/Platform/X11/XI2Input.cs index c2e8528e..d91d2c07 100644 --- a/Source/OpenTK/Platform/X11/XI2Input.cs +++ b/Source/OpenTK/Platform/X11/XI2Input.cs @@ -36,7 +36,7 @@ namespace OpenTK.Platform.X11 class XI2Input : IInputDriver2 { readonly XI2MouseKeyboard mouse_keyboard = new XI2MouseKeyboard(); - readonly X11Joystick joystick = new X11Joystick(); + readonly Linux.LinuxJoystick joystick = new Linux.LinuxJoystick(); readonly IGamePadDriver gamepad = new MappedGamePadDriver(); internal XI2Input()