Merge pull request #151 from thefiddler/kms

[KMS] Implement Linux/KMS platform
This commit is contained in:
thefiddler 2014-07-18 13:39:59 +02:00
commit 17aa8051f6
33 changed files with 4758 additions and 135 deletions

View file

@ -23,16 +23,35 @@ namespace Examples.Tests
DisplayDevice dev = DisplayDevice.GetDisplay(DisplayIndex.First + i); DisplayDevice dev = DisplayDevice.GetDisplay(DisplayIndex.First + i);
if (dev != null) if (dev != null)
{ {
Trace.WriteLine(dev.ToString()); Print(dev.ToString());
MessageBox.Show(dev.ToString());
dev.ChangeResolution(dev.SelectResolution(640, 480, 32, 60.0f)); dev.ChangeResolution(dev.SelectResolution(640, 480, 32, 60.0f));
Thread.Sleep(1000); Thread.Sleep(1000);
MessageBox.Show(dev.ToString()); Print(dev.ToString());
dev.RestoreResolution(); dev.RestoreResolution();
Thread.Sleep(1000); 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
{
}
}
} }
} }

View file

@ -95,7 +95,7 @@ namespace OpenTK
#region public static bool RunningOnLinux #region public static bool RunningOnLinux
/// <summary>Gets a System.Boolean indicating whether OpenTK is running on an X11 platform.</summary> /// <summary>Gets a System.Boolean indicating whether OpenTK is running on the Linux kernel.</summary>
public static bool RunningOnLinux { get { return runningOnLinux; } } public static bool RunningOnLinux { get { return runningOnLinux; } }
#endregion #endregion

View file

@ -351,6 +351,26 @@ namespace OpenTK
#endregion #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 #endregion
#region --- Private Methods --- #region --- Private Methods ---

View file

