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);
if (dev != null)
{
Trace.WriteLine(dev.ToString());
MessageBox.Show(dev.ToString());
Print(dev.ToString());
dev.ChangeResolution(dev.SelectResolution(640, 480, 32, 60.0f));
Thread.Sleep(1000);
MessageBox.Show(dev.ToString());
Print(dev.ToString());
dev.RestoreResolution();
Thread.Sleep(1000);
MessageBox.Show(dev.ToString());
Print(dev.ToString());
}
}
}
static void Print(string msg)
{
Trace.WriteLine(msg);
// Also display a MessageBox when running on a platform
// with WinForms support.
try
{
if (Configuration.RunningOnWindows || Configuration.RunningOnX11 || Configuration.RunningOnMacOS)
{
MessageBox.Show(msg);
}
}
catch
{
}
}
}
}

View file

@ -95,7 +95,7 @@ namespace OpenTK
#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; } }
#endregion

View file

@ -351,6 +351,26 @@ namespace OpenTK
#endregion
#region FromPoint
internal static DisplayDevice FromPoint(int x, int y)
{
for (DisplayIndex i = DisplayIndex.First; i < DisplayIndex.Sixth; i++)
{
DisplayDevice display = DisplayDevice.GetDisplay(i);
if (display != null)
{
if (display.Bounds.Contains(x, y))
{
return display;
}
}
}
return null;
}
#endregion
#endregion
#region --- Private Methods ---

View file

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

View file

