diff --git a/Source/Examples/OpenTK/Test/TestResolutionChanges.cs b/Source/Examples/OpenTK/Test/TestResolutionChanges.cs
index 314a34a3..740f60f4 100644
--- a/Source/Examples/OpenTK/Test/TestResolutionChanges.cs
+++ b/Source/Examples/OpenTK/Test/TestResolutionChanges.cs
@@ -23,16 +23,35 @@ namespace Examples.Tests
DisplayDevice dev = DisplayDevice.GetDisplay(DisplayIndex.First + i);
if (dev != null)
{
- Trace.WriteLine(dev.ToString());
- MessageBox.Show(dev.ToString());
+ Print(dev.ToString());
+
dev.ChangeResolution(dev.SelectResolution(640, 480, 32, 60.0f));
Thread.Sleep(1000);
- MessageBox.Show(dev.ToString());
+ Print(dev.ToString());
+
dev.RestoreResolution();
Thread.Sleep(1000);
- MessageBox.Show(dev.ToString());
+ Print(dev.ToString());
}
}
}
+
+ static void Print(string msg)
+ {
+ Trace.WriteLine(msg);
+
+ // Also display a MessageBox when running on a platform
+ // with WinForms support.
+ try
+ {
+ if (Configuration.RunningOnWindows || Configuration.RunningOnX11 || Configuration.RunningOnMacOS)
+ {
+ MessageBox.Show(msg);
+ }
+ }
+ catch
+ {
+ }
+ }
}
}
diff --git a/Source/OpenTK/Configuration.cs b/Source/OpenTK/Configuration.cs
index 2e04c773..1b29eaef 100644
--- a/Source/OpenTK/Configuration.cs
+++ b/Source/OpenTK/Configuration.cs
@@ -95,7 +95,7 @@ namespace OpenTK
#region public static bool RunningOnLinux
- /// Gets a System.Boolean indicating whether OpenTK is running on an X11 platform.
+ /// Gets a System.Boolean indicating whether OpenTK is running on the Linux kernel.
public static bool RunningOnLinux { get { return runningOnLinux; } }
#endregion
diff --git a/Source/OpenTK/DisplayDevice.cs b/Source/OpenTK/DisplayDevice.cs
index 4e8c7a3d..00789473 100644
--- a/Source/OpenTK/DisplayDevice.cs
+++ b/Source/OpenTK/DisplayDevice.cs
@@ -351,6 +351,26 @@ namespace OpenTK
#endregion
+ #region FromPoint
+
+ internal static DisplayDevice FromPoint(int x, int y)
+ {
+ for (DisplayIndex i = DisplayIndex.First; i < DisplayIndex.Sixth; i++)
+ {
+ DisplayDevice display = DisplayDevice.GetDisplay(i);
+ if (display != null)
+ {
+ if (display.Bounds.Contains(x, y))
+ {
+ return display;
+ }
+ }
+ }
+ return null;
+ }
+
+ #endregion
+
#endregion
#region --- Private Methods ---
diff --git a/Source/OpenTK/Graphics/GraphicsBindingsBase.cs b/Source/OpenTK/Graphics/GraphicsBindingsBase.cs
index 237b8783..79cd42b6 100644
--- a/Source/OpenTK/Graphics/GraphicsBindingsBase.cs
+++ b/Source/OpenTK/Graphics/GraphicsBindingsBase.cs
@@ -26,6 +26,7 @@
#endregion
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
namespace OpenTK.Graphics
@@ -84,6 +85,8 @@ namespace OpenTK.Graphics
// validation necessary.)
internal override void LoadEntryPoints()
{
+ Debug.Print("Loading entry points for {0}", GetType().FullName);
+
IGraphicsContext context = GraphicsContext.CurrentContext;
if (context == null)
throw new GraphicsContextMissingException();
diff --git a/Source/OpenTK/Input/MouseState.cs b/Source/OpenTK/Input/MouseState.cs
index 3a74a0f3..9c316e01 100644
--- a/Source/OpenTK/Input/MouseState.cs
+++ b/Source/OpenTK/Input/MouseState.cs
@@ -38,7 +38,8 @@ namespace OpenTK.Input
{
#region Fields
- int x, y;
+ internal const int MaxButtons = 16; // we are storing in an ushort
+ Vector2 position;
MouseScroll scroll;
ushort buttons;
bool is_connected;
@@ -101,7 +102,7 @@ namespace OpenTK.Input
}
///
- /// Gets a instance,
+ /// Gets a instance,
/// representing the current state of the mouse scroll wheel.
///
public MouseScroll Scroll
@@ -114,8 +115,8 @@ namespace OpenTK.Input
///
public int X
{
- get { return x; }
- internal set { x = value; }
+ get { return (int)Math.Round(position.X); }
+ internal set { position.X = value; }
}
///
@@ -123,8 +124,8 @@ namespace OpenTK.Input
///
public int Y
{
- get { return y; }
- internal set { y = value; }
+ get { return (int)Math.Round(position.Y); }
+ internal set { position.Y = value; }
}
///
@@ -272,6 +273,12 @@ namespace OpenTK.Input
#region Internal Members
+ internal Vector2 Position
+ {
+ get { return position; }
+ set { position = value; }
+ }
+
internal bool ReadBit(int offset)
{
ValidateOffset(offset);
diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index 718d8540..61e11bc4 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -343,9 +343,6 @@
Code
-
- Code
-
Code
@@ -806,6 +803,27 @@
+
+
+
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -836,4 +854,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/Source/OpenTK/Platform/DeviceCollection.cs b/Source/OpenTK/Platform/DeviceCollection.cs
new file mode 100644
index 00000000..0c70f356
--- /dev/null
+++ b/Source/OpenTK/Platform/DeviceCollection.cs
@@ -0,0 +1,151 @@
+#region License
+//
+// DeviceCollection.cs
+//
+// Author:
+// Stefanos A.
+//
+// Copyright (c) 2006-2014
+//
+// 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;
+
+namespace OpenTK.Platform
+{
+ // Holds a collection of hardware devices with an associated id.
+ // Note: 'id' refers to a unique hardware-specific device identifier.
+ // Note: 'index' refers to the offset of the device in the Devices array.
+ // Indices are allocated sequentially as devices are added to the system.
+ // If a device is removed, its index will be reused for the next device
+ // that is added.
+ class DeviceCollection : IEnumerable
+ {
+ readonly Dictionary Map = new Dictionary();
+ readonly List Devices = new List();
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ return Devices.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable implementation
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ #region Public Members
+
+ public void Add(int id, T device)
+ {
+ if (!Map.ContainsKey(id))
+ {
+ int index = GetIndex();
+ Map.Add(id, index);
+ }
+
+ Devices[Map[id]] = device;
+ }
+
+ public void Remove(int id)
+ {
+ if (!TryRemove(id))
+ {
+ Debug.Print("Invalid DeviceCollection<{0}> id: {1}", typeof(T).FullName, id);
+ }
+ }
+
+ public bool TryRemove(int id)
+ {
+ if (!Map.ContainsKey(id))
+ {
+ return false;
+ }
+
+ Devices[Map[id]] = default(T);
+ Map.Remove(id);
+ return true;
+ }
+
+ public T FromIndex(int index)
+ {
+ if (index >= 0 && index < Devices.Count)
+ {
+ return Devices[index];
+ }
+ else
+ {
+ return default(T);
+ }
+ }
+
+ public T FromHardwareId(int id)
+ {
+ if (Map.ContainsKey(id))
+ {
+ return FromIndex(Map[id]);
+ }
+ else
+ {
+ return default(T);
+ }
+ }
+
+ public int Count
+ {
+ get { return Map.Count; }
+ }
+
+ #endregion
+
+ #region Private Members
+
+ // Return the index of the first empty slot in Devices.
+ // If no empty slot exists, append a new one and return
+ // that index.
+ int GetIndex()
+ {
+ for (int i = 0; i < Devices.Count; i++)
+ {
+ if (Devices[i] == null)
+ {
+ return i;
+ }
+ }
+
+ Devices.Add(default(T));
+ return Devices.Count - 1;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Egl/Egl.cs b/Source/OpenTK/Platform/Egl/Egl.cs
index f271f5db..9d539da0 100644
--- a/Source/OpenTK/Platform/Egl/Egl.cs
+++ b/Source/OpenTK/Platform/Egl/Egl.cs
@@ -41,6 +41,13 @@ namespace OpenTK.Platform.Egl
using EGLSurface = IntPtr;
using EGLClientBuffer = IntPtr;
+ enum RenderApi
+ {
+ ES = Egl.OPENGL_ES_API,
+ GL = Egl.OPENGL_API,
+ VG = Egl.OPENVG_API
+ }
+
[Flags]
enum RenderableFlags
{
@@ -50,6 +57,24 @@ namespace OpenTK.Platform.Egl
VG = Egl.OPENVG_BIT,
}
+ enum ErrorCode
+ {
+ SUCCESS = 12288,
+ NOT_INITIALIZED = 12289,
+ BAD_ACCESS = 12290,
+ BAD_ALLOC = 12291,
+ BAD_ATTRIBUTE = 12292,
+ BAD_CONFIG = 12293,
+ BAD_CONTEXT = 12294,
+ BAD_CURRENT_SURFACE = 12295,
+ BAD_DISPLAY = 12296,
+ BAD_MATCH = 12297,
+ BAD_NATIVE_PIXMAP = 12298,
+ BAD_NATIVE_WINDOW = 12299,
+ BAD_PARAMETER = 12300,
+ BAD_SURFACE = 12301,
+ }
+
static partial class Egl
{
public const int VERSION_1_0 = 1;
@@ -60,20 +85,6 @@ namespace OpenTK.Platform.Egl
public const int FALSE = 0;
public const int TRUE = 1;
public const int DONT_CARE = -1;
- public const int SUCCESS = 12288;
- public const int NOT_INITIALIZED = 12289;
- public const int BAD_ACCESS = 12290;
- public const int BAD_ALLOC = 12291;
- public const int BAD_ATTRIBUTE = 12292;
- public const int BAD_CONFIG = 12293;
- public const int BAD_CONTEXT = 12294;
- public const int BAD_CURRENT_SURFACE = 12295;
- public const int BAD_DISPLAY = 12296;
- public const int BAD_MATCH = 12297;
- public const int BAD_NATIVE_PIXMAP = 12298;
- public const int BAD_NATIVE_WINDOW = 12299;
- public const int BAD_PARAMETER = 12300;
- public const int BAD_SURFACE = 12301;
public const int CONTEXT_LOST = 12302;
public const int BUFFER_SIZE = 12320;
public const int ALPHA_SIZE = 12321;
@@ -178,7 +189,7 @@ namespace OpenTK.Platform.Egl
public const int ALPHA_FORMAT_PRE = VG_ALPHA_FORMAT_PRE;
[DllImportAttribute("libEGL.dll", EntryPoint = "eglGetError")]
- public static extern int GetError();
+ public static extern ErrorCode GetError();
[DllImportAttribute("libEGL.dll", EntryPoint = "eglGetDisplay")]
public static extern EGLDisplay GetDisplay(EGLNativeDisplayType display_id);
@@ -225,7 +236,7 @@ namespace OpenTK.Platform.Egl
[DllImportAttribute("libEGL.dll", EntryPoint = "eglBindAPI")]
[return: MarshalAsAttribute(UnmanagedType.I1)]
- public static extern bool BindAPI(int api);
+ public static extern bool BindAPI(RenderApi api);
[DllImportAttribute("libEGL.dll", EntryPoint = "eglQueryAPI")]
public static extern int QueryAPI();
diff --git a/Source/OpenTK/Platform/Egl/EglContext.cs b/Source/OpenTK/Platform/Egl/EglContext.cs
index 37398f00..575174ff 100644
--- a/Source/OpenTK/Platform/Egl/EglContext.cs
+++ b/Source/OpenTK/Platform/Egl/EglContext.cs
@@ -35,9 +35,9 @@ namespace OpenTK.Platform.Egl
{
#region Fields
- readonly RenderableFlags Renderable;
+ protected readonly RenderableFlags Renderable;
+ protected EglWindowInfo WindowInfo;
- EglWindowInfo WindowInfo;
IntPtr HandleAsEGLContext { get { return Handle.Handle; } set { Handle = new ContextHandle(value); } }
int swap_interval = 1; // Default interval is defined as 1 in EGL.
@@ -60,7 +60,20 @@ namespace OpenTK.Platform.Egl
// Select an EGLConfig that matches the desired mode. We cannot use the 'mode'
// parameter directly, since it may have originated on a different system (e.g. GLX)
// and it may not support the desired renderer.
- Renderable = major > 1 ? RenderableFlags.ES2 : RenderableFlags.ES;
+
+ Renderable = RenderableFlags.GL;
+ if ((flags & GraphicsContextFlags.Embedded) != 0)
+ {
+ Renderable = major > 1 ? RenderableFlags.ES2 : RenderableFlags.ES;
+ }
+
+ RenderApi api = (Renderable & RenderableFlags.GL) != 0 ? RenderApi.GL : RenderApi.ES;
+ Debug.Print("[EGL] Binding {0} rendering API.", api);
+ if (!Egl.BindAPI(api))
+ {
+ Debug.Print("[EGL] Failed to bind rendering API. Error: {0}", Egl.GetError());
+ }
+
Mode = new EglGraphicsMode().SelectGraphicsMode(window,
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo,
@@ -133,7 +146,8 @@ namespace OpenTK.Platform.Egl
if (Egl.SwapInterval(WindowInfo.Display, value))
swap_interval = value;
else
- Debug.Print("[Warning] Egl.SwapInterval({0}, {1}) failed.", WindowInfo.Display, value);
+ Debug.Print("[Warning] Egl.SwapInterval({0}, {1}) failed. Error: {2}",
+ WindowInfo.Display, value, Egl.GetError());
}
}
diff --git a/Source/OpenTK/Platform/Egl/EglGraphicsMode.cs b/Source/OpenTK/Platform/Egl/EglGraphicsMode.cs
index bc78bc82..779e8ff2 100644
--- a/Source/OpenTK/Platform/Egl/EglGraphicsMode.cs
+++ b/Source/OpenTK/Platform/Egl/EglGraphicsMode.cs
@@ -34,6 +34,15 @@ namespace OpenTK.Platform.Egl
{
class EglGraphicsMode
{
+ public GraphicsMode SelectGraphicsMode(EglWindowInfo window,
+ GraphicsMode mode, RenderableFlags flags)
+ {
+ return SelectGraphicsMode(window,
+ mode.ColorFormat, mode.Depth, mode.Stencil,
+ mode.Samples, mode.AccumulatorFormat, mode.Buffers, mode.Stereo,
+ flags);
+ }
+
public GraphicsMode SelectGraphicsMode(EglWindowInfo window,
ColorFormat color, int depth, int stencil,
int samples, ColorFormat accum, int buffers, bool stereo,
diff --git a/Source/OpenTK/Platform/Egl/EglUnixContext.cs b/Source/OpenTK/Platform/Egl/EglUnixContext.cs
index 5880138e..94eb2e43 100644
--- a/Source/OpenTK/Platform/Egl/EglUnixContext.cs
+++ b/Source/OpenTK/Platform/Egl/EglUnixContext.cs
@@ -28,14 +28,16 @@
#endregion
using System;
+using System.Diagnostics;
using OpenTK.Graphics;
namespace OpenTK.Platform.Egl
{
class EglUnixContext : EglContext
{
- readonly IntPtr ES1 = OpenTK.Platform.X11.DL.Open("libGLESv1_CM", X11.DLOpenFlags.Lazy);
- readonly IntPtr ES2 = OpenTK.Platform.X11.DL.Open("libGLESv2", X11.DLOpenFlags.Lazy);
+ IntPtr ES1 = OpenTK.Platform.X11.DL.Open("libGLESv1_CM", X11.DLOpenFlags.Lazy);
+ IntPtr ES2 = OpenTK.Platform.X11.DL.Open("libGLESv2", X11.DLOpenFlags.Lazy);
+ IntPtr GL = OpenTK.Platform.X11.DL.Open("libGL", X11.DLOpenFlags.Lazy);
public EglUnixContext(GraphicsMode mode, EglWindowInfo window, IGraphicsContext sharedContext,
int major, int minor, GraphicsContextFlags flags)
@@ -59,6 +61,10 @@ namespace OpenTK.Platform.Egl
{
return X11.DL.Symbol(ES2, function);
}
+ else if ((renderable & RenderableFlags.GL) != 0 && GL != IntPtr.Zero)
+ {
+ return X11.DL.Symbol(GL, function);
+ }
return IntPtr.Zero;
}
@@ -72,8 +78,32 @@ namespace OpenTK.Platform.Egl
{
X11.DL.Close(ES2);
}
+ if (GL != IntPtr.Zero)
+ {
+ X11.DL.Close(GL);
+ }
+
+ GL = ES1 = ES2 = IntPtr.Zero;
base.Dispose(manual);
}
+
+ public override void LoadAll()
+ {
+ // Modern unices can use EGL to create
+ // both GL and ES contexts, so we need
+ // to load all entry points. This is
+ // especially true on KMS, Wayland and Mir.
+
+ Stopwatch time = Stopwatch.StartNew();
+
+ new OpenTK.Graphics.OpenGL.GL().LoadEntryPoints();
+ new OpenTK.Graphics.OpenGL4.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES11.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES20.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES30.GL().LoadEntryPoints();
+
+ Debug.Print("Bindings loaded in {0} ms.", time.Elapsed.TotalMilliseconds);
+ }
}
}
diff --git a/Source/OpenTK/Platform/Egl/EglWindowInfo.cs b/Source/OpenTK/Platform/Egl/EglWindowInfo.cs
index db9acb49..4d0e1945 100644
--- a/Source/OpenTK/Platform/Egl/EglWindowInfo.cs
+++ b/Source/OpenTK/Platform/Egl/EglWindowInfo.cs
@@ -75,7 +75,7 @@ namespace OpenTK.Platform.Egl
#region Public Members
- public IntPtr Handle { get { return handle; } private set { handle = value; } }
+ public IntPtr Handle { get { return handle; } set { handle = value; } }
public IntPtr Display { get { return display; } private set { display = value; } }
@@ -87,7 +87,7 @@ namespace OpenTK.Platform.Egl
if (Surface==IntPtr.Zero)
{
throw new GraphicsContextException(String.Format(
- "[Error] Failed to create EGL window surface, error {0}.", Egl.GetError()));
+ "[EGL] Failed to create window surface, error {0}.", Egl.GetError()));
}
}
diff --git a/Source/OpenTK/Platform/Factory.cs b/Source/OpenTK/Platform/Factory.cs
index 9a701844..2bcfada5 100644
--- a/Source/OpenTK/Platform/Factory.cs
+++ b/Source/OpenTK/Platform/Factory.cs
@@ -54,6 +54,7 @@ namespace OpenTK.Platform
// Create regular platform backend
if (Configuration.RunningOnSdl2) Default = new SDL2.Sdl2Factory();
else if (Configuration.RunningOnX11) Default = new X11.X11Factory();
+ else if (Configuration.RunningOnLinux) Default = new Linux.LinuxFactory();
else if (Configuration.RunningOnWindows) Default = new Windows.WinFactory();
else if (Configuration.RunningOnMacOS) Default = new MacOS.MacOSFactory();
else Default = new UnsupportedPlatform();
@@ -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
new file mode 100644
index 00000000..583dd449
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
@@ -0,0 +1,209 @@
+#region License
+//
+// Drm.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.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void VBlankCallback(int fd,
+ int sequence,
+ int tv_sec,
+ int tv_usec,
+ IntPtr user_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void PageFlipCallback(int fd,
+ int sequence,
+ int tv_sec,
+ int tv_usec,
+ IntPtr user_data);
+
+ class Drm
+ {
+ const string lib = "libdrm";
+
+ [DllImport(lib, EntryPoint = "drmHandleEvent", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int HandleEvent(int fd, ref EventContext evctx);
+
+ [DllImport(lib, EntryPoint = "drmModeAddFB", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ModeAddFB(int fd, int width, int height, byte depth,
+ byte bpp, int pitch, int bo_handle,
+ out int buf_id);
+
+ [DllImport(lib, EntryPoint = "drmModeRmFB", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ModeRmFB(int fd, int bufferId);
+
+ [DllImport(lib, EntryPoint = "drmModeFreeCrtc", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void ModeFreeCrtc(IntPtr ptr);
+
+ [DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr ModeGetCrtc(int fd, int 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, int connector_id);
+
+ [DllImport(lib, EntryPoint = "drmModeGetEncoder", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr ModeGetEncoder(int fd, int encoder_id);
+
+ [DllImport(lib, EntryPoint = "drmModeGetResources", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr ModeGetResources(int fd);
+
+ [DllImport(lib, EntryPoint = "drmModePageFlip", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ModePageFlip(int fd, int crtc_id, int fb_id,
+ PageFlipFlags flags, IntPtr user_data);
+
+ [DllImport(lib, EntryPoint = "drmModeSetCrtc", CallingConvention = CallingConvention.Cdecl)]
+ unsafe public static extern int ModeSetCrtc(int fd, int crtcId, int bufferId,
+ int x, int y, int* connectors, int count, ModeInfo* mode);
+
+ [DllImport(lib, EntryPoint = "drmModeSetCursor2", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int SetCursor(int fd, int crtcId, int bo_handle, int width, int height, int hot_x, int hot_y);
+
+ [DllImport(lib, EntryPoint = "drmModeMoveCursor", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int MoveCursor(int fd, int crtcId, int x, int y);
+
+ }
+
+ enum ModeConnection
+ {
+ Connected = 1,
+ Disconnected = 2,
+ Unknown = 3
+ }
+
+ enum ModeSubPixel
+ {
+ Unknown = 1,
+ HorizontalRgb = 2,
+ HorizontalBgr = 3,
+ VerticalRgb = 4,
+ VerticalBgr = 5,
+ None = 6
+ }
+
+ [Flags]
+ enum PageFlipFlags
+ {
+ FlipEvent = 0x01,
+ FlipAsync = 0x02,
+ FlipFlags = FlipEvent | FlipAsync
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct EventContext
+ {
+ public int version;
+ public IntPtr vblank_handler;
+ public IntPtr page_flip_handler;
+
+ public static readonly int Version = 2;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct ModeConnector
+ {
+ public int connector_id;
+ public int encoder_id;
+ public int connector_type;
+ public int connector_type_id;
+ public ModeConnection connection;
+ public int mmWidth, mmHeight;
+ public ModeSubPixel subpixel;
+
+ public int count_modes;
+ public ModeInfo* modes;
+
+ public int count_props;
+ public int *props;
+ public long *prop_values;
+
+ public int count_encoders;
+ public int *encoders;
+ }
+
+ struct ModeCrtc
+ {
+ public int crtc_id;
+ public int buffer_id;
+
+ public int x, y;
+ public int width, height;
+ public int mode_valid;
+ public ModeInfo mode;
+
+ public int gamma_size;
+ }
+
+ struct ModeEncoder
+ {
+ public int encoder_id;
+ public int encoder_type;
+ public int crtc_id;
+ public int possible_crtcs;
+ public int 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 int 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 int* fbs;
+ public int count_crtcs;
+ public int* crtcs;
+ public int count_connectors;
+ public int* connectors;
+ public int count_encoders;
+ public int* encoders;
+ public int min_width, max_width;
+ public int min_height, max_height;
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Evdev.cs b/Source/OpenTK/Platform/Linux/Bindings/Evdev.cs
new file mode 100644
index 00000000..11988ebe
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Evdev.cs
@@ -0,0 +1,452 @@
+#region License
+//
+// Evdev.cs
+//
+// Author:
+// Stefanos A.
+//
+// Copyright (c) 2006-2014
+//
+// 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.Input;
+
+namespace OpenTK.Platform.Linux
+{
+ // Bindings for linux/input.h
+ class Evdev
+ {
+ #region KeyMap
+
+ public static readonly Key[] KeyMap = new Key[]
+ {
+ // 0-7
+ Key.Unknown,
+ Key.Escape,
+ Key.Number1,
+ Key.Number2,
+ Key.Number3,
+ Key.Number4,
+ Key.Number5,
+ Key.Number6,
+ // 8-15
+ Key.Number7,
+ Key.Number8,
+ Key.Number9,
+ Key.Number0,
+ Key.Minus,
+ Key.Plus,
+ Key.BackSpace,
+ Key.Tab,
+ // 16-23
+ Key.Q,
+ Key.W,
+ Key.E,
+ Key.R,
+ Key.T,
+ Key.Y,
+ Key.U,
+ Key.I,
+ // 24-31
+ Key.O,
+ Key.P,
+ Key.BracketLeft,
+ Key.BracketRight,
+ Key.Enter,
+ Key.ControlLeft,
+ Key.A,
+ Key.S,
+ // 32-39
+ Key.D,
+ Key.F,
+ Key.G,
+ Key.H,
+ Key.J,
+ Key.K,
+ Key.L,
+ Key.Semicolon,
+ // 40-47
+ Key.Quote,
+ Key.Tilde,
+ Key.ShiftLeft,
+ Key.BackSlash, //Key.Execute,
+ Key.Z,
+ Key.X,
+ Key.C,
+ Key.V, //Key.Help,
+ // 48-55
+ Key.B,
+ Key.N,
+ Key.M,
+ Key.Comma,
+ Key.Period,
+ Key.Slash,
+ Key.ShiftRight,
+ Key.KeypadMultiply,
+ // 56-63
+ Key.AltLeft,
+ Key.Space,
+ Key.CapsLock,
+ Key.F1,
+ Key.F2,
+ Key.F3,
+ Key.F4,
+ Key.F5,
+ // 64-71
+ Key.F6,
+ Key.F7,
+ Key.F8,
+ Key.F9,
+ Key.F10,
+ Key.NumLock,
+ Key.ScrollLock,
+ Key.Keypad7,
+ // 72-79
+ Key.Keypad8,
+ Key.Keypad9,
+ Key.KeypadSubtract,
+ Key.Keypad4,
+ Key.Keypad5,
+ Key.Keypad6,
+ Key.KeypadPlus,
+ Key.Keypad1,
+ // 80-87
+ Key.Keypad2,
+ Key.Keypad3,
+ Key.Keypad0,
+ Key.KeypadPeriod,
+ Key.Unknown,
+ Key.Unknown, // zenkakuhankaku
+ Key.Unknown, // 102ND
+ Key.F11,
+ // 88-95
+ Key.F12,
+ Key.Unknown, // ro
+ Key.Unknown, // katakana
+ Key.Unknown, // hiragana
+ Key.Unknown, // henkan
+ Key.Unknown, // katakanahiragana
+ Key.Unknown, // muhenkan
+ Key.Unknown, // kpjpcomma
+ // 96-103
+ Key.KeypadEnter,
+ Key.ControlRight,
+ Key.KeypadDivide,
+ Key.Unknown, // sysrq
+ Key.AltRight,
+ Key.Unknown, // linefeed
+ Key.Home,
+ Key.Up,
+ // 104-111
+ Key.PageUp,
+ Key.Left,
+ Key.Right,
+ Key.End,
+ Key.Down,
+ Key.PageDown,
+ Key.Insert,
+ Key.Delete,
+ // 112-119
+ Key.Unknown, // macro
+ Key.Unknown, // mute
+ Key.Unknown, // volumedown
+ Key.Unknown, // volumeup
+ Key.Unknown, // power
+ Key.Unknown, // kpequal
+ Key.Unknown, // kpplusminus
+ Key.Pause,
+ // 120-127
+ Key.Unknown, // scale
+ Key.Unknown, // kpcomma
+ Key.Unknown, // hangeul / hanguel
+ Key.Unknown, // hanja
+ Key.Unknown, // yen
+ Key.WinLeft,
+ Key.WinRight,
+ Key.Unknown, // compose
+ // 128-135
+ Key.Unknown, // stop
+ Key.Unknown, // again
+ Key.Unknown, // props
+ Key.Unknown, // undo
+ Key.Unknown, // front
+ Key.Unknown, // copy
+ Key.Unknown, // open
+ Key.Unknown, // paste
+ // 136-143
+ Key.Unknown, // find
+ Key.Unknown, // cut
+ Key.Unknown, // help
+ Key.Unknown, // menu
+ Key.Unknown, // calc
+ Key.Unknown, // setup
+ Key.Unknown, // sleep
+ Key.Unknown, // wakeup
+ // 144-151
+ Key.Unknown, // file
+ Key.Unknown, // send file
+ Key.Unknown, // delete file
+ Key.Unknown, // xfer
+ Key.Unknown, // prog1
+ Key.Unknown, // prog2
+ Key.Unknown, // www
+ Key.Unknown, // msdos
+ // 152-159
+ Key.Unknown, // coffee / screenlock
+ Key.Unknown, // direction
+ Key.Unknown, // cycle windows
+ Key.Unknown, // mail
+ Key.Unknown, // bookmarks
+ Key.Unknown, // computer
+ Key.Back,
+ Key.Unknown, // forward
+ // 160-167
+ Key.Unknown, // close cd
+ Key.Unknown, // eject cd
+ Key.Unknown, // eject/close cd
+ Key.Unknown, // next song
+ Key.Unknown, // play/pause
+ Key.Unknown, // previous song
+ Key.Unknown, // stop cd
+ Key.Unknown, // record
+ // 168-175
+ Key.Unknown, // rewind
+ Key.Unknown, // phone
+ Key.Unknown, // iso
+ Key.Unknown, // config
+ Key.Unknown, // homepage
+ Key.Unknown, // refresh
+ Key.Unknown, // exit
+ Key.Unknown, // move,
+ // 176-183
+ Key.Unknown, // edit,
+ Key.Unknown, // scroll up,
+ Key.Unknown, // scroll down,
+ Key.Unknown, // kp left paren,
+ Key.Unknown, // kp right paren,
+ Key.Unknown, // new,
+ Key.Unknown, // redo,
+ Key.F13,
+ // 184-191
+ Key.F14,
+ Key.F15,
+ Key.F16,
+ Key.F17,
+ Key.F18,
+ Key.F19,
+ Key.F20,
+ Key.F21,
+ // 192-199
+ Key.F22,
+ Key.F23,
+ Key.F24,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ // 200-207
+ Key.Unknown, // play cd
+ Key.Unknown, // pause cd
+ Key.Unknown, // prog3
+ Key.Unknown, // prog4
+ Key.Unknown, // dashboard
+ Key.Unknown, // suspend
+ Key.Unknown, // close
+ Key.Unknown, // play
+ // 208-215
+ Key.Unknown, // fast forward
+ Key.Unknown, // bass boost
+ Key.Unknown, // print
+ Key.Unknown, // hp
+ Key.Unknown, // camera
+ Key.Unknown, // sound
+ Key.Unknown, // question
+ Key.Unknown, // email
+ // 216-223
+ Key.Unknown, // chat
+ Key.Unknown, // search
+ Key.Unknown, // connect
+ Key.Unknown, // finance
+ Key.Unknown, // sport
+ Key.Unknown, // shop
+ Key.Unknown, // alt erase
+ Key.Unknown, // cancel
+ // 224-231
+ Key.Unknown, // brightness down
+ Key.Unknown, // brightness up
+ Key.Unknown, // media
+ Key.Unknown, // switch video mode
+ Key.Unknown, // dillum toggle
+ Key.Unknown, // dillum down
+ Key.Unknown, // dillum up
+ Key.Unknown, // send
+ // 232-239
+ Key.Unknown, // reply
+ Key.Unknown, // forward email
+ Key.Unknown, // save
+ Key.Unknown, // documents
+ Key.Unknown, // battery
+ Key.Unknown, // bluetooth
+ Key.Unknown, // wlan
+ Key.Unknown, // uwb
+ // 240-247
+ Key.Unknown,
+ Key.Unknown, // video next
+ Key.Unknown, // video prev
+ Key.Unknown, // brightness cycle
+ Key.Unknown, // brightness zero
+ Key.Unknown, // display off
+ Key.Unknown, // wwan / wimax
+ Key.Unknown, // rfkill
+ // 248-255
+ Key.Unknown, // mic mute
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown,
+ Key.Unknown, // reserved
+ };
+
+ #endregion
+
+ public static MouseButton GetMouseButton(EvdevButton button)
+ {
+ switch (button)
+ {
+ case EvdevButton.LEFT:
+ return MouseButton.Left;
+ case EvdevButton.RIGHT:
+ return MouseButton.Right;
+ case EvdevButton.MIDDLE:
+ return MouseButton.Middle;
+ case EvdevButton.BTN0:
+ return MouseButton.Button1;
+ case EvdevButton.BTN1:
+ return MouseButton.Button2;
+ case EvdevButton.BTN2:
+ return MouseButton.Button3;
+ case EvdevButton.BTN3:
+ return MouseButton.Button4;
+ case EvdevButton.BTN4:
+ return MouseButton.Button5;
+ case EvdevButton.BTN5:
+ return MouseButton.Button6;
+ case EvdevButton.BTN6:
+ return MouseButton.Button7;
+ case EvdevButton.BTN7:
+ return MouseButton.Button8;
+ case EvdevButton.BTN8:
+ return MouseButton.Button9;
+ default:
+ Debug.Print("[Input] Unknown EvdevButton {0}", button);
+ return MouseButton.Left;
+ }
+ }
+ }
+
+ enum EvdevButton : uint
+ {
+ MISC = 0x100,
+ BTN0 = 0x100,
+ BTN1 = 0x101,
+ BTN2 = 0x102,
+ BTN3 = 0x103,
+ BTN4 = 0x104,
+ BTN5 = 0x105,
+ BTN6 = 0x106,
+ BTN7 = 0x107,
+ BTN8 = 0x108,
+ BTN9 = 0x109,
+
+ MOUSE = 0x110,
+ LEFT = 0x110,
+ RIGHT = 0x111,
+ MIDDLE = 0x112,
+ SIDE = 0x113,
+ EXTRA = 0x114,
+ FORWARD = 0x115,
+ BACK = 0x116,
+ TASK = 0x117,
+
+ JOYSTICK = 0x120,
+ TRIGGER = 0x120,
+ THUMB = 0x121,
+ THUMB2 = 0x122,
+ TOP = 0x123,
+ TOP2 = 0x124,
+ PINKIE = 0x125,
+ BASE = 0x126,
+ BASE2 = 0x127,
+ BASE3 = 0x128,
+ BASE4 = 0x129,
+ BASE5 = 0x12a,
+ BASE6 = 0x12b,
+ DEAD = 0x12f,
+
+ GAMEPAD = 0x130,
+ SOUTH = 0x130,
+ A = SOUTH,
+ EAST = 0x131,
+ B = EAST,
+ C = 0x132,
+ NORTH = 0x133,
+ X = NORTH,
+ WEST = 0x134,
+ Y = WEST,
+ Z = 0x135,
+ TL = 0x136,
+ TR = 0x137,
+ TL2 = 0x138,
+ TR2 = 0x139,
+ SELECT = 0x13a,
+ START = 0x13b,
+ MODE = 0x13c,
+ THUMBL = 0x13d,
+ THUMBR = 0x13e,
+
+ DIGI = 0x140,
+ TOOL_PEN = 0x140,
+ TOOL_RUBBER = 0x141,
+ TOOL_BRUSH = 0x142,
+ TOOL_PENCIL = 0x143,
+ TOOL_AIRBRUSH = 0x144,
+ TOOL_FINGER = 0x145,
+ TOOL_MOUSE = 0x146,
+ TOOL_LENS = 0x147,
+ TOOL_QUINTTAP = 0x148, // Five fingers on trackpad
+ TOUCH = 0x14a,
+ STYLUS = 0x14b,
+ STYLUS2 = 0x14c,
+ TOOL_DOUBLETAP = 0x14d,
+ TOOL_TRIPLETAP = 0x14e,
+ TOOL_QUADTAP = 0x14f, // Four fingers on trackpad
+
+ WHEEL = 0x150,
+ GEAR_DOWN = 0x150,
+ GEAR_UP = 0x151,
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
new file mode 100644
index 00000000..d298d5ea
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
@@ -0,0 +1,278 @@
+#region License
+//
+// Gbm.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.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ using Device = IntPtr; // struct gbm_device*
+ using Surface = IntPtr;
+ using BufferObjectHandle = IntPtr;
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void DestroyUserDataCallback(BufferObject bo, IntPtr data);
+
+ class Gbm
+ {
+ const string lib = "gbm";
+
+ [DllImport(lib, EntryPoint = "gbm_bo_create", CallingConvention = CallingConvention.Cdecl)]
+ public static extern BufferObject CreateBuffer(Device gbm, int width, int height, SurfaceFormat format, SurfaceFlags flags);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroyBuffer(BufferObject bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_write", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOWrite(IntPtr bo, IntPtr buf, IntPtr count);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_device", CallingConvention = CallingConvention.Cdecl)]
+ public static extern Device BOGetDevice(IntPtr bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_handle", CallingConvention = CallingConvention.Cdecl)]
+ public static extern BufferObjectHandle BOGetHandle(IntPtr bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_height", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOGetHeight(IntPtr bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_width", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOGetWidth(IntPtr bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_get_stride", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int BOGetStride(IntPtr bo);
+
+ [DllImport(lib, EntryPoint = "gbm_bo_set_user_data", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void BOSetUserData(IntPtr bo, IntPtr data, DestroyUserDataCallback callback);
+
+ [DllImport(lib, EntryPoint = "gbm_create_device", CallingConvention = CallingConvention.Cdecl)]
+ public static extern Device CreateDevice(int fd);
+
+ [DllImport(lib, EntryPoint = "gbm_device_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroyDevice(Device gbm);
+
+ [DllImport(lib, EntryPoint = "gbm_device_get_fd", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int DeviceGetFD(IntPtr gbm);
+
+ [DllImport(lib, EntryPoint = "gbm_surface_create", CallingConvention = CallingConvention.Cdecl)]
+ public static extern Surface CreateSurface(Device gbm, int width, int height, SurfaceFormat format, SurfaceFlags flags);
+
+ [DllImport(lib, EntryPoint = "gbm_surface_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroySurface(IntPtr surface);
+
+ [DllImport(lib, EntryPoint = "gbm_device_is_format_supported", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool IsFormatSupported(Device gbm, SurfaceFormat format, SurfaceFlags usage);
+
+ [DllImport(lib, EntryPoint = "gbm_surface_lock_front_buffer", CallingConvention = CallingConvention.Cdecl)]
+ public static extern BufferObject LockFrontBuffer(Surface surface);
+
+ [DllImport(lib, EntryPoint = "gbm_surface_release_buffer", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void ReleaseBuffer(Surface surface, BufferObject buffer);
+ }
+
+ enum SurfaceFormat
+ {
+ BigEndian = 1 << 31,
+ C8 = ((int)('C') | ((int)('8') << 8) | ((int)(' ') << 16) | ((int)(' ') << 24)),
+
+ RGB332 = ((int)('R') | ((int)('G') << 8) | ((int)('B') << 16) | ((int)('8') << 24)),
+ BGR233 = ((int)('B') | ((int)('G') << 8) | ((int)('R') << 16) | ((int)('8') << 24)),
+
+ XRGB4444 = ((int)('X') | ((int)('R') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ XBGR4444 = ((int)('X') | ((int)('B') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ RGBX4444 = ((int)('R') | ((int)('X') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ BGRX4444 = ((int)('B') | ((int)('X') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+
+ ARGB4444 = ((int)('A') | ((int)('R') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ ABGR4444 = ((int)('A') | ((int)('B') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ RGBA4444 = ((int)('R') | ((int)('A') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ BGRA4444 = ((int)('B') | ((int)('A') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+
+ XRGB1555 = ((int)('X') | ((int)('R') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+ XBGR1555 = ((int)('X') | ((int)('B') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+ RGBX5551 = ((int)('R') | ((int)('X') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+ BGRX5551 = ((int)('B') | ((int)('X') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+
+ ARGB1555 = ((int)('A') | ((int)('R') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+ ABGR1555 = ((int)('A') | ((int)('B') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+ RGBA5551 = ((int)('R') | ((int)('A') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+ BGRA5551 = ((int)('B') | ((int)('A') << 8) | ((int)('1') << 16) | ((int)('5') << 24)),
+
+ RGB565 = ((int)('R') | ((int)('G') << 8) | ((int)('1') << 16) | ((int)('6') << 24)),
+ BGR565 = ((int)('B') | ((int)('G') << 8) | ((int)('1') << 16) | ((int)('6') << 24)),
+
+ RGB888 = ((int)('R') | ((int)('G') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ BGR888 = ((int)('B') | ((int)('G') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+
+ XRGB8888 = ((int)('X') | ((int)('R') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ XBGR8888 = ((int)('X') | ((int)('B') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ RGBX8888 = ((int)('R') | ((int)('X') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ BGRX8888 = ((int)('B') | ((int)('X') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+
+ ARGB8888 = ((int)('A') | ((int)('R') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ ABGR8888 = ((int)('A') | ((int)('B') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ RGBA8888 = ((int)('R') | ((int)('A') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ BGRA8888 = ((int)('B') | ((int)('A') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+
+ XRGB2101010 = ((int)('X') | ((int)('R') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+ XBGR2101010 = ((int)('X') | ((int)('B') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+ RGBX1010102 = ((int)('R') | ((int)('X') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+ BGRX1010102 = ((int)('B') | ((int)('X') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+
+ ARGB2101010 = ((int)('A') | ((int)('R') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+ ABGR2101010 = ((int)('A') | ((int)('B') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+ RGBA1010102 = ((int)('R') | ((int)('A') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+ BGRA1010102 = ((int)('B') | ((int)('A') << 8) | ((int)('3') << 16) | ((int)('0') << 24)),
+
+ YUYV = ((int)('Y') | ((int)('U') << 8) | ((int)('Y') << 16) | ((int)('V') << 24)),
+ YVYU = ((int)('Y') | ((int)('V') << 8) | ((int)('Y') << 16) | ((int)('U') << 24)),
+ UYVY = ((int)('U') | ((int)('Y') << 8) | ((int)('V') << 16) | ((int)('Y') << 24)),
+ VYUY = ((int)('V') | ((int)('Y') << 8) | ((int)('U') << 16) | ((int)('Y') << 24)),
+
+ AYUV = ((int)('A') | ((int)('Y') << 8) | ((int)('U') << 16) | ((int)('V') << 24)),
+
+ NV12 = ((int)('N') | ((int)('V') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ NV21 = ((int)('N') | ((int)('V') << 8) | ((int)('2') << 16) | ((int)('1') << 24)),
+ NV16 = ((int)('N') | ((int)('V') << 8) | ((int)('1') << 16) | ((int)('6') << 24)),
+ NV61 = ((int)('N') | ((int)('V') << 8) | ((int)('6') << 16) | ((int)('1') << 24)),
+
+ YUV410 = ((int)('Y') | ((int)('U') << 8) | ((int)('V') << 16) | ((int)('9') << 24)),
+ YVU410 = ((int)('Y') | ((int)('V') << 8) | ((int)('U') << 16) | ((int)('9') << 24)),
+ YUV411 = ((int)('Y') | ((int)('U') << 8) | ((int)('1') << 16) | ((int)('1') << 24)),
+ YVU411 = ((int)('Y') | ((int)('V') << 8) | ((int)('1') << 16) | ((int)('1') << 24)),
+ YUV420 = ((int)('Y') | ((int)('U') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ YVU420 = ((int)('Y') | ((int)('V') << 8) | ((int)('1') << 16) | ((int)('2') << 24)),
+ YUV422 = ((int)('Y') | ((int)('U') << 8) | ((int)('1') << 16) | ((int)('6') << 24)),
+ YVU422 = ((int)('Y') | ((int)('V') << 8) | ((int)('1') << 16) | ((int)('6') << 24)),
+ YUV444 = ((int)('Y') | ((int)('U') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ YVU444 = ((int)('Y') | ((int)('V') << 8) | ((int)('2') << 16) | ((int)('4') << 24)),
+ }
+
+ [Flags]
+ enum SurfaceFlags
+ {
+ Scanout = (1 << 0),
+ Cursor64x64 = (1 << 1),
+ Rendering = (1 << 2),
+ Write = (1 << 3),
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct BufferObject : IEquatable
+ {
+ IntPtr buffer;
+
+ public static readonly BufferObject Zero =
+ default(BufferObject);
+
+ public int Write(byte[] data)
+ {
+ unsafe
+ {
+ fixed (byte* pdata = data)
+ {
+ return Gbm.BOWrite(buffer, (IntPtr)pdata, (IntPtr)data.Length);
+ }
+ }
+ }
+
+ public void SetUserData(IntPtr data, DestroyUserDataCallback destroyFB)
+ {
+ Gbm.BOSetUserData(buffer, data, destroyFB);
+ }
+
+ public Device Device
+ {
+ get { return Gbm.BOGetDevice(buffer); }
+ }
+
+ public int Handle
+ {
+ get { return Gbm.BOGetHandle(buffer).ToInt32(); }
+ }
+
+ public int Width
+ {
+ get { return Gbm.BOGetWidth(buffer); }
+ }
+
+ public int Height
+ {
+ get { return Gbm.BOGetHeight(buffer); }
+ }
+
+ public int Stride
+ {
+ get { return Gbm.BOGetStride(buffer); }
+ }
+
+ public void Dispose()
+ {
+ Gbm.DestroyBuffer(this);
+ buffer = IntPtr.Zero;
+ }
+
+ public static bool operator ==(BufferObject left, BufferObject right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(BufferObject left, BufferObject right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return
+ obj is BufferObject &&
+ this.Equals((BufferObject)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return buffer.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return string.Format("[BufferObject: {0}]", buffer);
+ }
+
+ #region IEquatable implementation
+
+ public bool Equals(BufferObject other)
+ {
+ return buffer == other.buffer;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Kms.cs b/Source/OpenTK/Platform/Linux/Bindings/Kms.cs
new file mode 100644
index 00000000..a5c7f48b
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Kms.cs
@@ -0,0 +1,46 @@
+#region License
+//
+// Kms.cs
+//
+// Author:
+// Stefanos A.
+//
+// Copyright (c) 2006-2014
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ class Kms
+ {
+ const string lib = "libkms";
+
+ [DllImport(lib, EntryPoint = "kms_bo_map", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int MapBuffer(IntPtr bo, out IntPtr @out);
+
+ [DllImport(lib, EntryPoint = "kms_bo_unmap", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int UnmapBuffer(IntPtr bo);
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs b/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs
new file mode 100644
index 00000000..8dd2b69d
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/LibInput.cs
@@ -0,0 +1,330 @@
+#region License
+//
+// LibInput.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
+
+#pragma warning disable 0169, 0219
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate int OpenRestrictedCallback(IntPtr path, int flags, IntPtr data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate void CloseRestrictedCallback(int fd, IntPtr data);
+
+ class LibInput
+ {
+ internal const string lib = "libinput";
+
+ [DllImport(lib, EntryPoint = "libinput_udev_create_for_seat", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr CreateContext(InputInterface @interface,
+ IntPtr user_data, IntPtr udev, string seat_id);
+
+ [DllImport(lib, EntryPoint = "libinput_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroyContext(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_event_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DestroyEvent(IntPtr @event);
+
+ [DllImport(lib, EntryPoint = "libinput_device_get_sysname", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DeviceGetNameInternal(IntPtr device);
+ public static string DeviceGetName(IntPtr device)
+ {
+ unsafe
+ {
+ return new string((sbyte*)DeviceGetNameInternal(device));
+ }
+ }
+
+ [DllImport(lib, EntryPoint = "libinput_device_get_user_data", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr DeviceGetData(IntPtr device);
+
+ [DllImport(lib, EntryPoint = "libinput_device_set_user_data", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void DeviceSetData(IntPtr device, IntPtr user_data);
+
+ [DllImport(lib, EntryPoint = "libinput_device_get_output_name", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DeviceGetOutputNameInternal(IntPtr device);
+ public static string DeviceGetOutputName(IntPtr device)
+ {
+ unsafe
+ {
+ sbyte* pname = (sbyte*)DeviceGetOutputNameInternal(device);
+ return pname == null ? String.Empty : new string(pname);
+ }
+ }
+
+ [DllImport(lib, EntryPoint = "libinput_device_get_seat", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr DeviceGetSeat(IntPtr device);
+
+ [DllImport(lib, EntryPoint = "libinput_device_has_capability", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool DeviceHasCapability(IntPtr device, DeviceCapability capability);
+
+ [DllImport(lib, EntryPoint = "libinput_dispatch", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Dispatch(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_event_get_device", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr GetDevice(IntPtr @event);
+
+ [DllImport(lib, EntryPoint = "libinput_get_event", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr GetEvent(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_event_get_keyboard_event", CallingConvention = CallingConvention.Cdecl)]
+ public static extern KeyboardEvent GetKeyboardEvent(IntPtr @event);
+
+ [DllImport(lib, EntryPoint = "libinput_event_get_pointer_event", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PointerEvent GetPointerEvent(IntPtr @event);
+
+ [DllImport(lib, EntryPoint = "libinput_event_get_type", CallingConvention = CallingConvention.Cdecl)]
+ public static extern InputEventType GetEventType(IntPtr @event);
+
+ [DllImport(lib, EntryPoint = "libinput_get_fd", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int GetFD(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_next_event_type", CallingConvention = CallingConvention.Cdecl)]
+ public static extern InputEventType NextEventType(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_resume", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Resume(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_suspend", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Suspend(IntPtr libinput);
+
+ [DllImport(lib, EntryPoint = "libinput_seat_get_logical_name", CallingConvention = CallingConvention.Cdecl)]
+ static extern public IntPtr SeatGetLogicalNameInternal(IntPtr seat);
+ public static string SeatGetLogicalName(IntPtr seat)
+ {
+ unsafe
+ {
+ return new string((sbyte*)SeatGetLogicalNameInternal(seat));
+ }
+ }
+
+ [DllImport(lib, EntryPoint = "libinput_seat_get_physical_name", CallingConvention = CallingConvention.Cdecl)]
+ static extern public IntPtr SeatGetPhysicalNameInternal(IntPtr seat);
+ public static string SeatGetPhysicalName(IntPtr seat)
+ {
+ unsafe
+ {
+ return new string((sbyte*)SeatGetPhysicalNameInternal(seat));
+ }
+ }
+ }
+
+ enum DeviceCapability
+ {
+ Keyboard = 0,
+ Mouse,
+ Touch
+ }
+
+ enum InputEventType
+ {
+ None = 0,
+
+ DeviceAdded,
+ DeviceRemoved,
+
+ KeyboardKey = 300,
+
+ PointerMotion = 400,
+ PointerMotionAbsolute,
+ PointerButton,
+ PointerAxis,
+
+ TouchDown = 500,
+ TouchUP,
+ TouchMotion,
+ TouchCancel,
+
+ /// \internal
+ ///
+ /// Signals the end of a set of touchpoints at one device sample
+ /// time. This event has no coordinate information attached.
+ ///
+ TouchFrame
+ }
+
+ enum ButtonState
+ {
+ Released = 0,
+ Pressed = 1
+ }
+
+ enum KeyState
+ {
+ Released = 0,
+ Pressed = 1
+ }
+
+ enum PointerAxis
+ {
+ VerticalScroll = 0,
+ HorizontalScroll = 1
+ }
+
+ struct Fixed24
+ {
+ internal readonly int Value;
+
+ public static implicit operator double(Fixed24 n)
+ {
+ long l = ((1023L + 44L) << 52) + (1L << 51) + n.Value;
+ unsafe
+ {
+ double d = *(double*)&l;
+ return d - (3L << 43);
+ }
+ }
+
+ public static implicit operator float(Fixed24 n)
+ {
+ return (float)(double)n;
+ }
+
+ public static explicit operator int(Fixed24 n)
+ {
+ return n.Value >> 8;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ class InputInterface
+ {
+ internal readonly IntPtr open;
+ internal readonly IntPtr close;
+
+ public InputInterface(
+ OpenRestrictedCallback open_restricted,
+ CloseRestrictedCallback close_restricted)
+ {
+ if (open_restricted == null || close_restricted == null)
+ throw new ArgumentNullException();
+
+ open = Marshal.GetFunctionPointerForDelegate(open_restricted);
+ close = Marshal.GetFunctionPointerForDelegate(close_restricted);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct KeyboardEvent
+ {
+ IntPtr @event;
+
+ public IntPtr BaseEvent { get { return GetBaseEvent(@event); } }
+ public IntPtr Event { get { return @event; } }
+ public uint Time { get { return GetTime(@event); } }
+ public uint Key { get { return GetKey(@event); } }
+ public uint KeyCount { get { return GetSeatKeyCount(@event); } }
+ public KeyState KeyState { get { return GetKeyState(@event); } }
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_keyboard_get_time", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetTime(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_keyboard_get_base_event", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr GetBaseEvent(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_keyboard_get_seat_key_count", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetSeatKeyCount(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_keyboard_get_key", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetKey(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_keyboard_get_key_state", CallingConvention = CallingConvention.Cdecl)]
+ static extern KeyState GetKeyState(IntPtr @event);
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct PointerEvent
+ {
+ IntPtr @event;
+
+ public IntPtr BaseEvent { get { return GetBaseEvent(@event); } }
+ public IntPtr Event { get { return @event; } }
+ public uint Time { get { return GetTime(@event); } }
+ public EvdevButton Button { get { return (EvdevButton)GetButton(@event); } }
+ public uint ButtonCount { get { return GetButtonCount(@event); } }
+ public ButtonState ButtonState { get { return GetButtonState(@event); } }
+ public PointerAxis Axis { get { return GetAxis(@event); } }
+ public Fixed24 AxisValue { get { return GetAxisValue(@event); } }
+ public Fixed24 DeltaX { get { return GetDX(@event); } }
+ public Fixed24 DeltaY { get { return GetDY(@event); } }
+ public Fixed24 X { get { return GetAbsX(@event); } }
+ public Fixed24 Y { get { return GetAbsY(@event); } }
+ public Fixed24 TransformedX(int width) { return GetAbsXTransformed(@event, width); }
+ public Fixed24 TransformedY(int height) { return GetAbsYTransformed(@event, height); }
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_time", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetTime(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_base_event", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr GetBaseEvent(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_seat_key_count", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetSeatKeyCount(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_button", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetButton(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_seat_button_count", CallingConvention = CallingConvention.Cdecl)]
+ static extern uint GetButtonCount(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_button_state", CallingConvention = CallingConvention.Cdecl)]
+ static extern ButtonState GetButtonState(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_axis", CallingConvention = CallingConvention.Cdecl)]
+ static extern PointerAxis GetAxis(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_axis_value", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetAxisValue(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_dx", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetDX(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_dy", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetDY(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_absolute_x", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetAbsX(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_absolute_y", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetAbsY(IntPtr @event);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_absolute_x_transformed", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetAbsXTransformed(IntPtr @event, int width);
+
+ [DllImport(LibInput.lib, EntryPoint = "libinput_event_pointer_get_absolute_y_transformed", CallingConvention = CallingConvention.Cdecl)]
+ static extern Fixed24 GetAbsYTransformed(IntPtr @event, int height);
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Libc.cs b/Source/OpenTK/Platform/Linux/Bindings/Libc.cs
new file mode 100644
index 00000000..db026a77
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Libc.cs
@@ -0,0 +1,178 @@
+#region License
+//
+// Linux.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.Runtime.InteropServices;
+using System.Text;
+
+namespace OpenTK.Platform.Linux
+{
+ partial class Libc
+ {
+ const string lib = "libc";
+
+ [DllImport(lib)]
+ public static extern int dup(int file);
+
+ [DllImport(lib)]
+ public static extern int dup2(int file1, int file2);
+
+ [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 ioctl(int d, KeyboardIoctlCode request, ref IntPtr data);
+
+ [DllImport(lib)]
+ public static extern int ioctl(int d, KeyboardIoctlCode request, int data);
+
+ [DllImport(lib)]
+ public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags);
+
+ [DllImport(lib)]
+ public static extern int open(IntPtr 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);
+
+ public static int read(int fd, out byte b)
+ {
+ unsafe
+ {
+ fixed (byte* pb = &b)
+ {
+ return read(fd, pb, (UIntPtr)1).ToInt32();
+ }
+ }
+ }
+
+ public static int read(int fd, out short s)
+ {
+ unsafe
+ {
+ fixed (short* ps = &s)
+ {
+ return read(fd, ps, (UIntPtr)2).ToInt32();
+ }
+ }
+ }
+
+ [DllImport(lib)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool isatty(int fd);
+ }
+
+ enum ErrorNumber
+ {
+ Interrupted = 4,
+ Again = 11,
+ InvalidValue = 22,
+ }
+
+ [Flags]
+ enum OpenFlags
+ {
+ 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)
+ }
+
+ enum KeyboardIoctlCode
+ {
+ GetMode = 0x4b44,
+ SetMode = 0x4b45,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct Stat
+ {
+ public IntPtr dev; /* ID of device containing file */
+ public IntPtr ino; /* inode number */
+ public IntPtr mode; /* protection */
+ public IntPtr nlink; /* number of hard links */
+ public IntPtr uid; /* user ID of owner */
+ public IntPtr gid; /* group ID of owner */
+ public IntPtr rdev; /* device ID (if special file) */
+ public IntPtr size; /* total size, in bytes */
+ public IntPtr blksize; /* blocksize for file system I/O */
+ public IntPtr blocks; /* number of 512B blocks allocated */
+ public IntPtr atime; /* time of last access */
+ 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/Bindings/Poll.cs b/Source/OpenTK/Platform/Linux/Bindings/Poll.cs
new file mode 100644
index 00000000..f78d6d73
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Poll.cs
@@ -0,0 +1,65 @@
+#region License
+//
+// Poll.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.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ partial class Libc
+ {
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern int poll(ref PollFD fd, IntPtr fd_count, int timeout);
+
+ public static int poll(ref PollFD fd, int fd_count, int timeout)
+ {
+ return poll(ref fd, (IntPtr)fd_count, timeout);
+ }
+ }
+
+ [Flags]
+ enum PollFlags : short
+ {
+ In = 0x01,
+ Pri = 0x02,
+ Out = 0x04,
+ Error = 0x08,
+ Hup = 0x10,
+ Invalid = 0x20,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct PollFD
+ {
+ public int fd;
+ public PollFlags events;
+ public PollFlags revents;
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Terminal.cs b/Source/OpenTK/Platform/Linux/Bindings/Terminal.cs
new file mode 100644
index 00000000..e72ad741
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Terminal.cs
@@ -0,0 +1,170 @@
+#region License
+//
+// Terminal.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.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ class Terminal
+ {
+ const string lib = "libc";
+
+ [DllImport(lib, EntryPoint = "isatty", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I4)]
+ public static extern bool IsTerminal(int fd);
+
+ [DllImport(lib, EntryPoint = "tcgetattr", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int GetAttributes(int fd, out TerminalState state);
+
+ [DllImport(lib, EntryPoint = "tcsetattr", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int SetAttributes(int fd, OptionalActions actions, ref TerminalState state);
+ }
+
+ [Flags]
+ enum InputFlags
+ {
+ IGNBRK = 1 << 0,
+ BRKINT = 1 << 1,
+ IGNPAR = 1 << 2,
+ PARMRK = 1 << 3,
+ INPCK = 1 << 4,
+ ISTRIP = 1 << 5,
+ INLCR = 1 << 6,
+ IGNCR = 1 << 7,
+ ICRNL = 1 << 8,
+ IUCLC = 1 << 9,
+ IXON = 1 << 10,
+ IXANY = 1 << 11,
+ IXOFF = 1 << 12,
+ IMAXBEL = 1 << 13,
+ IUTF8 = 1 << 14,
+ }
+
+ [Flags]
+ enum OutputFlags
+ {
+ OPOST = 1 << 1,
+ OLCUC = 1 << 2,
+ ONLCR = 1 << 3,
+ OCRNL = 1 << 4,
+ ONOCR = 1 << 5,
+ ONLRET = 1 << 6,
+ OFILL = 1 << 7,
+ OFDEL = 1 << 8,
+ }
+
+ [Flags]
+ enum ControlFlags
+ {
+ B0 = 0, // hang up
+ B50,
+ B75,
+ B110,
+ B134,
+ B150,
+ B200,
+ B300,
+ B600,
+ B1200,
+ B1800,
+ B2400,
+ B4800,
+ B9600,
+ B19200,
+ B38400,
+ }
+
+ [Flags]
+ enum LocalFlags
+ {
+ ISIG = 0x01,
+ ICANON = 0x02,
+ ECHO = 0x08,
+ }
+
+ enum OptionalActions
+ {
+ NOW = 0,
+ DRAIN = 1,
+ FLUSH = 2
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct TerminalState
+ {
+ public InputFlags InputMode;
+ public OutputFlags OutputMode;
+ public ControlFlags ControlMode;
+ public LocalFlags LocalMode;
+ public byte LineDiscipline;
+ public ControlCharacters ControlCharacters;
+ public int InputSpeed;
+ public int OutputSpeed;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct ControlCharacters
+ {
+ public byte VINTR;
+ public byte VQUIT;
+ public byte VERASE;
+ public byte VKILL;
+ public byte VEOF;
+ public byte VTIME;
+ public byte VMIN;
+ public byte VSWTC;
+ public byte VSTART;
+ public byte VSTOP;
+ public byte VSUSP;
+ public byte VEOL;
+ public byte VREPRINT;
+ public byte VDISCARD;
+ public byte VWERASE;
+ public byte VLNEXT;
+ public byte VEOL2;
+ public byte C17;
+ public byte C18;
+ public byte C19;
+ public byte C20;
+ public byte C21;
+ public byte C22;
+ public byte C23;
+ public byte C24;
+ public byte C25;
+ public byte C26;
+ public byte C27;
+ public byte C28;
+ public byte C29;
+ public byte C30;
+ public byte C31;
+
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Udev.cs b/Source/OpenTK/Platform/Linux/Bindings/Udev.cs
new file mode 100644
index 00000000..4c341b70
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/Bindings/Udev.cs
@@ -0,0 +1,46 @@
+#region License
+//
+// Udev.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.Runtime.InteropServices;
+
+namespace OpenTK.Platform.Linux
+{
+ class Udev
+ {
+ const string lib = "libudev";
+
+ [DllImport(lib, EntryPoint = "udev_new", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr New();
+
+ [DllImport(lib, EntryPoint = "udev_destroy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Destroy(IntPtr Udev);
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/DefaultCursor.cs b/Source/OpenTK/Platform/Linux/DefaultCursor.cs
new file mode 100644
index 00000000..40e58e57
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/DefaultCursor.cs
@@ -0,0 +1,76 @@
+#region License
+//
+// DefaultCursor.cs
+//
+// Author:
+// Stefanos A.
+//
+// Copyright (c) 2006-2014
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+#endregion
+
+using System;
+
+namespace OpenTK.Platform.Linux
+{
+ static class Cursors
+ {
+ public static readonly MouseCursor Default =
+ new MouseCursor(8, 4, 32, 32, new byte[]
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1A, 0x1A, 0x1A, 0x1F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0xF1, 0xF1, 0xF1, 0xF3, 0x16, 0x16, 0x16, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xEC, 0xEC, 0xF4, 0x11, 0x11, 0x11, 0x37, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE9, 0xE9, 0xE9, 0xF3, 0x0C, 0x0C, 0x0C, 0x33, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0xFF, 0xF7, 0xF7, 0xF7, 0xFF, 0xE4, 0xE4, 0xE4, 0xF0, 0x07, 0x07, 0x07, 0x2D, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x61, 0x61, 0xFF, 0x52, 0x52, 0x52, 0xFF, 0xF9, 0xF9, 0xF9, 0xFF, 0xE0, 0xE0, 0xE0, 0xEC, 0x03, 0x03, 0x03, 0x29, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0x5F, 0x5F, 0xFF, 0x13, 0x13, 0x13, 0xFF, 0x5C, 0x5C, 0x5C, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, 0xD9, 0xD9, 0xD9, 0xE7, 0x01, 0x01, 0x01, 0x26, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0x5C, 0x5C, 0xFF, 0x10, 0x10, 0x10, 0xFF, 0x19, 0x19, 0x19, 0xFF, 0x66, 0x66, 0x66, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xD3, 0xD3, 0xD3, 0xE2, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x59, 0x59, 0x59, 0xFF, 0x0C, 0x0C, 0x0C, 0xFF, 0x15, 0x15, 0x15, 0xFF, 0x1E, 0x1E, 0x1E, 0xFF, 0x72, 0x72, 0x72, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xCA, 0xCA, 0xCA, 0xDB, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x08, 0x08, 0x08, 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x1A, 0x1A, 0x1A, 0xFF, 0x24, 0x24, 0x24, 0xFF, 0x7C, 0x7C, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xC1, 0xC1, 0xD4, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x04, 0x04, 0x04, 0xFF, 0x0D, 0x0D, 0x0D, 0xFF, 0x16, 0x16, 0x16, 0xFF, 0x20, 0x20, 0x20, 0xFF, 0x29, 0x29, 0x29, 0xFF, 0x88, 0x88, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB7, 0xB7, 0xB7, 0xCD, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x09, 0x09, 0x09, 0xFF, 0x12, 0x12, 0x12, 0xFF, 0x1C, 0x1C, 0x1C, 0xFF, 0x25, 0x25, 0x25, 0xFF, 0x2F, 0x2F, 0x2F, 0xFF, 0x92, 0x92, 0x92, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAD, 0xAD, 0xAD, 0xC5, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x05, 0x05, 0x05, 0xFF, 0x0E, 0x0E, 0x0E, 0xFF, 0x18, 0x18, 0x18, 0xFF, 0x21, 0x21, 0x21, 0xFF, 0x2B, 0x2B, 0x2B, 0xFF, 0x34, 0x34, 0x34, 0xFF, 0x9C, 0x9C, 0x9C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0xA1, 0xA1, 0xBC, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x01, 0x01, 0xFF, 0x0A, 0x0A, 0x0A, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x1D, 0x1D, 0x1D, 0xFF, 0x27, 0x27, 0x27, 0xFF, 0x30, 0x30, 0x30, 0xFF, 0x39, 0x39, 0x39, 0xFF, 0xA7, 0xA7, 0xA7, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x96, 0x96, 0x96, 0xB3, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x07, 0x07, 0xFF, 0x10, 0x10, 0x10, 0xFF, 0x19, 0x19, 0x19, 0xFF, 0x23, 0x23, 0x23, 0xFF, 0x2C, 0x2C, 0x2C, 0xFF, 0x35, 0x35, 0x35, 0xFF, 0x3F, 0x3F, 0x3F, 0xFF, 0xB0, 0xB0, 0xB0, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x8B, 0x8B, 0x8B, 0xAB, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x03, 0x03, 0x03, 0xFF, 0x0C, 0x0C, 0x0C, 0xFF, 0x15, 0x15, 0x15, 0xFF, 0x1F, 0x1F, 0x1F, 0xFF, 0x28, 0x28, 0x28, 0xFF, 0x31, 0x31, 0x31, 0xFF, 0x3B, 0x3B, 0x3B, 0xFF, 0x45, 0x45, 0x45, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x80, 0x80, 0x80, 0xA2, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x08, 0x08, 0x08, 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x1F, 0x1F, 0x1F, 0xFF, 0x61, 0x61, 0x61, 0xFF, 0x69, 0x69, 0x69, 0xFF, 0x6F, 0x6F, 0x6F, 0xFF, 0x76, 0x76, 0x76, 0xFF, 0x7D, 0x7D, 0x7D, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0x75, 0x75, 0x75, 0x97, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x04, 0x04, 0x04, 0xFF, 0x0D, 0x0D, 0x0D, 0xFF, 0x17, 0x17, 0x17, 0xFF, 0xCD, 0xCD, 0xCD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66, 0x85, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x5B, 0x5B, 0x5B, 0xFF, 0x10, 0x10, 0x10, 0xFF, 0x13, 0x13, 0x13, 0xFF, 0x61, 0x61, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x69, 0x69, 0x69, 0xBA, 0x1E, 0x1E, 0x1E, 0x83, 0x1E, 0x1E, 0x1E, 0x78, 0x1E, 0x1E, 0x1E, 0x77, 0x1E, 0x1E, 0x1E, 0x74, 0x1E, 0x1E, 0x1E, 0x6B, 0x15, 0x15, 0x15, 0x4A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x6D, 0x6D, 0x6D, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x6D, 0x6D, 0x6D, 0xFF, 0x0F, 0x0F, 0x0F, 0xFF, 0x19, 0x19, 0x19, 0xFF, 0xDE, 0xDE, 0xDE, 0xFF, 0xDB, 0xDB, 0xDB, 0xEE, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x75, 0x75, 0x75, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xF7, 0xF7, 0xFC, 0xE6, 0xE6, 0xE6, 0xFF, 0x10, 0x10, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x71, 0x71, 0x71, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x36, 0x36, 0x36, 0x75, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x58, 0x58, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xAF, 0xAF, 0xD9, 0x36, 0x36, 0x36, 0x8F, 0xFD, 0xFD, 0xFD, 0xFF, 0x67, 0x67, 0x67, 0xFF, 0x10, 0x10, 0x10, 0xFF, 0x1E, 0x1E, 0x1E, 0xFF, 0xEA, 0xEA, 0xEA, 0xFF, 0xCD, 0xCD, 0xCD, 0xE2, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0xCC, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0xA7, 0xA7, 0xD5, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x3A, 0xD5, 0xD5, 0xD5, 0xE3, 0xE1, 0xE1, 0xE1, 0xFF, 0x0F, 0x0F, 0x0F, 0xFF, 0x16, 0x16, 0x16, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, 0x20, 0x20, 0x20, 0x5E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x9F, 0x9F, 0x9F, 0xCF, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x12, 0x3D, 0x3D, 0x3D, 0x59, 0xFD, 0xFD, 0xFD, 0xFF, 0x61, 0x61, 0x61, 0xFF, 0x12, 0x12, 0x12, 0xFF, 0x25, 0x25, 0x25, 0xFF, 0xFA, 0xFA, 0xFA, 0xFF, 0x97, 0x97, 0x97, 0xC0, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0x97, 0x97, 0x97, 0xC1, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0xD9, 0xD9, 0xD9, 0xE5, 0xDC, 0xDC, 0xDC, 0xFF, 0x19, 0x19, 0x19, 0xFF, 0x34, 0x34, 0x34, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xA2, 0xA2, 0xA2, 0xCB, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x8E, 0x8E, 0x8E, 0xA1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x45, 0x45, 0x45, 0x5F, 0xFA, 0xFA, 0xFA, 0xFF, 0xF2, 0xF2, 0xF2, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xF2, 0xF2, 0xF2, 0xFC, 0x30, 0x30, 0x30, 0x83, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x34, 0x34, 0x34, 0x5D, 0xBB, 0xBB, 0xBB, 0xD5, 0x95, 0x95, 0x95, 0xC4, 0x16, 0x16, 0x16, 0x72, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ });
+
+ public static readonly MouseCursor Empty = MouseCursor.Empty;
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
new file mode 100644
index 00000000..d347a6aa
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
@@ -0,0 +1,413 @@
+#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
+{
+ // Stores platform-specific information about a display
+ class LinuxDisplay
+ {
+ public int FD;
+ public IntPtr Connector;
+ public IntPtr Crtc;
+ public IntPtr Encoder;
+
+ unsafe public ModeConnector* pConnector { get { return (ModeConnector*)Connector; } }
+ unsafe public ModeCrtc* pCrtc { get { return (ModeCrtc*)Crtc; } }
+ unsafe public ModeEncoder* pEncoder { get { return (ModeEncoder*)Encoder; } }
+ /*
+ public ModeInfo Mode
+ {
+ get
+ {
+ if (Crtc == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ unsafe
+ {
+ return pCrtc->mode;
+ }
+ }
+ }
+ */
+
+ public ModeInfo OriginalMode;
+
+ public int Id
+ {
+ get
+ {
+ if (Crtc == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ unsafe
+ {
+ return (int)pCrtc->crtc_id;
+ }
+ }
+ }
+
+ public LinuxDisplay(int fd, IntPtr c, IntPtr e, IntPtr r)
+ {
+ FD = fd;
+ Connector = c;
+ Encoder = e;
+ Crtc = r;
+ unsafe
+ {
+ OriginalMode = pCrtc->mode; // in case we change resolution later on
+ }
+ }
+ }
+
+ class LinuxDisplayDriver : DisplayDeviceBase
+ {
+ readonly int FD;
+ readonly Dictionary DisplayIds =
+ new Dictionary();
+
+ public LinuxDisplayDriver(int fd)
+ {
+ Debug.Print("[KMS] Creating LinuxDisplayDriver for fd:{0}", fd);
+ Debug.Indent();
+ try
+ {
+ FD = fd;
+ UpdateDisplays(fd);
+ }
+ finally
+ {
+ Debug.Unindent();
+ }
+ }
+
+ /// \internal
+ ///
+ /// Queries the specified GPU for connected displays and, optionally,
+ /// returns the list of displays.
+ ///
+ /// true, if at least one display is connected, false otherwise.
+ /// The fd for the GPU to query, obtained through open("/dev/dri/card0").
+ ///
+ /// If not null, this will contain a list instances,
+ /// one for each connected display.
+ ///
+ internal static bool QueryDisplays(int fd, List displays)
+ {
+ unsafe
+ {
+ bool has_displays = false;
+ if (displays != null)
+ {
+ displays.Clear();
+ }
+
+ ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd);
+ if (resources == null)
+ {
+ Debug.Print("[KMS] Drm.ModeGetResources failed.");
+ return false;
+ }
+ 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)
+ {
+ bool success = false;
+ LinuxDisplay display = null;
+ try
+ {
+ if (connector->connection == ModeConnection.Connected &&
+ connector->count_modes > 0)
+ {
+ success = QueryDisplay(fd, connector, out display);
+ has_displays |= success;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Print("[KMS] Failed to add display. Error: {0}", e);
+ }
+
+ if (success && displays != null)
+ {
+ displays.Add(display);
+ }
+ else
+ {
+ Drm.ModeFreeConnector((IntPtr)connector);
+ connector = null;
+ }
+ }
+ }
+
+ return has_displays;
+ }
+ }
+
+ void UpdateDisplays(int fd)
+ {
+ unsafe
+ {
+ lock (this)
+ {
+ AvailableDevices.Clear();
+ DisplayIds.Clear();
+
+ List displays = new List();
+ if (QueryDisplays(fd, displays))
+ {
+ foreach (LinuxDisplay display in displays)
+ {
+ AddDisplay(display);
+ }
+ }
+
+ if (AvailableDevices.Count == 0)
+ {
+ Debug.Print("[KMS] Failed to find any active displays");
+ }
+ }
+ }
+ }
+
+ unsafe static ModeEncoder* GetEncoder(int fd, ModeConnector* c)
+ {
+ 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 encoder;
+ }
+
+ unsafe static ModeCrtc* GetCrtc(int fd, ModeEncoder* encoder)
+ {
+ 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 crtc;
+ }
+
+ unsafe static void GetModes(LinuxDisplay display, DisplayResolution[] modes, out DisplayResolution current)
+ {
+ int mode_count = display.pConnector->count_modes;
+ Debug.Print("[KMS] Display supports {0} mode(s)", mode_count);
+ for (int i = 0; i < mode_count; i++)
+ {
+ ModeInfo* mode = display.pConnector->modes + i;
+ if (mode != null)
+ {
+ Debug.Print("Mode {0}: {1}x{2} @{3}", i,
+ mode->hdisplay, mode->vdisplay, mode->vrefresh);
+ DisplayResolution res = GetDisplayResolution(mode);
+ modes[i] = res;
+ }
+ }
+
+ if (display.pCrtc->mode_valid != 0)
+ {
+ ModeInfo cmode = display.pCrtc->mode;
+ current = GetDisplayResolution(&cmode);
+ }
+ else
+ {
+ current = GetDisplayResolution(display.pConnector->modes);
+ }
+ Debug.Print("Current mode: {0}", current.ToString());
+ }
+
+ System.Drawing.Rectangle GetBounds(DisplayResolution current)
+ {
+ // 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.
+ int x = AvailableDevices.Count == 0 ?
+ 0 : AvailableDevices[AvailableDevices.Count - 1].Bounds.Right;
+ int y = 0;
+
+ return new System.Drawing.Rectangle(
+ x, y, current.Width, current.Height);
+ }
+
+ void UpdateDisplayIndices(LinuxDisplay display, DisplayDevice device)
+ {
+ if (!DisplayIds.ContainsKey(display.Id))
+ {
+ Debug.Print("[KMS] Adding display {0} as {1}", display.Id, AvailableDevices.Count);
+ DisplayIds.Add(display.Id, AvailableDevices.Count);
+ }
+ int index = DisplayIds[display.Id];
+ if (index >= AvailableDevices.Count)
+ {
+ AvailableDevices.Add(device);
+ }
+ else
+ {
+ AvailableDevices[index] = device;
+ }
+ }
+
+ unsafe static bool QueryDisplay(int fd, ModeConnector* c, out LinuxDisplay display)
+ {
+ display = null;
+
+ // Find corresponding encoder
+ ModeEncoder* encoder = GetEncoder(fd, c);
+ if (encoder == null)
+ return false;
+
+ ModeCrtc* crtc = GetCrtc(fd, encoder);
+ if (crtc == null)
+ return false;
+
+ display = new LinuxDisplay(fd, (IntPtr)c, (IntPtr)encoder, (IntPtr)crtc);
+ return true;
+ }
+
+ unsafe void AddDisplay(LinuxDisplay display)
+ {
+ DisplayResolution[] modes = new DisplayResolution[display.pConnector->count_modes];
+ DisplayResolution current;
+ GetModes(display, modes, out current);
+
+ bool is_primary = AvailableDevices.Count == 0;
+ DisplayDevice device = new DisplayDevice(current, is_primary,
+ modes, GetBounds(current), display);
+
+ if (is_primary)
+ {
+ Primary = device;
+ }
+
+ UpdateDisplayIndices(display, device);
+
+ Debug.Print("[KMS] Added DisplayDevice {0}", device);
+ }
+
+ unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode)
+ {
+ return new DisplayResolution(
+ 0, 0,
+ mode->hdisplay, mode->vdisplay,
+ 32, // This is actually part of the framebuffer, not the DisplayResolution
+ mode->vrefresh);
+ }
+
+ unsafe static ModeInfo* GetModeInfo(LinuxDisplay display, DisplayResolution resolution)
+ {
+ for (int i = 0; i < display.pConnector->count_modes; i++)
+ {
+ ModeInfo* mode = display.pConnector->modes + i;
+ if (mode != null &&
+ mode->hdisplay == resolution.Width &&
+ mode->vdisplay == 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);
+ int connector_id = display.pConnector->connector_id;
+ if (mode != null)
+ {
+ return Drm.ModeSetCrtc(FD, display.Id, 0, 0, 0,
+ &connector_id, 1, mode) == 0;
+ }
+ return false;
+ }
+ }
+
+ public override bool TryRestoreResolution(DisplayDevice device)
+ {
+ unsafe
+ {
+ LinuxDisplay display = (LinuxDisplay)device.Id;
+ ModeInfo mode = display.OriginalMode;
+ int connector_id = display.pConnector->connector_id;
+ return Drm.ModeSetCrtc(FD, 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
new file mode 100644
index 00000000..21132c13
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxFactory.cs
@@ -0,0 +1,248 @@
+#region License
+//
+// LinuxFactory.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 System.IO;
+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;
+ IntPtr gbm_device;
+ IntPtr egl_display;
+
+ IJoystickDriver2 JoystickDriver;
+ LinuxInput MouseKeyboardDriver;
+
+ const string gpu_path = "/dev/dri"; // card0, card1, ...
+
+ public LinuxFactory()
+ {
+ Debug.Print("[KMS] Using Linux/KMS backend.");
+ }
+
+ #region Private Members
+
+ int gpu_fd
+ {
+ get
+ {
+ lock (this)
+ {
+ if (_fd == 0)
+ {
+ _fd = CreateDisplay(out gbm_device, out egl_display);
+ }
+ return _fd;
+ }
+ }
+ }
+
+ static int CreateDisplay(out IntPtr gbm_device, out IntPtr egl_display)
+ {
+ // Query all GPUs until we find one that has a connected display.
+ // This is necessary in multi-gpu systems, where only one GPU
+ // can output a signal.
+ // Todo: allow OpenTK to drive multiple GPUs
+ // Todo: allow OpenTK to run on an offscreen GPU
+ // Todo: allow the user to pick a GPU
+ int fd = 0;
+ gbm_device = IntPtr.Zero;
+ egl_display = IntPtr.Zero;
+
+ var files = Directory.GetFiles(gpu_path);
+ foreach (var gpu in files)
+ {
+ if (Path.GetFileName(gpu).StartsWith("card"))
+ {
+ int test_fd = SetupDisplay(gpu, out gbm_device, out egl_display);
+ if (test_fd >= 0)
+ {
+ try
+ {
+ if (LinuxDisplayDriver.QueryDisplays(test_fd, null))
+ {
+ fd = test_fd;
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ }
+
+ Debug.Print("[KMS] GPU '{0}' is not connected, skipping.", gpu);
+ Libc.close(test_fd);
+ }
+ }
+ }
+
+ if (fd == 0)
+ {
+ Debug.Print("[Error] No valid GPU found, bailing out.");
+ throw new PlatformNotSupportedException();
+ }
+
+ return fd;
+ }
+
+ static int SetupDisplay(string gpu, out IntPtr gbm_device, out IntPtr egl_display)
+ {
+ Debug.Print("[KMS] Attempting to use gpu '{0}'.", gpu);
+
+ gbm_device = IntPtr.Zero;
+ egl_display = IntPtr.Zero;
+
+ int fd = Libc.open(gpu, OpenFlags.ReadWrite | OpenFlags.CloseOnExec);
+ if (fd < 0)
+ {
+ Debug.Print("[KMS] Failed to open gpu");
+ return fd;
+ }
+ Debug.Print("[KMS] GPU '{0}' opened as fd:{1}", gpu, fd);
+
+ gbm_device = Gbm.CreateDevice(fd);
+ if (gbm_device == IntPtr.Zero)
+ {
+ throw new NotSupportedException("[KMS] Failed to create GBM device");
+ }
+ Debug.Print("[KMS] GBM {0:x} created successfully; ", gbm_device);
+
+ egl_display = Egl.GetDisplay(gbm_device);
+ if (egl_display == IntPtr.Zero)
+ {
+ throw new NotSupportedException("[KMS] Failed to create EGL display");
+ }
+ Debug.Print("[KMS] EGL display {0:x} created successfully", egl_display);
+
+ int major, minor;
+ if (!Egl.Initialize(egl_display, out major, out minor))
+ {
+ ErrorCode error = Egl.GetError();
+ throw new NotSupportedException("[KMS] Failed to initialize EGL display. Error code: " + error);
+ }
+ Debug.Print("[KMS] EGL {0}.{1} initialized successfully on display {2:x}", major, minor, egl_display);
+
+ return fd;
+ }
+
+ #endregion
+
+ #region Protected Members
+
+ protected override void Dispose(bool manual)
+ {
+ if (egl_display != IntPtr.Zero)
+ {
+ Debug.Print("[KMS] Terminating EGL.");
+ Egl.Terminate(egl_display);
+ egl_display = IntPtr.Zero;
+ }
+ if (gbm_device != IntPtr.Zero)
+ {
+ Debug.Print("[KMS] Destroying GBM device.");
+ Gbm.DestroyDevice(gbm_device);
+ gbm_device = IntPtr.Zero;
+ }
+ if (_fd >= 0)
+ {
+ Debug.Print("[KMS] Closing GPU fd.");
+ Libc.close(_fd);
+ }
+
+ base.Dispose(manual);
+ }
+
+ #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(egl_display, gbm_device, gpu_fd, x, y, width, height, title, mode, options, display_device);
+ }
+
+ public override IDisplayDeviceDriver CreateDisplayDeviceDriver()
+ {
+ return new LinuxDisplayDriver(gpu_fd);
+ }
+
+ public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags)
+ {
+ return new LinuxGraphicsContext(mode, (LinuxWindowInfo)window, shareContext, major, minor, flags);
+ }
+
+ public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext()
+ {
+ return (GraphicsContext.GetCurrentContextDelegate)delegate
+ {
+ return new ContextHandle(Egl.GetCurrentContext());
+ };
+ }
+
+ public override IKeyboardDriver2 CreateKeyboardDriver()
+ {
+ lock (this)
+ {
+ MouseKeyboardDriver = MouseKeyboardDriver ?? new LinuxInput();
+ return MouseKeyboardDriver;
+ }
+ }
+
+ public override IMouseDriver2 CreateMouseDriver()
+ {
+ lock (this)
+ {
+ MouseKeyboardDriver = MouseKeyboardDriver ?? new LinuxInput();
+ return MouseKeyboardDriver;
+ }
+ }
+
+ public override IJoystickDriver2 CreateJoystickDriver()
+ {
+ lock (this)
+ {
+ JoystickDriver = JoystickDriver ?? new LinuxJoystick();
+ return JoystickDriver;
+ }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
new file mode 100644
index 00000000..d6959544
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
@@ -0,0 +1,303 @@
+#region License
+//
+// LinuxGraphicsContext.cs
+//
+// Author:
+// thefiddler
+//
+// Copyright (c) 2006-2014
+//
+// 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 System.Runtime.InteropServices;
+using OpenTK.Graphics;
+
+namespace OpenTK.Platform.Linux
+{
+ /// \internal
+ ///
+ /// Defines an IGraphicsContext implementation for the Linux KMS framebuffer.
+ /// For Linux/X11 and other Unix operating systems, use the more generic
+ /// instead.
+ ///
+ ///
+ /// Note: to display our results, we need to allocate a GBM framebuffer
+ /// and point the scanout address to that via Drm.ModeSetCrtc.
+ ///
+ class LinuxGraphicsContext : Egl.EglUnixContext
+ {
+ BufferObject bo, bo_next;
+ int fd;
+ bool is_flip_queued;
+ int swap_interval;
+
+ public LinuxGraphicsContext(GraphicsMode mode, LinuxWindowInfo window, IGraphicsContext sharedContext,
+ int major, int minor, GraphicsContextFlags flags)
+ : base(mode, window, sharedContext, major, minor, flags)
+ {
+ if (mode.Buffers < 1)
+ throw new ArgumentException();
+ fd = window.FD;
+
+ PageFlip = HandlePageFlip;
+ PageFlipPtr = Marshal.GetFunctionPointerForDelegate(PageFlip);
+ }
+
+ public override void SwapBuffers()
+ {
+ base.SwapBuffers();
+
+ bo_next = LockSurface();
+ int fb = GetFramebuffer(bo_next);
+
+ if (is_flip_queued)
+ {
+ // Todo: if we don't wait for the page flip,
+ // we drop all rendering buffers and get a crash
+ // in Egl.SwapBuffers(). We need to fix that
+ // before we can disable vsync.
+ WaitFlip(true); // WaitFlip(SwapInterval > 0)
+ if (is_flip_queued)
+ {
+ Debug.Print("[KMS] Dropping frame");
+ return;
+ }
+ }
+
+ QueueFlip(fb);
+ }
+
+ public override void Update(IWindowInfo window)
+ {
+ WaitFlip(true);
+
+ base.SwapBuffers();
+
+ bo = LockSurface();
+ int fb = GetFramebuffer(bo);
+ SetScanoutRegion(fb);
+ }
+
+ public override int SwapInterval
+ {
+ get
+ {
+ return swap_interval;
+ }
+ set
+ {
+ // We only support a SwapInterval of 0 (immediate)
+ // or 1 (vsynced).
+ // Todo: add support for SwapInterval of -1 (adaptive).
+ // This requires a small change in WaitFlip().
+ swap_interval = MathHelper.Clamp(value, 0, 1);
+ }
+ }
+
+ void WaitFlip(bool block)
+ {
+ PollFD fds = new PollFD();
+ fds.fd = fd;
+ fds.events = PollFlags.In;
+
+ EventContext evctx = new EventContext();
+ evctx.version = EventContext.Version;
+ evctx.page_flip_handler = PageFlipPtr;
+
+ int timeout = block ? -1 : 0;
+
+ while (is_flip_queued)
+ {
+ fds.revents = 0;
+ if (Libc.poll(ref fds, 1, timeout) < 0)
+ break;
+
+ if ((fds.revents & (PollFlags.Hup | PollFlags.Error)) != 0)
+ break;
+
+ if ((fds.revents & PollFlags.In) != 0)
+ Drm.HandleEvent(fd, ref evctx);
+ else
+ break;
+ }
+
+ // Page flip has taken place, update buffer objects
+ if (!is_flip_queued)
+ {
+ IntPtr gbm_surface = WindowInfo.Handle;
+ Gbm.ReleaseBuffer(gbm_surface, bo);
+ bo = bo_next;
+ }
+ }
+
+ void QueueFlip(int buffer)
+ {
+ LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo;
+ if (wnd == null)
+ throw new InvalidOperationException();
+
+ unsafe
+ {
+ int ret = Drm.ModePageFlip(fd, wnd.DisplayDevice.Id, buffer,
+ PageFlipFlags.FlipEvent, IntPtr.Zero);
+
+ if (ret < 0)
+ {
+ Debug.Print("[KMS] Failed to enqueue framebuffer flip. Error: {0}", ret);
+ }
+
+ is_flip_queued = true;
+ }
+ }
+
+ void SetScanoutRegion(int buffer)
+ {
+ LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo;
+ if (wnd == null)
+ throw new InvalidOperationException();
+
+ unsafe
+ {
+ ModeInfo* mode = wnd.DisplayDevice.pConnector->modes;
+ int connector_id = wnd.DisplayDevice.pConnector->connector_id;
+ int crtc_id = wnd.DisplayDevice.Id;
+
+ int x = 0;
+ int y = 0;
+ int connector_count = 1;
+ int ret = Drm.ModeSetCrtc(fd, crtc_id, buffer, x, y,
+ &connector_id, connector_count, mode);
+
+ if (ret != 0)
+ {
+ Debug.Print("[KMS] Drm.ModeSetCrtc{0}, {1}, {2}, {3}, {4:x}, {5}, {6:x}) failed. Error: {7}",
+ fd, crtc_id, buffer, x, y, (IntPtr)connector_id, connector_count, (IntPtr)mode, ret);
+ }
+ }
+ }
+
+ BufferObject LockSurface()
+ {
+ IntPtr gbm_surface = WindowInfo.Handle;
+ return Gbm.LockFrontBuffer(gbm_surface);
+ }
+
+ int GetFramebuffer(BufferObject bo)
+ {
+ if (bo == BufferObject.Zero)
+ goto fail;
+
+ int bo_handle = bo.Handle;
+ if (bo_handle == 0)
+ {
+ Debug.Print("[KMS] Gbm.BOGetHandle({0:x}) failed.", bo);
+ goto fail;
+ }
+
+ int width = bo.Width;
+ int height = bo.Height;
+ int bpp = Mode.ColorFormat.BitsPerPixel;
+ int depth = Mode.Depth;
+ int stride = bo.Stride;
+
+ if (width == 0 || height == 0 || bpp == 0)
+ {
+ Debug.Print("[KMS] Invalid framebuffer format: {0}x{1} {2} {3} {4}",
+ width, height, stride, bpp, depth);
+ goto fail;
+ }
+
+ int buffer;
+ int ret = Drm.ModeAddFB(
+ fd, width, height,
+ (byte)depth, (byte)bpp, stride, bo_handle,
+ out buffer);
+ if (ret != 0)
+ {
+ Debug.Print("[KMS] Drm.ModeAddFB({0}, {1}, {2}, {3}, {4}, {5}, {6}) failed. Error: {7}",
+ fd, width, height, depth, bpp, stride, bo_handle, ret);
+ goto fail;
+ }
+
+ bo.SetUserData((IntPtr)buffer, DestroyFB);
+ return buffer;
+
+ fail:
+ Debug.Print("[Error] Failed to create framebuffer.");
+ return -1;
+ }
+
+ readonly IntPtr PageFlipPtr;
+ readonly PageFlipCallback PageFlip;
+ void HandlePageFlip(int fd,
+ int sequence,
+ int tv_sec,
+ int tv_usec,
+ IntPtr user_data)
+ {
+ is_flip_queued = false;
+ }
+
+ static readonly DestroyUserDataCallback DestroyFB = HandleDestroyFB;
+ static void HandleDestroyFB(BufferObject bo, IntPtr data)
+ {
+ IntPtr gbm = bo.Device;
+ int fb = data.ToInt32();
+ Debug.Print("[KMS] Destroying framebuffer {0}", fb);
+
+ if (fb != 0)
+ {
+ Drm.ModeRmFB(Gbm.DeviceGetFD(gbm), fb);
+ }
+ }
+
+ protected override void Dispose(bool manual)
+ {
+ if (manual)
+ {
+ // Reset the scanout region
+ LinuxWindowInfo wnd = WindowInfo as LinuxWindowInfo;
+ if (wnd != null)
+ {
+ unsafe
+ {
+ int connector_id = wnd.DisplayDevice.pConnector->connector_id;
+ ModeInfo mode = wnd.DisplayDevice.OriginalMode;
+ Drm.ModeSetCrtc(fd,
+ wnd.DisplayDevice.pCrtc->crtc_id,
+ wnd.DisplayDevice.pCrtc->buffer_id,
+ wnd.DisplayDevice.pCrtc->x,
+ wnd.DisplayDevice.pCrtc->y,
+ &connector_id,
+ 1,
+ &mode);
+ }
+ }
+ }
+ base.Dispose(manual);
+ }
+ }
+}
+
+
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxInput.cs b/Source/OpenTK/Platform/Linux/LinuxInput.cs
new file mode 100644
index 00000000..ddca85f5
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxInput.cs
@@ -0,0 +1,717 @@
+#region License
+//
+// LinuxKeyboardLibInput.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 System.Drawing;
+using System.Runtime.InteropServices;
+using System.Threading;
+using OpenTK.Input;
+
+namespace OpenTK.Platform.Linux
+{
+ class LinuxInput : IKeyboardDriver2, IMouseDriver2, IDisposable
+ {
+ class DeviceBase
+ {
+ readonly IntPtr Device;
+ string name;
+ string output;
+ string logical_seat;
+ string physical_seat;
+
+ public DeviceBase(IntPtr device, int id)
+ {
+ Device = device;
+ Id = id;
+ }
+
+ public int Id
+ {
+ get
+ {
+ return GetId(Device);
+ }
+ set
+ {
+ LibInput.DeviceSetData(Device, (IntPtr)value);
+ }
+ }
+
+ public string Name
+ {
+ get
+ {
+ name = name ?? LibInput.DeviceGetName(Device);
+ return name;
+ }
+ }
+
+ public IntPtr Seat
+ {
+ get
+ {
+ return LibInput.DeviceGetSeat(Device);
+ }
+ }
+
+ public string LogicalSeatName
+ {
+ get
+ {
+ logical_seat = logical_seat ?? LibInput.SeatGetLogicalName(Seat);
+ return logical_seat;
+ }
+ }
+
+ public string PhysicalSeatName
+ {
+ get
+ {
+ physical_seat = physical_seat ?? LibInput.SeatGetPhysicalName(Seat);
+ return physical_seat;
+ }
+ }
+
+ public string Output
+ {
+ get
+ {
+ output = output ?? LibInput.DeviceGetOutputName(Device);
+ return output;
+ }
+ }
+ }
+
+ class KeyboardDevice : DeviceBase
+ {
+ public KeyboardState State;
+
+ public KeyboardDevice(IntPtr device, int id)
+ : base(device, id)
+ {
+ }
+ }
+
+ class MouseDevice : DeviceBase
+ {
+ public MouseState State;
+
+ public MouseDevice(IntPtr device, int id)
+ : base(device, id)
+ {
+ }
+ }
+
+ static readonly object Sync = new object();
+ static readonly Key[] KeyMap = Evdev.KeyMap;
+ static long DeviceFDCount;
+
+ // libinput returns various devices with keyboard/pointer even though
+ // they are not traditional keyboards/mice (for example "Integrated Camera"
+ // can be detected as a keyboard.)
+ // Since there is no API to retrieve actual device capabilities,
+ // we add all detected devices to a "candidate" list and promote them
+ // to an actual keyboard/mouse only when we receive a valid input event.
+ // This is far from optimal, but it appears to be the only viable solution
+ // unless a new API is added to libinput.
+ DeviceCollection KeyboardCandidates = new DeviceCollection();
+ DeviceCollection MouseCandidates = new DeviceCollection();
+ DeviceCollection Keyboards = new DeviceCollection();
+ DeviceCollection Mice = new DeviceCollection();
+
+ // Todo: do we need to maintain the geometry of each display separately?
+ Rectangle bounds;
+
+ // Global mouse cursor state
+ Vector2 CursorPosition = Vector2.Zero;
+ // Global mouse cursor offset (used for emulating SetPosition)
+ Vector2 CursorOffset = Vector2.Zero;
+
+ IntPtr udev;
+ IntPtr input_context;
+ InputInterface input_interface = new InputInterface(
+ OpenRestricted, CloseRestricted);
+ int fd;
+ Thread input_thread;
+ long exit;
+
+ public LinuxInput()
+ {
+ Debug.Print("[Linux] Initializing {0}", GetType().Name);
+ Debug.Indent();
+ try
+ {
+ Semaphore ready = new Semaphore(0, 1);
+ input_thread = new Thread(InputThreadLoop);
+ input_thread.IsBackground = true;
+ input_thread.Start(ready);
+
+ // Wait until the input thread is ready.
+ // Note: it would be nicer if we could avoid this.
+ // however we need to marshal errors back to the caller
+ // as exceptions.
+ // Todo: in a future version, we should add an "Application" object
+ // to handle all communication with the OS (including event processing.)
+ // Once we do that, we can remove all separate input threads.
+ ready.WaitOne();
+ if (exit != 0)
+ {
+ throw new NotSupportedException();
+ }
+ }
+ finally
+ {
+ Debug.Print("Initialization {0}", exit == 0 ?
+ "complete" : "failed");
+ Debug.Unindent();
+ }
+ }
+
+ #region Private Members
+
+ static CloseRestrictedCallback CloseRestricted = CloseRestrictedHandler;
+ static void CloseRestrictedHandler(int fd, IntPtr data)
+ {
+ Debug.Print("[Input] Closing fd {0}", fd);
+ int ret = Libc.close(fd);
+
+ if (ret < 0)
+ {
+ Debug.Print("[Input] Failed to close fd {0}. Error: {1}", fd, ret);
+ }
+ else
+ {
+ Interlocked.Decrement(ref DeviceFDCount);
+ }
+ }
+
+ static OpenRestrictedCallback OpenRestricted = OpenRestrictedHandler;
+ static int OpenRestrictedHandler(IntPtr path, int flags, IntPtr data)
+ {
+ int fd = Libc.open(path, (OpenFlags)flags);
+ Debug.Print("[Input] Opening '{0}' with flags {1}. fd:{2}",
+ Marshal.PtrToStringAnsi(path), (OpenFlags)flags, fd);
+
+ if (fd >= 0)
+ {
+ Interlocked.Increment(ref DeviceFDCount);
+ }
+
+ return fd;
+ }
+
+ void InputThreadLoop(object semaphore)
+ {
+ Debug.Print("[Input] Running on thread {0}", Thread.CurrentThread.ManagedThreadId);
+ Setup();
+
+ // Inform the parent thread that initialization has completed successfully
+ (semaphore as Semaphore).Release();
+ Debug.Print("[Input] Released main thread.", input_context);
+
+ // Use a blocking poll for input messages, in order to reduce CPU usage
+ PollFD poll_fd = new PollFD();
+ poll_fd.fd = fd;
+ poll_fd.events = PollFlags.In;
+ Debug.Print("[Input] Created PollFD({0}, {1})", poll_fd.fd, poll_fd.events);
+
+ Debug.Print("[Input] Entering input loop.", poll_fd.fd, poll_fd.events);
+ while (Interlocked.Read(ref exit) == 0)
+ {
+ int ret = Libc.poll(ref poll_fd, 1, -1);
+ ErrorNumber error = (ErrorNumber)Marshal.GetLastWin32Error();
+ bool is_error =
+ ret < 0 && !(error == ErrorNumber.Again || error == ErrorNumber.Interrupted) ||
+ (poll_fd.revents & (PollFlags.Hup | PollFlags.Error | PollFlags.Invalid)) != 0;
+
+ // We need to query the desktop bounds in order to position the mouse cursor correctly.
+ // This value will be used for the current bunch of input events. If a monitor changes
+ // resolution in the meantime, we might be slightly off in our calculations - this error
+ // will be corrected when the next bunch of input events arrives.
+ UpdateDisplayBounds();
+
+ if (ret > 0 && (poll_fd.revents & (PollFlags.In | PollFlags.Pri)) != 0)
+ {
+ ProcessEvents(input_context);
+ }
+
+ if (is_error)
+ {
+ Debug.Print("[Input] Exiting input loop {0} due to poll error [ret:{1} events:{2}]. Error: {3}.",
+ input_thread.ManagedThreadId, ret, poll_fd.revents, error);
+ Interlocked.Increment(ref exit);
+ }
+ }
+ Debug.Print("[Input] Exited input loop.", poll_fd.fd, poll_fd.events);
+ }
+
+ void UpdateDisplayBounds()
+ {
+ bounds = Rectangle.Empty;
+ for (DisplayIndex i = DisplayIndex.First; i < DisplayIndex.Sixth; i++)
+ {
+ DisplayDevice display = DisplayDevice.GetDisplay(i);
+ if (display != null)
+ {
+ bounds = Rectangle.Union(bounds, display.Bounds);
+ }
+ }
+ }
+
+ void UpdateCursor()
+ {
+ Point p = new Point(
+ (int)Math.Round(CursorPosition.X + CursorOffset.X),
+ (int)Math.Round(CursorPosition.Y + CursorOffset.Y));
+
+ DisplayDevice display = DisplayDevice.FromPoint(p.X, p.Y) ?? DisplayDevice.Default;
+ if (display != null)
+ {
+ LinuxDisplay d = (LinuxDisplay)display.Id;
+ Drm.MoveCursor(d.FD, d.Id, p.X, p.Y);
+ }
+ }
+
+ void Setup()
+ {
+ // Todo: add static path fallback when udev is not installed.
+ udev = Udev.New();
+ if (udev == IntPtr.Zero)
+ {
+ Debug.Print("[Input] Udev.New() failed.");
+ Interlocked.Increment(ref exit);
+ return;
+ }
+ Debug.Print("[Input] Udev.New() = {0:x}", udev);
+
+ input_context = LibInput.CreateContext(input_interface, IntPtr.Zero, udev, "seat0");
+ if (input_context == IntPtr.Zero)
+ {
+ Debug.Print("[Input] LibInput.CreateContext({0:x}) failed.", udev);
+ Interlocked.Increment(ref exit);
+ return;
+ }
+ Debug.Print("[Input] LibInput.CreateContext({0:x}) = {1:x}", udev, input_context);
+
+ fd = LibInput.GetFD(input_context);
+ if (fd < 0)
+ {
+ Debug.Print("[Input] LibInput.GetFD({0:x}) failed.", input_context);
+ Interlocked.Increment(ref exit);
+ return;
+ }
+ Debug.Print("[Input] LibInput.GetFD({0:x}) = {1}.", input_context, fd);
+
+ ProcessEvents(input_context);
+ LibInput.Resume(input_context);
+ Debug.Print("[Input] LibInput.Resume({0:x})", input_context);
+
+ if (Interlocked.Read(ref DeviceFDCount) <= 0)
+ {
+ Debug.Print("[Error] Failed to open any input devices.");
+ Debug.Print("[Error] Ensure that you have access to '/dev/input/event*'.");
+ Interlocked.Increment(ref exit);
+ }
+ }
+
+ void ProcessEvents(IntPtr input_context)
+ {
+ // Process all events in the event queue
+ while (true)
+ {
+ // Data available
+ int ret = LibInput.Dispatch(input_context);
+ if (ret != 0)
+ {
+ Debug.Print("[Input] LibInput.Dispatch({0:x}) failed. Error: {1}",
+ input_context, ret);
+ break;
+ }
+
+ IntPtr pevent = LibInput.GetEvent(input_context);
+ if (pevent == IntPtr.Zero)
+ {
+ break;
+ }
+
+ IntPtr device = LibInput.GetDevice(pevent);
+ InputEventType type = LibInput.GetEventType(pevent);
+
+ lock (Sync)
+ {
+ switch (type)
+ {
+ case InputEventType.DeviceAdded:
+ HandleDeviceAdded(input_context, device);
+ break;
+
+ case InputEventType.DeviceRemoved:
+ HandleDeviceRemoved(input_context, device);
+ break;
+
+ case InputEventType.KeyboardKey:
+ HandleKeyboard(GetKeyboard(device), LibInput.GetKeyboardEvent(pevent));
+ break;
+
+ case InputEventType.PointerAxis:
+ HandlePointerAxis(GetMouse(device), LibInput.GetPointerEvent(pevent));
+ break;
+
+ case InputEventType.PointerButton:
+ HandlePointerButton(GetMouse(device), LibInput.GetPointerEvent(pevent));
+ break;
+
+ case InputEventType.PointerMotion:
+ HandlePointerMotion(GetMouse(device), LibInput.GetPointerEvent(pevent));
+ break;
+
+ case InputEventType.PointerMotionAbsolute:
+ HandlePointerMotionAbsolute(GetMouse(device), LibInput.GetPointerEvent(pevent));
+ break;
+ }
+ }
+
+ LibInput.DestroyEvent(pevent);
+ }
+ }
+
+ void HandleDeviceAdded(IntPtr context, IntPtr device)
+ {
+ if (LibInput.DeviceHasCapability(device, DeviceCapability.Keyboard))
+ {
+ KeyboardDevice keyboard = new KeyboardDevice(device, Keyboards.Count);
+ KeyboardCandidates.Add(keyboard.Id, keyboard);
+ Debug.Print("[Input] Added keyboard device {0} '{1}' on '{2}' ('{3}')",
+ keyboard.Id, keyboard.Name, keyboard.LogicalSeatName, keyboard.PhysicalSeatName);
+ }
+
+ if (LibInput.DeviceHasCapability(device, DeviceCapability.Mouse))
+ {
+ MouseDevice mouse = new MouseDevice(device, Mice.Count);
+ MouseCandidates.Add(mouse.Id, mouse);
+ Debug.Print("[Input] Added mouse device {0} '{1}' on '{2}' ('{3}')",
+ mouse.Id, mouse.Name, mouse.LogicalSeatName, mouse.PhysicalSeatName);
+ }
+
+ if (LibInput.DeviceHasCapability(device, DeviceCapability.Touch))
+ {
+ Debug.Print("[Input] Todo: touch device.");
+ }
+ }
+
+ void HandleDeviceRemoved(IntPtr context, IntPtr device)
+ {
+ if (LibInput.DeviceHasCapability(device, DeviceCapability.Keyboard))
+ {
+ int id = GetId(device);
+ Keyboards.TryRemove(id);
+ KeyboardCandidates.TryRemove(id);
+ }
+
+ if (LibInput.DeviceHasCapability(device, DeviceCapability.Mouse))
+ {
+ int id = GetId(device);
+ Mice.TryRemove(id);
+ MouseCandidates.TryRemove(id);
+ }
+ }
+
+ void HandleKeyboard(KeyboardDevice device, KeyboardEvent e)
+ {
+ if (device != null)
+ {
+ device.State.SetIsConnected(true);
+ Debug.Print("[Input] Added keyboard {0}", device.Id);
+
+ Key key = Key.Unknown;
+ uint raw = e.Key;
+ if (raw >= 0 && raw < KeyMap.Length)
+ {
+ key = KeyMap[raw];
+ }
+
+ if (key == Key.Unknown)
+ {
+ Debug.Print("[Linux] Unknown key with code '{0}'", raw);
+ }
+
+ device.State.SetKeyState(key, e.KeyState == KeyState.Pressed);
+ }
+ }
+
+ void HandlePointerAxis(MouseDevice mouse, PointerEvent e)
+ {
+ if (mouse != null)
+ {
+ mouse.State.SetIsConnected(true);
+
+ double value = e.AxisValue;
+ PointerAxis axis = e.Axis;
+ switch (axis)
+ {
+ case PointerAxis.HorizontalScroll:
+ mouse.State.SetScrollRelative((float)value, 0);
+ break;
+
+ case PointerAxis.VerticalScroll:
+ mouse.State.SetScrollRelative(0, (float)value);
+ break;
+
+ default:
+ Debug.Print("[Input] Unknown scroll axis {0}.", axis);
+ break;
+ }
+ }
+ }
+
+ void HandlePointerButton(MouseDevice mouse, PointerEvent e)
+ {
+ if (mouse != null)
+ {
+ mouse.State.SetIsConnected(true);
+
+ MouseButton button = Evdev.GetMouseButton(e.Button);
+ ButtonState state = e.ButtonState;
+ mouse.State[(MouseButton)button] = state == ButtonState.Pressed;
+ }
+ }
+
+ void HandlePointerMotion(MouseDevice mouse, PointerEvent e)
+ {
+ Vector2 delta = new Vector2((float)e.X, (float)e.Y);
+ if (mouse != null)
+ {
+ mouse.State.SetIsConnected(true);
+ mouse.State.Position += delta;
+ }
+
+ CursorPosition = new Vector2(
+ MathHelper.Clamp(CursorPosition.X + delta.X, bounds.Left, bounds.Right - 1),
+ MathHelper.Clamp(CursorPosition.Y + delta.Y, bounds.Top, bounds.Bottom - 1));
+ UpdateCursor();
+ }
+
+ void HandlePointerMotionAbsolute(MouseDevice mouse, PointerEvent e)
+ {
+ if (mouse != null)
+ {
+ mouse.State.SetIsConnected(true);
+ mouse.State.Position = new Vector2(e.X, e.Y);
+ }
+
+ CursorPosition = new Vector2(
+ e.TransformedX(bounds.Width),
+ e.TransformedY(bounds.Height));
+ UpdateCursor();
+ }
+
+ static int GetId(IntPtr device)
+ {
+ return LibInput.DeviceGetData(device).ToInt32();
+ }
+
+ KeyboardDevice GetKeyboard(IntPtr device)
+ {
+ int id = GetId(device);
+ KeyboardDevice keyboard = KeyboardCandidates.FromHardwareId(id);
+ if (keyboard != null)
+ {
+ Keyboards.Add(id, keyboard);
+ }
+ else
+ {
+ Debug.Print("[Input] Keyboard {0} does not exist in device list.", id);
+ }
+ return keyboard;
+ }
+
+ MouseDevice GetMouse(IntPtr device)
+ {
+ int id = GetId(device);
+ MouseDevice mouse = MouseCandidates.FromHardwareId(id);
+ if (mouse != null)
+ {
+ Mice.Add(id, mouse);
+ }
+ else
+ {
+ Debug.Print("[Input] Mouse {0} does not exist in device list.", id);
+ }
+ return mouse;
+ }
+
+ #endregion
+
+ #region IKeyboardDriver2 implementation
+
+ KeyboardState IKeyboardDriver2.GetState()
+ {
+ lock (Sync)
+ {
+ KeyboardState state = new KeyboardState();
+ foreach (KeyboardDevice keyboard in Keyboards)
+ {
+ state.MergeBits(keyboard.State);
+ }
+ return state;
+ }
+ }
+
+ KeyboardState IKeyboardDriver2.GetState(int index)
+ {
+ lock (Sync)
+ {
+ KeyboardDevice device = Keyboards.FromIndex(index);
+ if (device != null)
+ {
+ return device.State;
+ }
+ else
+ {
+ return new KeyboardState();
+ }
+ }
+ }
+
+ string IKeyboardDriver2.GetDeviceName(int index)
+ {
+ lock (Sync)
+ {
+ KeyboardDevice device = Keyboards.FromIndex(index);
+ if (device != null)
+ {
+ return device.Name;
+ }
+ else
+ {
+ return String.Empty;
+ }
+ }
+ }
+
+ #endregion
+
+ #region IMouseDriver2 implementation
+
+ MouseState IMouseDriver2.GetState()
+ {
+ lock (Sync)
+ {
+ MouseState state = new MouseState();
+ foreach (MouseDevice mouse in Mice)
+ {
+ state.MergeBits(mouse.State);
+ }
+ return state;
+ }
+ }
+
+ MouseState IMouseDriver2.GetState(int index)
+ {
+ lock (Sync)
+ {
+ MouseDevice device = Mice.FromIndex(index);
+ if (device != null)
+ {
+ return device.State;
+ }
+ else
+ {
+ return new MouseState();
+ }
+ }
+ }
+
+ void IMouseDriver2.SetPosition(double x, double y)
+ {
+ // Todo: this does not appear to be supported in libinput.
+ // We will have to emulate this in the KMS mouse rendering code.
+ CursorOffset = new Vector2(
+ (float)x - CursorPosition.X,
+ (float)y - CursorPosition.Y);
+ UpdateCursor();
+ }
+
+ MouseState IMouseDriver2.GetCursorState()
+ {
+ MouseState state = (this as IMouseDriver2).GetState();
+ state.Position = CursorPosition + CursorOffset;
+ return state;
+ }
+
+ #endregion
+
+ #region IDisposable implementation
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (input_context != IntPtr.Zero)
+ {
+ Debug.Print("[Input] Destroying libinput context");
+ LibInput.Suspend(input_context);
+ Interlocked.Increment(ref exit);
+
+ LibInput.DestroyContext(input_context);
+ input_context = IntPtr.Zero;
+ }
+
+ if (udev != IntPtr.Zero)
+ {
+ Debug.Print("[Input] Destroying udev context");
+ Udev.Destroy(udev);
+ udev = IntPtr.Zero;
+ }
+
+ input_interface = null;
+ }
+ else
+ {
+ Debug.Print("[Input] {0} leaked. Did you forget to call Dispose()?", GetType().FullName);
+ }
+ }
+
+ ~LinuxInput()
+ {
+ Dispose(false);
+ }
+
+ #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/LinuxKeyboardTTY.cs b/Source/OpenTK/Platform/Linux/LinuxKeyboardTTY.cs
new file mode 100644
index 00000000..afe66bc1
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxKeyboardTTY.cs
@@ -0,0 +1,268 @@
+#region License
+//
+// LinuxKeyboardTTY.cs
+//
+// Author:
+// thefiddler
+//
+// Copyright (c) 2006-2014
+//
+// 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 System.IO;
+using System.Threading;
+using OpenTK.Input;
+
+namespace OpenTK.Platform.Linux
+{
+ // Todo: this has terrible side-effects on process exit
+ // (the keyboard remains tied up.) We need to find a
+ // proper way to clean up after ourselves, even in case
+ // of a crash.
+ #if EXPERIMENTAL
+ class LinuxKeyboardTTY : IKeyboardDriver2, IDisposable
+ {
+ const int stdin = 0; // STDIN_FILENO
+ readonly object sync = new object();
+ Thread input_thread;
+ long exit;
+ KeyboardState state;
+
+ TerminalState original_state;
+ TerminalState current_state;
+
+ IntPtr original_mode = new IntPtr(-1);
+ int original_stdin;
+
+ public LinuxKeyboardTTY()
+ {
+ Debug.Print("[Linux] Using TTY keyboard input.");
+
+ if (!SetupTTY(stdin))
+ {
+ throw new NotSupportedException();
+ }
+
+ input_thread = new Thread(ProcessEvents);
+ input_thread.IsBackground = true;
+ input_thread.Start();
+ }
+
+ #region Private Members
+
+ bool SetupTTY(int stdin)
+ {
+ // Ensure that we are using a real terminal,
+ // rather than some short of file redirection.thing.
+ if (!Terminal.IsTerminal(stdin))
+ {
+ Debug.Print("[Linux] Terminal.IsTerminal({0}) returned false.", stdin);
+ return false;
+ }
+
+ //original_stdin = Libc.dup(stdin);
+
+ int ret = Terminal.GetAttributes(stdin, out original_state);
+ if (ret < 0)
+ {
+ Debug.Print("[Linux] Terminal.GetAttributes({0}) failed. Error: {1}",
+ stdin, ret);
+ return false;
+ }
+
+ // Retrieve current keyboard mode
+ ret = Libc.ioctl(stdin, KeyboardIoctlCode.GetMode, ref original_mode);
+ if (ret != 0)
+ {
+ Debug.Print("[Linux] Libc.ioctl({0}, KeyboardIoctlCode.GetMode) failed. Error: {1}",
+ stdin, ret);
+ return false;
+ }
+
+ // Update terminal state
+ current_state = original_state;
+ current_state.LocalMode &= ~(/*LocalFlags.ECHO |*/ LocalFlags.ICANON | LocalFlags.ISIG);
+ current_state.InputMode &= ~(
+ InputFlags.ISTRIP | InputFlags.IGNCR | InputFlags.ICRNL |
+ InputFlags.INLCR | InputFlags.IXOFF | InputFlags.IXON);
+ current_state.ControlCharacters.VMIN = 0;
+ current_state.ControlCharacters.VTIME = 0;
+ Terminal.SetAttributes(stdin, OptionalActions.FLUSH, ref current_state);
+
+ // Request keycodes
+ int mode = 0x02; // K_MEDIUMRAW
+ ret = Libc.ioctl(stdin, KeyboardIoctlCode.SetMode, mode);
+ if (ret != 0)
+ {
+ Debug.Print("[Linux] Libc.ioctl({0}, KeyboardIoctlCode.SetMode, {1}) failed. Error: {2}",
+ stdin, mode, ret);
+ ExitTTY(this, EventArgs.Empty);
+ return false;
+ }
+
+ // Ensure we reset the original keyboard/terminal state on exit,
+ // even if we crash.
+ HookEvents();
+
+ return true;
+ }
+
+ void ExitTTY(object sender, EventArgs e)
+ {
+ if (original_mode != new IntPtr(-1))
+ {
+ Debug.Print("[Linux] Exiting TTY keyboard input.");
+
+ Libc.ioctl(stdin, KeyboardIoctlCode.SetMode, ref original_mode);
+ Terminal.SetAttributes(stdin, OptionalActions.FLUSH, ref original_state);
+ original_mode = new IntPtr(-1);
+
+ UnhookEvents();
+ }
+ }
+
+ void HookEvents()
+ {
+ Process.GetCurrentProcess().Exited += ExitTTY;
+ Console.CancelKeyPress += ExitTTY;
+ }
+
+ void UnhookEvents()
+ {
+ Process.GetCurrentProcess().Exited -= ExitTTY;
+ Console.CancelKeyPress -= ExitTTY;
+ }
+
+ void ProcessEvents()
+ {
+ state.SetIsConnected(true);
+
+ while (Interlocked.Read(ref exit) == 0)
+ {
+ byte scancode;
+ short extended;
+
+ while (Libc.read(stdin, out scancode) > 0)
+ {
+ bool pressed = (scancode & 0x80) == 0;
+ int key = scancode & ~0x80;
+ KeyModifiers mods;
+ Debug.Print("{0}:{1} is {2}", key, (int)TranslateKey(key, out mods), pressed);
+
+ if (key == 0)
+ {
+ // This is an extended scancode, ignore
+ Libc.read(stdin, out extended);
+ }
+ else
+ {
+ lock (sync)
+ {
+ state[(Key)key] = pressed;
+ }
+ }
+
+ }
+ }
+
+ input_thread = null;
+ }
+
+ Key TranslateKey(int key, out KeyModifiers mods)
+ {
+ int k = MathHelper.Clamp((int)key, 0, KeyMap.Length);
+ Key result = KeyMap[k];
+ mods = 0;
+ mods |= (result == Key.AltLeft || result == Key.AltRight) ? KeyModifiers.Alt : 0;
+ mods |= (result == Key.ControlLeft || result == Key.ControlRight) ? KeyModifiers.Control : 0;
+ mods |= (result == Key.ShiftLeft || result == Key.ShiftRight) ? KeyModifiers.Shift : 0;
+ return KeyMap[k];
+ }
+
+ static readonly Key[] KeyMap = Evdev.KeyMap;
+
+ #endregion
+
+ #region IKeyboardDriver2 implementation
+
+ public KeyboardState GetState()
+ {
+ lock (this)
+ {
+ return state;
+ }
+ }
+
+ public KeyboardState GetState(int index)
+ {
+ lock (this)
+ {
+ if (index == 0)
+ return state;
+ else
+ return new KeyboardState();
+ }
+ }
+
+ public string GetDeviceName(int index)
+ {
+ if (index == 0)
+ return "Standard Input";
+ else
+ return String.Empty;
+ }
+
+ #endregion
+
+ #region IDisposable Implementation
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ void Dispose(bool disposing)
+ {
+ Interlocked.Increment(ref exit);
+
+ if (disposing)
+ {
+ ExitTTY(this, EventArgs.Empty);
+ }
+ else
+ {
+ Debug.Print("{0} leaked, did you forget to call Dispose()?", typeof(LinuxKeyboardTTY).FullName);
+ }
+ }
+
+ ~LinuxKeyboardTTY()
+ {
+ Dispose(false);
+ }
+
+ #endregion
+ }
+ #endif
+}
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs b/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs
new file mode 100644
index 00000000..961381ee
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxNativeWindow.cs
@@ -0,0 +1,549 @@
+#region License
+//
+// LinuxNativeWindow.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 System.Drawing;
+using System.Runtime.InteropServices;
+using OpenTK.Graphics;
+using OpenTK.Input;
+using OpenTK.Platform.Egl;
+
+namespace OpenTK.Platform.Linux
+{
+ using Egl = OpenTK.Platform.Egl.Egl;
+
+ class LinuxNativeWindow : NativeWindowBase
+ {
+ LinuxWindowInfo window;
+ string title;
+ Icon icon;
+ Rectangle bounds;
+ Size client_size;
+ bool exists;
+ bool is_focused;
+ bool is_cursor_visible = true;
+
+ KeyboardState previous_keyboard;
+ MouseState previous_mouse;
+
+ MouseCursor cursor_current;
+ BufferObject cursor_custom;
+ BufferObject cursor_default;
+ BufferObject cursor_empty;
+
+ IntPtr gbm_surface;
+
+ public LinuxNativeWindow(IntPtr display, IntPtr gbm, int fd,
+ int x, int y, int width, int height, string title,
+ GraphicsMode mode, GameWindowFlags options,
+ DisplayDevice display_device)
+ {
+ Debug.Print("[KMS] Creating window on display {0:x}", display);
+
+ Title = title;
+
+ display_device = display_device ?? DisplayDevice.Default;
+ if (display_device == null)
+ {
+ throw new NotSupportedException("[KMS] Driver does not currently support headless systems");
+ }
+
+ window = new LinuxWindowInfo(display, fd, gbm, display_device.Id as LinuxDisplay);
+
+ // Note: we only support fullscreen windows on KMS.
+ // We implicitly override the requested width and height
+ // by the width and height of the DisplayDevice, if any.
+ width = display_device.Width;
+ height = display_device.Height;
+ bounds = new Rectangle(0, 0, width, height);
+ client_size = bounds.Size;
+
+ if (!mode.Index.HasValue)
+ {
+ mode = new EglGraphicsMode().SelectGraphicsMode(window, mode, 0);
+ }
+ Debug.Print("[KMS] Selected EGL mode {0}", mode);
+
+ SurfaceFormat format = GetSurfaceFormat(display, mode);
+ SurfaceFlags usage = SurfaceFlags.Rendering | SurfaceFlags.Scanout;
+ if (!Gbm.IsFormatSupported(gbm, format, usage))
+ {
+ Debug.Print("[KMS] Failed to find suitable surface format, using XRGB8888");
+ format = SurfaceFormat.XRGB8888;
+ }
+
+ Debug.Print("[KMS] Creating GBM surface on {0:x} with {1}x{2} {3} [{4}]",
+ gbm, width, height, format, usage);
+ IntPtr gbm_surface = Gbm.CreateSurface(gbm,
+ width, height, format, usage);
+ if (gbm_surface == IntPtr.Zero)
+ {
+ throw new NotSupportedException("[KMS] Failed to create GBM surface for rendering");
+ }
+
+ window.Handle = gbm_surface;
+ Debug.Print("[KMS] Created GBM surface {0:x}", window.Handle);
+
+ window.CreateWindowSurface(mode.Index.Value);
+ Debug.Print("[KMS] Created EGL surface {0:x}", window.Surface);
+
+ cursor_default = CreateCursor(gbm, Cursors.Default);
+ cursor_empty = CreateCursor(gbm, Cursors.Empty);
+ Cursor = MouseCursor.Default;
+ exists = true;
+ }
+
+ #region Private Members
+
+ static BufferObject CreateCursor(IntPtr gbm, MouseCursor cursor)
+ {
+ if (cursor.Width > 64 || cursor.Height > 64)
+ {
+ Debug.Print("[KMS] Cursor size {0}x{1} unsupported. Maximum is 64x64.",
+ cursor.Width, cursor.Height);
+ return default(BufferObject);
+ }
+
+ int width = 64;
+ int height = 64;
+ SurfaceFormat format = SurfaceFormat.ARGB8888;
+ SurfaceFlags usage = SurfaceFlags.Cursor64x64 | SurfaceFlags.Write;
+
+ Debug.Print("[KMS] Gbm.CreateBuffer({0:X}, {1}, {2}, {3}, {4}).",
+ gbm, width, height, format, usage);
+
+ BufferObject bo = Gbm.CreateBuffer(
+ gbm, width, height, format, usage);
+
+ if (bo == BufferObject.Zero)
+ {
+ Debug.Print("[KMS] Failed to create buffer.");
+ return bo;
+ }
+
+ // Copy cursor.Data into a new buffer of the correct size
+ byte[] cursor_data = new byte[width * height * 4];
+ for (int y = 0; y < cursor.Height; y++)
+ {
+ int dst_offset = y * width * 4;
+ int src_offset = y * cursor.Width * 4;
+ int src_length = cursor.Width * 4;
+ Array.Copy(
+ cursor.Data, src_offset,
+ cursor_data, dst_offset,
+ src_length);
+ }
+ bo.Write(cursor_data);
+
+ return bo;
+ }
+
+ void SetCursor(MouseCursor cursor)
+ {
+ BufferObject bo = default(BufferObject);
+ if (cursor == MouseCursor.Default)
+ {
+ bo = cursor_default;
+ }
+ else if (cursor == MouseCursor.Empty)
+ {
+ bo = cursor_empty;
+ }
+ else
+ {
+ if (cursor_custom != BufferObject.Zero)
+ cursor_custom.Dispose();
+ cursor_custom = CreateCursor(window.BufferManager, cursor);
+ bo = cursor_custom;
+ }
+
+ // If we failed to create a proper cursor, try falling back
+ // to the empty cursor. We do not want to crash here!
+ if (bo == BufferObject.Zero)
+ {
+ bo = cursor_empty;
+ }
+
+ if (bo != BufferObject.Zero)
+ {
+ Drm.SetCursor(window.FD, window.DisplayDevice.Id,
+ bo.Handle, bo.Width, bo.Height, cursor.X, cursor.Y);
+ }
+ }
+
+ static SurfaceFormat GetSurfaceFormat(IntPtr display, GraphicsMode mode)
+ {
+ // Use EGL 1.4 EGL_NATIVE_VISUAL_ID to retrieve
+ // the corresponding surface format. If that fails
+ // fall back to a manual algorithm.
+ int format;
+ Egl.GetConfigAttrib(display, mode.Index.Value,
+ Egl.NATIVE_VISUAL_ID, out format);
+ if ((SurfaceFormat)format != 0)
+ return (SurfaceFormat)format;
+
+ Debug.Print("[KMS] Failed to retrieve EGL visual from GBM surface. Error: {0}",
+ Egl.GetError());
+ Debug.Print("[KMS] Falling back to hardcoded formats.");
+
+ int r = mode.ColorFormat.Red;
+ int g = mode.ColorFormat.Green;
+ int b = mode.ColorFormat.Blue;
+ int a = mode.ColorFormat.Alpha;
+
+ if (mode.ColorFormat.IsIndexed)
+ return SurfaceFormat.C8;
+ if (r == 3 && g == 3 && b == 2 && a == 0)
+ return SurfaceFormat.RGB332;
+ if (r == 5 && g == 6 && b == 5 && a == 0)
+ return SurfaceFormat.RGB565;
+ if (r == 5 && g == 6 && b == 5 && a == 0)
+ return SurfaceFormat.RGB565;
+ if (r == 8 && g == 8 && b == 8 && a == 0)
+ return SurfaceFormat.RGB888;
+ if (r == 5 && g == 5 && b == 5 && a == 1)
+ return SurfaceFormat.RGBA5551;
+ if (r == 10 && g == 10 && b == 10 && a == 2)
+ return SurfaceFormat.RGBA1010102;
+ if (r == 4 && g == 4 && b == 4 && a == 4)
+ return SurfaceFormat.RGBA4444;
+ if (r == 8 && g == 8 && b == 8 && a == 8)
+ return SurfaceFormat.RGBA8888;
+
+ return SurfaceFormat.RGBA8888;
+ }
+
+ KeyboardState ProcessKeyboard(KeyboardState keyboard)
+ {
+ for (Key i = 0; i < Key.LastKey; i++)
+ {
+ if (keyboard[i])
+ {
+ OnKeyDown(i, previous_keyboard[i]);
+ // Todo: implement libxkb-common binding for text input
+ }
+
+ if (!keyboard[i] && previous_keyboard[i])
+ {
+ OnKeyUp(i);
+ }
+ }
+ return keyboard;
+ }
+
+ MouseState ProcessMouse(MouseState mouse)
+ {
+ // Handle mouse buttons
+ for (MouseButton i = 0; i < MouseButton.LastButton; i++)
+ {
+ if (mouse[i] && !previous_mouse[i])
+ {
+ OnMouseDown(i);
+ }
+
+ if (!mouse[i] && previous_mouse[i])
+ {
+ OnMouseUp(i);
+ }
+ }
+
+ // Handle mouse movement
+ {
+ int x = mouse.X;
+ int y = mouse.Y;
+
+ // Make sure the mouse cannot leave the GameWindow when captured
+ if (!CursorVisible)
+ {
+ x = MathHelper.Clamp(mouse.X, Bounds.Left, Bounds.Right - 1);
+ y = MathHelper.Clamp(mouse.Y, Bounds.Top, Bounds.Bottom - 1);
+ if (x != mouse.X || y != mouse.Y)
+ {
+ Mouse.SetPosition(x, y);
+ }
+ }
+
+ if (x != previous_mouse.X || y != previous_mouse.Y)
+ {
+ OnMouseMove(x, y);
+ }
+ }
+
+ // Handle mouse scroll
+ if (mouse.Scroll != previous_mouse.Scroll)
+ {
+ float dx = mouse.Scroll.X - previous_mouse.Scroll.X;
+ float dy = mouse.Scroll.Y - previous_mouse.Scroll.Y;
+ OnMouseWheel(dx, dy);
+ }
+
+ // Handle mouse focus
+ // Note: focus follows mouse. Literally.
+ bool cursor_in = Bounds.Contains(new Point(mouse.X, mouse.Y));
+ if (!cursor_in && Focused)
+ {
+ OnMouseLeave(EventArgs.Empty);
+ SetFocus(false);
+ }
+ else if (cursor_in && !Focused)
+ {
+ OnMouseEnter(EventArgs.Empty);
+ SetFocus(true);
+ }
+
+ return mouse;
+ }
+
+ void SetFocus(bool focus)
+ {
+ if (is_focused != focus)
+ {
+ is_focused = focus;
+ OnFocusedChanged(EventArgs.Empty);
+ }
+ }
+
+ #endregion
+
+ #region INativeWindow Members
+
+ public override void ProcessEvents()
+ {
+ // Note: there is no event-based keyboard/mouse input available.
+ // We will fake that by polling OpenTK.Input.
+ previous_keyboard = ProcessKeyboard(Keyboard.GetState());
+ previous_mouse = ProcessMouse(Mouse.GetCursorState());
+
+ base.ProcessEvents();
+ }
+
+ public override void Close()
+ {
+ exists = false;
+ }
+
+ public override Point PointToClient(Point point)
+ {
+ var origin = Point.Empty;
+ var display = DisplayDevice.Default;
+ if (display != null)
+ {
+ origin = display.Bounds.Location;
+ }
+ var client = Location;
+ return new Point(point.X + client.X - origin.X, point.Y + client.Y - origin.Y);
+ }
+
+ public override Point PointToScreen(Point point)
+ {
+ var origin = Point.Empty;
+ var display = DisplayDevice.Default;
+ if (display != null)
+ {
+ origin = display.Bounds.Location;
+ }
+ var client = Location;
+ return new Point(point.X + origin.X - client.X, point.Y + origin.Y - client.Y);
+
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ Debug.Print("[KMS] Destroying window {0}.", window.Handle);
+ window.Dispose();
+ Gbm.DestroySurface(window.Handle);
+ }
+ else
+ {
+ Debug.Print("[KMS] {0} leaked. Did you forget to call Dispose()?", GetType().FullName);
+ }
+ }
+
+ public override Icon Icon
+ {
+ get
+ {
+ return icon;
+ }
+ set
+ {
+ if (icon != value)
+ {
+ icon = value;
+ OnIconChanged(EventArgs.Empty);
+ }
+ }
+ }
+
+ public override string Title
+ {
+ get
+ {
+ return title;
+ }
+ set
+ {
+ if (title != value)
+ {
+ title = value;
+ OnTitleChanged(EventArgs.Empty);
+ }
+ }
+ }
+
+ public override bool Focused
+ {
+ get
+ {
+ return is_focused;
+ }
+ }
+
+ public override bool Visible
+ {
+ get
+ {
+ return true;
+ }
+ set
+ {
+ }
+ }
+
+ public override bool Exists
+ {
+ get
+ {
+ return exists;
+ }
+ }
+
+ public override IWindowInfo WindowInfo
+ {
+ get
+ {
+ return window;
+ }
+ }
+
+ public override WindowState WindowState
+ {
+ get
+ {
+ return WindowState.Fullscreen;
+ }
+ set
+ {
+ }
+ }
+
+ public override WindowBorder WindowBorder
+ {
+ get
+ {
+ return WindowBorder.Hidden;
+ }
+ set
+ {
+ }
+ }
+
+ public override Rectangle Bounds
+ {
+ get
+ {
+ return bounds;
+ }
+ set
+ {
+ }
+ }
+
+ public override Size ClientSize
+ {
+ get
+ {
+ return client_size;
+ }
+ set
+ {
+ }
+ }
+
+ public override bool CursorVisible
+ {
+ get
+ {
+ return is_cursor_visible;
+ }
+ set
+ {
+ if (value && !is_cursor_visible)
+ {
+ SetCursor(cursor_current);
+ }
+ else if (!value && is_cursor_visible)
+ {
+ SetCursor(MouseCursor.Empty);
+ }
+ is_cursor_visible = value;
+ }
+ }
+
+ public override MouseCursor Cursor
+ {
+ get
+ {
+ return cursor_current;
+ }
+ set
+ {
+ if (cursor_current != value)
+ {
+ if (cursor_custom != BufferObject.Zero)
+ {
+ cursor_custom.Dispose();
+ }
+
+ if (CursorVisible)
+ {
+ SetCursor(value);
+ }
+ cursor_current = value;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs b/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs
new file mode 100644
index 00000000..e1d91e9a
--- /dev/null
+++ b/Source/OpenTK/Platform/Linux/LinuxWindowInfo.cs
@@ -0,0 +1,56 @@
+#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 System.Diagnostics;
+using OpenTK.Platform.Egl;
+
+namespace OpenTK.Platform.Linux
+{
+ class LinuxWindowInfo : EglWindowInfo
+ {
+ public int FD { get; private set; }
+ public LinuxDisplay DisplayDevice { get; private set; }
+ public IntPtr BufferManager { get; private set; }
+
+ public LinuxWindowInfo(IntPtr display, int fd, IntPtr gbm, LinuxDisplay display_device)
+ : base(IntPtr.Zero, display, IntPtr.Zero)
+ {
+ if (display_device == null)
+ throw new ArgumentNullException();
+
+ FD = fd;
+ BufferManager = gbm;
+ DisplayDevice = display_device;
+ // The window handle and surface handle must
+ // be filled in manually once they are known.
+ }
+ }
+}
+
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()