@ -26,6 +26,7 @@
#endregion #endregion
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace OpenTK.Graphics namespace OpenTK.Graphics
@ -84,6 +85,8 @@ namespace OpenTK.Graphics
// validation necessary.) // validation necessary.)
internal override void LoadEntryPoints() internal override void LoadEntryPoints()
{ {
Debug.Print("Loading entry points for {0}", GetType().FullName);
IGraphicsContext context = GraphicsContext.CurrentContext; IGraphicsContext context = GraphicsContext.CurrentContext;
if (context == null) if (context == null)
throw new GraphicsContextMissingException(); throw new GraphicsContextMissingException();

View file

@ -38,7 +38,8 @@ namespace OpenTK.Input
{ {
#region Fields #region Fields
int x, y; internal const int MaxButtons = 16; // we are storing in an ushort
Vector2 position;
MouseScroll scroll; MouseScroll scroll;
ushort buttons; ushort buttons;
bool is_connected; bool is_connected;
@ -101,7 +102,7 @@ namespace OpenTK.Input
} }
/// <summary> /// <summary>
/// Gets a <see cref="OpenTK.Input.MouseScrollWheel"/> instance, /// Gets a <see cref="OpenTK.Input.MouseScroll"/> instance,
/// representing the current state of the mouse scroll wheel. /// representing the current state of the mouse scroll wheel.
/// </summary> /// </summary>
public MouseScroll Scroll public MouseScroll Scroll
@ -114,8 +115,8 @@ namespace OpenTK.Input
/// </summary> /// </summary>
public int X public int X
{ {
get { return x; } get { return (int)Math.Round(position.X); }
internal set { x = value; } internal set { position.X = value; }
} }
/// <summary> /// <summary>
@ -123,8 +124,8 @@ namespace OpenTK.Input
/// </summary> /// </summary>
public int Y public int Y
{ {
get { return y; } get { return (int)Math.Round(position.Y); }
internal set { y = value; } internal set { position.Y = value; }
} }
/// <summary> /// <summary>
@ -272,6 +273,12 @@ namespace OpenTK.Input
#region Internal Members #region Internal Members
internal Vector2 Position
{
get { return position; }
set { position = value; }
}
internal bool ReadBit(int offset) internal bool ReadBit(int offset)
{ {
ValidateOffset(offset); ValidateOffset(offset);

View file

@ -343,9 +343,6 @@
<Compile Include="Platform\X11\X11WindowInfo.cs"> <Compile Include="Platform\X11\X11WindowInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Platform\X11\X11Joystick.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Platform\X11\Functions.cs"> <Compile Include="Platform\X11\Functions.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
@ -806,6 +803,27 @@
<Compile Include="Platform\X11\XI2Input.cs" /> <Compile Include="Platform\X11\XI2Input.cs" />
<Compile Include="Platform\X11\XI2MouseKeyboard.cs" /> <Compile Include="Platform\X11\XI2MouseKeyboard.cs" />
<Compile Include="Platform\MacOS\Cocoa\NSFloat.cs" /> <Compile Include="Platform\MacOS\Cocoa\NSFloat.cs" />
<Compile Include="Platform\Linux\Bindings\Drm.cs" />
<Compile Include="Platform\Linux\LinuxFactory.cs" />
<Compile Include="Platform\Linux\LinuxNativeWindow.cs" />
<Compile Include="Platform\Linux\Bindings\Gbm.cs" />
<Compile Include="Platform\Linux\LinuxDisplayDriver.cs" />
<Compile Include="Platform\Linux\LinuxJoystick.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Platform\Linux\Bindings\Libc.cs" />
<Compile Include="Platform\Linux\LinuxWindowInfo.cs" />
<Compile Include="Platform\Linux\LinuxGraphicsContext.cs" />
<Compile Include="Platform\Linux\Bindings\Poll.cs" />
<Compile Include="Platform\Linux\LinuxKeyboardTTY.cs" />
<Compile Include="Platform\Linux\Bindings\Udev.cs" />
<Compile Include="Platform\Linux\Bindings\LibInput.cs" />
<Compile Include="Platform\Linux\LinuxInput.cs" />
<Compile Include="Platform\Linux\Bindings\Terminal.cs" />
<Compile Include="Platform\DeviceCollection.cs" />
<Compile Include="Platform\Linux\Bindings\Evdev.cs" />
<Compile Include="Platform\Linux\DefaultCursor.cs" />
<Compile Include="Platform\Linux\Bindings\Kms.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
@ -836,4 +854,8 @@
</MonoDevelop> </MonoDevelop>
</ProjectExtensions> </ProjectExtensions>
<ItemGroup /> <ItemGroup />
<ItemGroup>
<Folder Include="Platform\Linux\" />
<Folder Include="Platform\Linux\Bindings\" />
</ItemGroup>
</Project> </Project>

View file

@ -0,0 +1,151 @@
#region License
//
// DeviceCollection.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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<T> : IEnumerable<T>
{
readonly Dictionary<int, int> Map = new Dictionary<int, int>();
readonly List<T> Devices = new List<T>();
#region IEnumerable<T> Members
public IEnumerator<T> 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
}
}

View file

@ -41,6 +41,13 @@ namespace OpenTK.Platform.Egl
using EGLSurface = IntPtr; using EGLSurface = IntPtr;
using EGLClientBuffer = IntPtr; using EGLClientBuffer = IntPtr;
enum RenderApi
{
ES = Egl.OPENGL_ES_API,
GL = Egl.OPENGL_API,
VG = Egl.OPENVG_API
}
[Flags] [Flags]
enum RenderableFlags enum RenderableFlags
{ {
@ -50,6 +57,24 @@ namespace OpenTK.Platform.Egl
VG = Egl.OPENVG_BIT, 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 static partial class Egl
{ {
public const int VERSION_1_0 = 1; public const int VERSION_1_0 = 1;
@ -60,20 +85,6 @@ namespace OpenTK.Platform.Egl
public const int FALSE = 0; public const int FALSE = 0;
public const int TRUE = 1; public const int TRUE = 1;
public const int DONT_CARE = -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 CONTEXT_LOST = 12302;
public const int BUFFER_SIZE = 12320; public const int BUFFER_SIZE = 12320;
public const int ALPHA_SIZE = 12321; public const int ALPHA_SIZE = 12321;
@ -178,7 +189,7 @@ namespace OpenTK.Platform.Egl
public const int ALPHA_FORMAT_PRE = VG_ALPHA_FORMAT_PRE; public const int ALPHA_FORMAT_PRE = VG_ALPHA_FORMAT_PRE;
[DllImportAttribute("libEGL.dll", EntryPoint = "eglGetError")] [DllImportAttribute("libEGL.dll", EntryPoint = "eglGetError")]
public static extern int GetError(); public static extern ErrorCode GetError();
[DllImportAttribute("libEGL.dll", EntryPoint = "eglGetDisplay")] [DllImportAttribute("libEGL.dll", EntryPoint = "eglGetDisplay")]
public static extern EGLDisplay GetDisplay(EGLNativeDisplayType display_id); public static extern EGLDisplay GetDisplay(EGLNativeDisplayType display_id);
@ -225,7 +236,7 @@ namespace OpenTK.Platform.Egl
[DllImportAttribute("libEGL.dll", EntryPoint = "eglBindAPI")] [DllImportAttribute("libEGL.dll", EntryPoint = "eglBindAPI")]
[return: MarshalAsAttribute(UnmanagedType.I1)] [return: MarshalAsAttribute(UnmanagedType.I1)]
public static extern bool BindAPI(int api); public static extern bool BindAPI(RenderApi api);
[DllImportAttribute("libEGL.dll", EntryPoint = "eglQueryAPI")] [DllImportAttribute("libEGL.dll", EntryPoint = "eglQueryAPI")]
public static extern int QueryAPI(); public static extern int QueryAPI();

View file

@ -35,9 +35,9 @@ namespace OpenTK.Platform.Egl
{ {
#region Fields #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); } } IntPtr HandleAsEGLContext { get { return Handle.Handle; } set { Handle = new ContextHandle(value); } }
int swap_interval = 1; // Default interval is defined as 1 in EGL. 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' // 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) // parameter directly, since it may have originated on a different system (e.g. GLX)
// and it may not support the desired renderer. // 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 = new EglGraphicsMode().SelectGraphicsMode(window,
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples, mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo, mode.AccumulatorFormat, mode.Buffers, mode.Stereo,
@ -133,7 +146,8 @@ namespace OpenTK.Platform.Egl
if (Egl.SwapInterval(WindowInfo.Display, value)) if (Egl.SwapInterval(WindowInfo.Display, value))
swap_interval = value; swap_interval = value;
else 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());
} }
} }

View file

@ -34,6 +34,15 @@ namespace OpenTK.Platform.Egl
{ {
class EglGraphicsMode 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, public GraphicsMode SelectGraphicsMode(EglWindowInfo window,
ColorFormat color, int depth, int stencil, ColorFormat color, int depth, int stencil,
int samples, ColorFormat accum, int buffers, bool stereo, int samples, ColorFormat accum, int buffers, bool stereo,

View file

@ -28,14 +28,16 @@
#endregion #endregion
using System; using System;
using System.Diagnostics;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace OpenTK.Platform.Egl namespace OpenTK.Platform.Egl
{ {
class EglUnixContext : EglContext class EglUnixContext : EglContext
{ {
readonly IntPtr ES1 = OpenTK.Platform.X11.DL.Open("libGLESv1_CM", X11.DLOpenFlags.Lazy); 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 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, public EglUnixContext(GraphicsMode mode, EglWindowInfo window, IGraphicsContext sharedContext,
int major, int minor, GraphicsContextFlags flags) int major, int minor, GraphicsContextFlags flags)
@ -59,6 +61,10 @@ namespace OpenTK.Platform.Egl
{ {
return X11.DL.Symbol(ES2, function); return X11.DL.Symbol(ES2, function);
} }
else if ((renderable & RenderableFlags.GL) != 0 && GL != IntPtr.Zero)
{
return X11.DL.Symbol(GL, function);
}
return IntPtr.Zero; return IntPtr.Zero;
} }
@ -72,8 +78,32 @@ namespace OpenTK.Platform.Egl
{ {
X11.DL.Close(ES2); X11.DL.Close(ES2);
} }
if (GL != IntPtr.Zero)
{
X11.DL.Close(GL);
}
GL = ES1 = ES2 = IntPtr.Zero;
base.Dispose(manual); 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);
}
} }
} }

View file

@ -75,7 +75,7 @@ namespace OpenTK.Platform.Egl
#region Public Members #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; } } public IntPtr Display { get { return display; } private set { display = value; } }
@ -87,7 +87,7 @@ namespace OpenTK.Platform.Egl
if (Surface==IntPtr.Zero) if (Surface==IntPtr.Zero)
{ {
throw new GraphicsContextException(String.Format( 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()));
} }
} }