@ -38,7 +38,8 @@ namespace OpenTK.Input
{
#region Fields
int x, y;
internal const int MaxButtons = 16; // we are storing in an ushort
Vector2 position;
MouseScroll scroll;
ushort buttons;
bool is_connected;
@ -101,7 +102,7 @@ namespace OpenTK.Input
}
/// <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.
/// </summary>
public MouseScroll Scroll
@ -114,8 +115,8 @@ namespace OpenTK.Input
/// </summary>
public int X
{
get { return x; }
internal set { x = value; }
get { return (int)Math.Round(position.X); }
internal set { position.X = value; }
}
/// <summary>
@ -123,8 +124,8 @@ namespace OpenTK.Input
/// </summary>
public int Y
{
get { return y; }
internal set { y = value; }
get { return (int)Math.Round(position.Y); }
internal set { position.Y = value; }
}
/// <summary>
@ -272,6 +273,12 @@ namespace OpenTK.Input
#region Internal Members
internal Vector2 Position
{
get { return position; }
set { position = value; }
}
internal bool ReadBit(int offset)
{
ValidateOffset(offset);

View file

@ -343,9 +343,6 @@
<Compile Include="Platform\X11\X11WindowInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Platform\X11\X11Joystick.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Platform\X11\Functions.cs">
<SubType>Code</SubType>
</Compile>
@ -806,6 +803,27 @@
<Compile Include="Platform\X11\XI2Input.cs" />
<Compile Include="Platform\X11\XI2MouseKeyboard.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>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
@ -836,4 +854,8 @@
</MonoDevelop>
</ProjectExtensions>
<ItemGroup />
<ItemGroup>
<Folder Include="Platform\Linux\" />
<Folder Include="Platform\Linux\Bindings\" />
</ItemGroup>
</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 EGLClientBuffer = IntPtr;
enum RenderApi
{
ES = Egl.OPENGL_ES_API,
GL = Egl.OPENGL_API,
VG = Egl.OPENVG_API
}
[Flags]
enum RenderableFlags
{
@ -50,6 +57,24 @@ namespace OpenTK.Platform.Egl
VG = Egl.OPENVG_BIT,
}
enum ErrorCode
{
SUCCESS = 12288,
NOT_INITIALIZED = 12289,
BAD_ACCESS = 12290,
BAD_ALLOC = 12291,
BAD_ATTRIBUTE = 12292,
BAD_CONFIG = 12293,
BAD_CONTEXT = 12294,
BAD_CURRENT_SURFACE = 12295,
BAD_DISPLAY = 12296,
BAD_MATCH = 12297,
BAD_NATIVE_PIXMAP = 12298,
BAD_NATIVE_WINDOW = 12299,
BAD_PARAMETER = 12300,
BAD_SURFACE = 12301,
}
static partial class Egl
{
public const int VERSION_1_0 = 1;
@ -60,20 +85,6 @@ namespace OpenTK.Platform.Egl
public const int FALSE = 0;
public const int TRUE = 1;
public const int DONT_CARE = -1;
public const int SUCCESS = 12288;
public const int NOT_INITIALIZED = 12289;
public const int BAD_ACCESS = 12290;
public const int BAD_ALLOC = 12291;
public const int BAD_ATTRIBUTE = 12292;
public const int BAD_CONFIG = 12293;
public const int BAD_CONTEXT = 12294;
public const int BAD_CURRENT_SURFACE = 12295;
public const int BAD_DISPLAY = 12296;
public const int BAD_MATCH = 12297;
public const int BAD_NATIVE_PIXMAP = 12298;
public const int BAD_NATIVE_WINDOW = 12299;
public const int BAD_PARAMETER = 12300;
public const int BAD_SURFACE = 12301;
public const int CONTEXT_LOST = 12302;
public const int BUFFER_SIZE = 12320;
public const int ALPHA_SIZE = 12321;
@ -178,7 +189,7 @@ namespace OpenTK.Platform.Egl
public const int ALPHA_FORMAT_PRE = VG_ALPHA_FORMAT_PRE;
[DllImportAttribute("libEGL.dll", EntryPoint = "eglGetError")]
public static extern int GetError();
public static extern ErrorCode GetError();
[DllImportAttribute("libEGL.dll", EntryPoint = "eglGetDisplay")]
public static extern EGLDisplay GetDisplay(EGLNativeDisplayType display_id);
@ -225,7 +236,7 @@ namespace OpenTK.Platform.Egl
[DllImportAttribute("libEGL.dll", EntryPoint = "eglBindAPI")]
[return: MarshalAsAttribute(UnmanagedType.I1)]
public static extern bool BindAPI(int api);
public static extern bool BindAPI(RenderApi api);
[DllImportAttribute("libEGL.dll", EntryPoint = "eglQueryAPI")]
public static extern int QueryAPI();

View file

@ -35,9 +35,9 @@ namespace OpenTK.Platform.Egl
{
#region Fields
readonly RenderableFlags Renderable;
protected readonly RenderableFlags Renderable;
protected EglWindowInfo WindowInfo;
EglWindowInfo WindowInfo;
IntPtr HandleAsEGLContext { get { return Handle.Handle; } set { Handle = new ContextHandle(value); } }
int swap_interval = 1; // Default interval is defined as 1 in EGL.
@ -60,7 +60,20 @@ namespace OpenTK.Platform.Egl
// Select an EGLConfig that matches the desired mode. We cannot use the 'mode'
// parameter directly, since it may have originated on a different system (e.g. GLX)
// and it may not support the desired renderer.
Renderable = major > 1 ? RenderableFlags.ES2 : RenderableFlags.ES;
Renderable = RenderableFlags.GL;
if ((flags & GraphicsContextFlags.Embedded) != 0)
{
Renderable = major > 1 ? RenderableFlags.ES2 : RenderableFlags.ES;
}
RenderApi api = (Renderable & RenderableFlags.GL) != 0 ? RenderApi.GL : RenderApi.ES;
Debug.Print("[EGL] Binding {0} rendering API.", api);
if (!Egl.BindAPI(api))
{
Debug.Print("[EGL] Failed to bind rendering API. Error: {0}", Egl.GetError());
}
Mode = new EglGraphicsMode().SelectGraphicsMode(window,
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo,
@ -133,7 +146,8 @@ namespace OpenTK.Platform.Egl
if (Egl.SwapInterval(WindowInfo.Display, value))
swap_interval = value;
else
Debug.Print("[Warning] Egl.SwapInterval({0}, {1}) failed.", WindowInfo.Display, value);
Debug.Print("[Warning] Egl.SwapInterval({0}, {1}) failed. Error: {2}",
WindowInfo.Display, value, Egl.GetError());
}
}

View file

@ -34,6 +34,15 @@ namespace OpenTK.Platform.Egl
{
class EglGraphicsMode
{
public GraphicsMode SelectGraphicsMode(EglWindowInfo window,
GraphicsMode mode, RenderableFlags flags)
{
return SelectGraphicsMode(window,
mode.ColorFormat, mode.Depth, mode.Stencil,
mode.Samples, mode.AccumulatorFormat, mode.Buffers, mode.Stereo,
flags);
}
public GraphicsMode SelectGraphicsMode(EglWindowInfo window,
ColorFormat color, int depth, int stencil,
int samples, ColorFormat accum, int buffers, bool stereo,

View file

@ -28,14 +28,16 @@
#endregion
using System;
using System.Diagnostics;
using OpenTK.Graphics;
namespace OpenTK.Platform.Egl
{
class EglUnixContext : EglContext
{
readonly IntPtr ES1 = OpenTK.Platform.X11.DL.Open("libGLESv1_CM", X11.DLOpenFlags.Lazy);
readonly IntPtr ES2 = OpenTK.Platform.X11.DL.Open("libGLESv2", X11.DLOpenFlags.Lazy);
IntPtr ES1 = OpenTK.Platform.X11.DL.Open("libGLESv1_CM", X11.DLOpenFlags.Lazy);
IntPtr ES2 = OpenTK.Platform.X11.DL.Open("libGLESv2", X11.DLOpenFlags.Lazy);
IntPtr GL = OpenTK.Platform.X11.DL.Open("libGL", X11.DLOpenFlags.Lazy);
public EglUnixContext(GraphicsMode mode, EglWindowInfo window, IGraphicsContext sharedContext,
int major, int minor, GraphicsContextFlags flags)
@ -59,6 +61,10 @@ namespace OpenTK.Platform.Egl
{
return X11.DL.Symbol(ES2, function);
}
else if ((renderable & RenderableFlags.GL) != 0 && GL != IntPtr.Zero)
{
return X11.DL.Symbol(GL, function);
}
return IntPtr.Zero;
}
@ -72,8 +78,32 @@ namespace OpenTK.Platform.Egl
{
X11.DL.Close(ES2);
}
if (GL != IntPtr.Zero)
{
X11.DL.Close(GL);
}
GL = ES1 = ES2 = IntPtr.Zero;
base.Dispose(manual);
}
public override void LoadAll()
{
// Modern unices can use EGL to create
// both GL and ES contexts, so we need
// to load all entry points. This is
// especially true on KMS, Wayland and Mir.
Stopwatch time = Stopwatch.StartNew();
new OpenTK.Graphics.OpenGL.GL().LoadEntryPoints();
new OpenTK.Graphics.OpenGL4.GL().LoadEntryPoints();
new OpenTK.Graphics.ES11.GL().LoadEntryPoints();
new OpenTK.Graphics.ES20.GL().LoadEntryPoints();
new OpenTK.Graphics.ES30.GL().LoadEntryPoints();
Debug.Print("Bindings loaded in {0} ms.", time.Elapsed.TotalMilliseconds);
}
}
}

View file

@ -75,7 +75,7 @@ namespace OpenTK.Platform.Egl
#region Public Members
public IntPtr Handle { get { return handle; } private set { handle = value; } }
public IntPtr Handle { get { return handle; } set { handle = value; } }
public IntPtr Display { get { return display; } private set { display = value; } }
@ -87,7 +87,7 @@ namespace OpenTK.Platform.Egl
if (Surface==IntPtr.Zero)
{
throw new GraphicsContextException(String.Format(
"[Error] Failed to create EGL window surface, error {0}.", Egl.GetError()));
"[EGL] Failed to create window surface, error {0}.", Egl.GetError()));
}
}