View file

@ -54,6 +54,7 @@ namespace OpenTK.Platform
// Create regular platform backend // Create regular platform backend
if (Configuration.RunningOnSdl2) Default = new SDL2.Sdl2Factory(); if (Configuration.RunningOnSdl2) Default = new SDL2.Sdl2Factory();
else if (Configuration.RunningOnX11) Default = new X11.X11Factory(); 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.RunningOnWindows) Default = new Windows.WinFactory();
else if (Configuration.RunningOnMacOS) Default = new MacOS.MacOSFactory(); else if (Configuration.RunningOnMacOS) Default = new MacOS.MacOSFactory();
else Default = new UnsupportedPlatform(); else Default = new UnsupportedPlatform();
@ -70,7 +71,8 @@ namespace OpenTK.Platform
} }
else if (Egl.Egl.IsSupported) 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.RunningOnWindows) Embedded = new Egl.EglWinPlatformFactory();
else if (Configuration.RunningOnMacOS) Embedded = new Egl.EglMacPlatformFactory(); else if (Configuration.RunningOnMacOS) Embedded = new Egl.EglMacPlatformFactory();
else Embedded = new UnsupportedPlatform(); else Embedded = new UnsupportedPlatform();

View file

@ -0,0 +1,209 @@
#region License
//
// Drm.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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;
}
}

View file

@ -0,0 +1,452 @@
#region License
//
// Evdev.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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,
}
}

View file

@ -0,0 +1,278 @@
#region License
//
// Gbm.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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<BufferObject>
{
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
}
}

View file

@ -0,0 +1,46 @@
#region License
//
// Kms.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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);
}
}

View file

@ -0,0 +1,330 @@
#region License
//
// LibInput.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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
/// <summary>
/// Signals the end of a set of touchpoints at one device sample
/// time. This event has no coordinate information attached.
/// </summary>
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);
}
}

View file

@ -0,0 +1,178 @@
#region License
//
// Linux.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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
}
}

View file

@ -0,0 +1,65 @@
#region License
//
// Poll.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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;
}
}

View file

@ -0,0 +1,170 @@
#region License
//
// Terminal.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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;
}
}

View file

@ -0,0 +1,46 @@
#region License
//
// Udev.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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);
}
}

View file

@ -0,0 +1,76 @@
#region License
//
// DefaultCursor.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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;
}
}

View file

@ -0,0 +1,413 @@
#region License
//
// LinuxDisplayDriver.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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<int, int> DisplayIds =
new Dictionary<int, int>();
public LinuxDisplayDriver(int fd)
{
Debug.Print("[KMS] Creating LinuxDisplayDriver for fd:{0}", fd);
Debug.Indent();
try
{
FD = fd;
UpdateDisplays(fd);
}
finally
{
Debug.Unindent();
}
}
/// \internal
/// <summary>
/// Queries the specified GPU for connected displays and, optionally,
/// returns the list of displays.
/// </summary>
/// <returns><c>true</c>, if at least one display is connected, <c>false</c> otherwise.</returns>
/// <param name="fd">The fd for the GPU to query, obtained through open("/dev/dri/card0").</param>
/// <param name="displays">
/// If not null, this will contain a list <see cref="LinuxDisplay"/> instances,
/// one for each connected display.
/// </param>
internal static bool QueryDisplays(int fd, List<LinuxDisplay> 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<LinuxDisplay> displays = new List<LinuxDisplay>();
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
}
}