View file

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

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 OpenTK.Input;
namespace OpenTK.Platform.X11
namespace OpenTK.Platform.Linux
{
struct X11JoyDetails
struct LinuxJoyDetails
{
public Guid Guid;
public int FileDescriptor;
@ -43,7 +43,7 @@ namespace OpenTK.Platform.X11
}
// Note: despite what the name says, this class is Linux-specific.
sealed class X11Joystick : IJoystickDriver2
sealed class LinuxJoystick : IJoystickDriver2
{
#region Fields
@ -52,7 +52,7 @@ namespace OpenTK.Platform.X11
readonly FileSystemWatcher watcher = new FileSystemWatcher();
readonly Dictionary<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;
@ -60,7 +60,7 @@ namespace OpenTK.Platform.X11
#region Constructors
public X11Joystick()
public LinuxJoystick()
{
string path =
Directory.Exists(JoystickPath) ? JoystickPath :
@ -89,7 +89,7 @@ namespace OpenTK.Platform.X11
{
foreach (string file in Directory.GetFiles(path))
{
JoystickDevice<X11JoyDetails> stick = OpenJoystick(file);
JoystickDevice<LinuxJoyDetails> stick = OpenJoystick(file);
if (stick != null)
{
//stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons, {3}{0})",
@ -155,7 +155,7 @@ namespace OpenTK.Platform.X11
#region Private Members
Guid CreateGuid(JoystickDevice<X11JoyDetails> js, string path, int number)
Guid CreateGuid(JoystickDevice<LinuxJoyDetails> js, string path, int number)
{
byte[] bytes = new byte[16];
for (int i = 0; i < Math.Min(bytes.Length, js.Description.Length); i++)
@ -221,9 +221,9 @@ namespace OpenTK.Platform.X11
#endif
}
JoystickDevice<X11JoyDetails> OpenJoystick(string path)
JoystickDevice<LinuxJoyDetails> OpenJoystick(string path)
{
JoystickDevice<X11JoyDetails> stick = null;
JoystickDevice<LinuxJoyDetails> stick = null;
int number = GetJoystickNumber(Path.GetFileName(path));
if (number >= 0)
@ -231,28 +231,28 @@ namespace OpenTK.Platform.X11
int fd = -1;
try
{
fd = UnsafeNativeMethods.open(path, OpenFlags.NonBlock);
fd = Libc.open(path, OpenFlags.NonBlock);
if (fd == -1)
return null;
// Check joystick driver version (must be 1.0+)
int driver_version = 0x00000800;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Version, ref driver_version);
Libc.ioctl(fd, JoystickIoctlCode.Version, ref driver_version);
if (driver_version < 0x00010000)
return null;
// Get number of joystick axes
int axes = 0;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Axes, ref axes);
Libc.ioctl(fd, JoystickIoctlCode.Axes, ref axes);
// Get number of joystick buttons
int buttons = 0;
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
Libc.ioctl(fd, JoystickIoctlCode.Buttons, ref buttons);
stick = new JoystickDevice<X11JoyDetails>(number, axes, buttons);
stick = new JoystickDevice<LinuxJoyDetails>(number, axes, buttons);
StringBuilder sb = new StringBuilder(128);
UnsafeNativeMethods.ioctl(fd, JoystickIoctlCode.Name128, sb);
Libc.ioctl(fd, JoystickIoctlCode.Name128, sb);
stick.Description = sb.ToString();
stick.Details.FileDescriptor = fd;
@ -287,16 +287,16 @@ namespace OpenTK.Platform.X11
finally
{
if (stick == null && fd != -1)
UnsafeNativeMethods.close(fd);
Libc.close(fd);
}
}
return stick;
}
void CloseJoystick(JoystickDevice<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.FileDescriptor = -1;
@ -317,13 +317,13 @@ namespace OpenTK.Platform.X11
}
}
void PollJoystick(JoystickDevice<X11JoyDetails> js)
void PollJoystick(JoystickDevice<LinuxJoyDetails> js)
{
JoystickEvent e;
unsafe
{
while ((long)UnsafeNativeMethods.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
while ((long)Libc.read(js.Details.FileDescriptor, (void*)&e, (UIntPtr)sizeof(JoystickEvent)) > 0)
{
e.Type &= ~JoystickEventType.Init;
@ -352,78 +352,9 @@ namespace OpenTK.Platform.X11
return index_to_stick.ContainsKey(index);
}
#region UnsafeNativeMethods
struct EvdevInputId
{
public ushort BusType;
public ushort Vendor;
public ushort Product;
public ushort Version;
}
enum EvdevIoctlCode : uint
{
Id = ((byte)'E' << 8) | (0x02 << 0) //EVIOCGID, which is _IOR('E', 0x02, struct input_id)
}
struct JoystickEvent
{
public uint Time; // (u32) event timestamp in milliseconds
public short Value; // (s16) value
public JoystickEventType Type; // (u8) event type
public byte Number; // (u8) axis/button number
}
[Flags]
enum JoystickEventType : byte
{
Button = 0x01, // button pressed/released
Axis = 0x02, // joystick moved
Init = 0x80 // initial state of device
}
enum JoystickIoctlCode : uint
{
Version = 0x80046a01,
Axes = 0x80016a11,
Buttons = 0x80016a12,
Name128 = (2u << 30) | (0x6A << 8) | (0x13 << 0) | (128 << 16) //JSIOCGNAME(128), which is _IOC(_IO_READ, 'j', 0x13, len)
}
static readonly string JoystickPath = "/dev/input";
static readonly string JoystickPathLegacy = "/dev";
[Flags]
enum OpenFlags
{
NonBlock = 0x00000800
}
static class UnsafeNativeMethods
{
[DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, JoystickIoctlCode request, ref int data);
[DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, JoystickIoctlCode request, StringBuilder data);
[DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data);
[DllImport("libc", SetLastError = true)]
public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags);
[DllImport("libc", SetLastError = true)]
public static extern int close(int fd);
[DllImport("libc", SetLastError = true)]
unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count);
}
#endregion
#endregion
#region IDisposable Members
@ -443,7 +374,7 @@ namespace OpenTK.Platform.X11
}
watcher.Dispose();
foreach (JoystickDevice<X11JoyDetails> js in sticks)
foreach (JoystickDevice<LinuxJoyDetails> js in sticks)
{
CloseJoystick(js);
}
@ -452,7 +383,7 @@ namespace OpenTK.Platform.X11
}
}
~X11Joystick()
~LinuxJoystick()
{
Dispose(false);
}
@ -465,7 +396,7 @@ namespace OpenTK.Platform.X11
{
if (IsValid(index))
{
JoystickDevice<X11JoyDetails> js =
JoystickDevice<LinuxJoyDetails> js =
sticks[index_to_stick[index]];
PollJoystick(js);
return js.Details.State;
@ -478,7 +409,7 @@ namespace OpenTK.Platform.X11
JoystickCapabilities caps = new JoystickCapabilities();
if (IsValid(index))
{
JoystickDevice<X11JoyDetails> js = sticks[index_to_stick[index]];
JoystickDevice<LinuxJoyDetails> js = sticks[index_to_stick[index]];
caps = new JoystickCapabilities(
js.Axis.Count,
js.Button.Count,
@ -492,7 +423,7 @@ namespace OpenTK.Platform.X11
{
if (IsValid(index))
{
JoystickDevice<X11JoyDetails> js = sticks[index_to_stick[index]];
JoystickDevice<LinuxJoyDetails> js = sticks[index_to_stick[index]];
return js.Details.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 X11Keyboard keyboard = new X11Keyboard();
readonly X11Joystick joystick = new X11Joystick();
readonly Linux.LinuxJoystick joystick = new Linux.LinuxJoystick();
readonly IGamePadDriver gamepad = new MappedGamePadDriver();
internal X11Input()

View file

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