View file

@ -0,0 +1,248 @@
#region License
//
// LinuxFactory.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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
}
}

View file

@ -0,0 +1,303 @@
#region License
//
// LinuxGraphicsContext.cs
//
// Author:
// thefiddler <stapostol@gmail.com>
//
// 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
/// <summary>
/// Defines an IGraphicsContext implementation for the Linux KMS framebuffer.
/// For Linux/X11 and other Unix operating systems, use the more generic
/// <see cref="OpenTK.Platform.Egl.EglUnixContext"/> instead.
/// </summary>
/// <remarks>
/// Note: to display our results, we need to allocate a GBM framebuffer
/// and point the scanout address to that via Drm.ModeSetCrtc.
/// </remarks>
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);
}
}
}

View file

@ -0,0 +1,717 @@
#region License
//
// LinuxKeyboardLibInput.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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<KeyboardDevice> KeyboardCandidates = new DeviceCollection<KeyboardDevice>();
DeviceCollection<MouseDevice> MouseCandidates = new DeviceCollection<MouseDevice>();
DeviceCollection<KeyboardDevice> Keyboards = new DeviceCollection<KeyboardDevice>();
DeviceCollection<MouseDevice> Mice = new DeviceCollection<MouseDevice>();
// 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
}
}

View file

@ -33,9 +33,9 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using OpenTK.Input; using OpenTK.Input;
namespace OpenTK.Platform.X11 namespace OpenTK.Platform.Linux
{ {
struct X11JoyDetails struct LinuxJoyDetails
{ {
public Guid Guid; public Guid Guid;
public int FileDescriptor; public int FileDescriptor;
@ -43,7 +43,7 @@ namespace OpenTK.Platform.X11
} }
// Note: despite what the name says, this class is Linux-specific. // Note: despite what the name says, this class is Linux-specific.
sealed class X11Joystick : IJoystickDriver2 sealed class LinuxJoystick : IJoystickDriver2
{ {
#region Fields #region Fields
@ -52,7 +52,7 @@ namespace OpenTK.Platform.X11
readonly FileSystemWatcher watcher = new FileSystemWatcher(); readonly FileSystemWatcher watcher = new FileSystemWatcher();
readonly Dictionary<int, int> index_to_stick = new Dictionary<int, int>(); readonly Dictionary<int, int> index_to_stick = new Dictionary<int, int>();
List<JoystickDevice<X11JoyDetails>> sticks = new List<JoystickDevice<X11JoyDetails>>(); List<JoystickDevice<LinuxJoyDetails>> sticks = new List<JoystickDevice<LinuxJoyDetails>>();
bool disposed; bool disposed;
@ -60,7 +60,7 @@ namespace OpenTK.Platform.X11
#region Constructors #region Constructors
public X11Joystick() public LinuxJoystick()
{ {
string path = string path =
Directory.Exists(JoystickPath) ? JoystickPath : Directory.Exists(JoystickPath) ? JoystickPath :
@ -89,7 +89,7 @@ namespace OpenTK.Platform.X11
{ {
foreach (string file in Directory.GetFiles(path)) foreach (string file in Directory.GetFiles(path))
{ {
JoystickDevice<X11JoyDetails> stick = OpenJoystick(file); JoystickDevice<LinuxJoyDetails> stick = OpenJoystick(file);
if (stick != null) if (stick != null)
{ {
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})", //stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
@ -155,7 +155,7 @@ namespace OpenTK.Platform.X11
#region Private Members #region Private Members
Guid CreateGuid(JoystickDevice<X11JoyDetails> js, string path, int number) Guid CreateGuid(JoystickDevice<LinuxJoyDetails> js, string path, int number)
{ {
byte[] bytes = new byte[16]; byte[] bytes = new byte[16];
for (int i = 0; i < Math.Min(bytes.Length, js.Description.Length); i++) for (int i = 0; i < Math.Min(bytes.Length, js.Description.Length); i++)
@ -221,9 +221,9 @@ namespace OpenTK.Platform.X11
#endif #endif
} }
JoystickDevice<X11JoyDetails> OpenJoystick(string path) JoystickDevice<LinuxJoyDetails> OpenJoystick(string path)
{ {
JoystickDevice<X11JoyDetails> stick = null; JoystickDevice<LinuxJoyDetails> stick = null;
int number = GetJoystickNumber(Path.GetFileName(path)); int number = GetJoystickNumber(Path.GetFileName(path));
if (number >= 0) if (number >= 0)
@ -231,28 +231,28 @@ namespace OpenTK.Platform.X11
int fd = -1; int fd = -1;
try try
{ {
fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock); fd = Libc.open(path, OpenFlags.NonBlock);
if (fd == -1) if (fd == -1)
return null; return null;
// Check joystick driver version (must be 1.0+) // Check joystick driver version (must be 1.0+)
int driver_version = 0x00000800; int driver_version = 0x00000800;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Version, ref driver_version); Libc.ioctl(fd, JoystickIoctlCode.Version, ref driver_version);
if (driver_version < 0x00010000) if (driver_version < 0x00010000)
return null; return null;
// Get number of joystick axes // Get number of joystick axes
int axes = 0; int axes = 0;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes); Libc.ioctl(fd, JoystickIoctlCode.Axes, ref axes);
// Get number of joystick buttons // Get number of joystick buttons
int buttons = 0; int buttons = 0;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons); Libc.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
stick = new JoystickDevice<X11JoyDetails>(number, axes, buttons); stick = new JoystickDevice<LinuxJoyDetails>(number, axes, buttons);
StringBuilder sb = new StringBuilder(128); StringBuilder sb = new StringBuilder(128);
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb); Libc.ioctl(fd, JoystickIoctlCode.Name128, sb);
stick.Description = sb.ToString(); stick.Description = sb.ToString();
stick.Details.FileDescriptor = fd; stick.Details.FileDescriptor = fd;
@ -287,16 +287,16 @@ namespace OpenTK.Platform.X11
finally finally
{ {
if (stick == null && fd != -1) if (stick == null && fd != -1)
UnsafeNativeMethods.close(fd); Libc.close(fd);
} }
} }
return stick; return stick;
} }
void CloseJoystick(JoystickDevice<X11JoyDetails> js) void CloseJoystick(JoystickDevice<LinuxJoyDetails> js)
{ {
UnsafeNativeMethods.close(js.Details.FileDescriptor); Libc.close(js.Details.FileDescriptor);
js.Details.State = new JoystickState(); // clear joystick state js.Details.State = new JoystickState(); // clear joystick state
js.Details.FileDescriptor = -1; js.Details.FileDescriptor = -1;
@ -317,13 +317,13 @@ namespace OpenTK.Platform.X11
} }
} }
void PollJoystick(JoystickDevice<X11JoyDetails> js) void PollJoystick(JoystickDevice<LinuxJoyDetails> js)
{ {
JoystickEvent e; JoystickEvent e;
unsafe 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; e.Type &= ~JoystickEventType.Init;
@ -352,78 +352,9 @@ namespace OpenTK.Platform.X11
return index_to_stick.ContainsKey(index); return index_to_stick.ContainsKey(index);
} }
#region UnsafeNativeMethods
struct EvdevInputId
{
public ushort BusType;
public ushort Vendor;
public ushort Product;
public ushort Version;
}
enum EvdevIoctlCode : uint
{
Id = ((byte)'E' << 8) | (0x02 << 0) //EVIOCGID, which is _IOR('E', 0x02, struct input_id)
}
struct JoystickEvent
{
public uint Time; // (u32) event timestamp in milliseconds
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 JoystickPath = "/dev/input";
static readonly string JoystickPathLegacy = "/dev"; 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 #endregion
#region IDisposable Members #region IDisposable Members
@ -443,7 +374,7 @@ namespace OpenTK.Platform.X11
} }
watcher.Dispose(); watcher.Dispose();
foreach (JoystickDevice<X11JoyDetails> js in sticks) foreach (JoystickDevice<LinuxJoyDetails> js in sticks)
{ {
CloseJoystick(js); CloseJoystick(js);
} }
@ -452,7 +383,7 @@ namespace OpenTK.Platform.X11
} }
} }
~X11Joystick() ~LinuxJoystick()
{ {
Dispose(false); Dispose(false);
} }
@ -465,7 +396,7 @@ namespace OpenTK.Platform.X11
{ {
if (IsValid(index)) if (IsValid(index))
{ {
JoystickDevice<X11JoyDetails> js = JoystickDevice<LinuxJoyDetails> js =
sticks[index_to_stick[index]]; sticks[index_to_stick[index]];
PollJoystick(js); PollJoystick(js);
return js.Details.State; return js.Details.State;
@ -478,7 +409,7 @@ namespace OpenTK.Platform.X11
JoystickCapabilities caps = new JoystickCapabilities(); JoystickCapabilities caps = new JoystickCapabilities();
if (IsValid(index)) if (IsValid(index))
{ {
JoystickDevice<X11JoyDetails> js = sticks[index_to_stick[index]]; JoystickDevice<LinuxJoyDetails> js = sticks[index_to_stick[index]];
caps = new JoystickCapabilities( caps = new JoystickCapabilities(
js.Axis.Count, js.Axis.Count,
js.Button.Count, js.Button.Count,
@ -492,7 +423,7 @@ namespace OpenTK.Platform.X11
{ {
if (IsValid(index)) if (IsValid(index))
{ {
JoystickDevice<X11JoyDetails> js = sticks[index_to_stick[index]]; JoystickDevice<LinuxJoyDetails> js = sticks[index_to_stick[index]];
return js.Details.Guid; return js.Details.Guid;
} }
return new Guid(); return new Guid();

View file

@ -0,0 +1,268 @@
#region License
//
// LinuxKeyboardTTY.cs
//
// Author:
// thefiddler <stapostol@gmail.com>
//
// 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
}

View file

@ -0,0 +1,549 @@
#region License
//
// LinuxNativeWindow.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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
}
}

View file

@ -0,0 +1,56 @@
#region License
//
// LinuxWindowInfo.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// 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.
}
}
}

View file

@ -37,7 +37,7 @@ namespace OpenTK.Platform.X11
{ {
readonly X11Mouse mouse = new X11Mouse(); readonly X11Mouse mouse = new X11Mouse();
readonly X11Keyboard keyboard = new X11Keyboard(); readonly X11Keyboard keyboard = new X11Keyboard();
readonly X11Joystick joystick = new X11Joystick(); readonly Linux.LinuxJoystick joystick = new Linux.LinuxJoystick();
readonly IGamePadDriver gamepad = new MappedGamePadDriver(); readonly IGamePadDriver gamepad = new MappedGamePadDriver();
internal X11Input() internal X11Input()

View file

@ -36,7 +36,7 @@ namespace OpenTK.Platform.X11
class XI2Input : IInputDriver2 class XI2Input : IInputDriver2
{ {
readonly XI2MouseKeyboard mouse_keyboard = new XI2MouseKeyboard(); readonly XI2MouseKeyboard mouse_keyboard = new XI2MouseKeyboard();
readonly X11Joystick joystick = new X11Joystick(); readonly Linux.LinuxJoystick joystick = new Linux.LinuxJoystick();
readonly IGamePadDriver gamepad = new MappedGamePadDriver(); readonly IGamePadDriver gamepad = new MappedGamePadDriver();
internal XI2Input() internal XI2Input()