[KMS] Added new Linux KMS driver

This commit is contained in:
thefiddler 2014-06-24 19:27:38 +02:00
parent c3451530a6
commit 6f6798de62
12 changed files with 643 additions and 168 deletions

View file

@ -101,6 +101,15 @@ namespace Examples
public static void Main(string[] args) public static void Main(string[] args)
{ {
Trace.Listeners.Add(new ConsoleTraceListener()); Trace.Listeners.Add(new ConsoleTraceListener());
Tests.GameWindowStates.Main();
return;
using (var gw = new GameWindow())
{
gw.KeyDown += (sender, e) => gw.Exit();
gw.Run(60);
}
return;
if (args.Length > 0) if (args.Length > 0)
{ {

View file

@ -343,9 +343,6 @@
<Compile Include="Platform\X11\X11WindowInfo.cs"> <Compile Include="Platform\X11\X11WindowInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Platform\X11\X11Joystick.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Platform\X11\Functions.cs"> <Compile Include="Platform\X11\Functions.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
@ -809,8 +806,13 @@
<Compile Include="Platform\Linux\Bindings\Drm.cs" /> <Compile Include="Platform\Linux\Bindings\Drm.cs" />
<Compile Include="Platform\Linux\LinuxFactory.cs" /> <Compile Include="Platform\Linux\LinuxFactory.cs" />
<Compile Include="Platform\Linux\LinuxNativeWindow.cs" /> <Compile Include="Platform\Linux\LinuxNativeWindow.cs" />
<Compile Include="Platform\Linux\Bindings\Linux.cs" />
<Compile Include="Platform\Linux\Bindings\Gbm.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" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>

View file

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

View file

@ -36,20 +36,115 @@ namespace OpenTK.Platform.Linux
{ {
const string lib = "libdrm"; const string lib = "libdrm";
[DllImport(lib, EntryPoint = "drmModeGetCrtc", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ModeGetCrtc(int fd, uint crtcId);
[DllImport(lib, EntryPoint = "drmModeFreeConnector", CallingConvention = CallingConvention.Cdecl)]
public static extern void ModeFreeConnector(IntPtr ptr);
[DllImport(lib, EntryPoint = "drmModeFreeEncoder", CallingConvention = CallingConvention.Cdecl)]
public static extern void ModeFreeEncoder(IntPtr ptr);
[DllImport(lib, EntryPoint = "drmModeGetConnector", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ModeGetConnector(int fd, uint connector_id);
[DllImport(lib, EntryPoint = "drmModeGetEncoder", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ModeGetEncoder(int fd, uint encoder_id);
[DllImport(lib, EntryPoint = "drmModeGetResources", CallingConvention = CallingConvention.Cdecl)] [DllImport(lib, EntryPoint = "drmModeGetResources", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ModeGetResources(int fd); public static extern IntPtr ModeGetResources(int fd);
[DllImport(lib, EntryPoint = "drmModeSetCrtc", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int ModeSetCrtc(int fd, uint crtcId, uint bufferId,
uint x, uint y, uint* connectors, int count, ModeInfo* mode);
} }
struct ModeRes enum ModeConnection : byte
{
Connected = 1,
Disconnected = 2,
Unknown = 3
}
enum ModeSubPixel : byte
{
Unknown = 1,
HorizontalRgb = 2,
HorizontalBgr = 3,
VerticalRgb = 4,
VerticalBgr = 5,
None = 6
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct ModeConnector
{
public uint connector_id;
public uint encoder_id;
public uint connector_type;
public uint connector_type_id;
public ModeConnection connection;
public uint mmWidth, mmHeight;
public ModeSubPixel subpixel;
public int count_modes;
public ModeInfo* modes;
public int count_props;
public uint *props;
public ulong *prop_values;
public int count_encoders;
public uint *encoders;
}
struct ModeCrtc
{
public uint crtc_id;
public uint buffer_id;
public uint x, y;
public uint width, height;
public int mode_valid;
public ModeInfo mode;
public int gamma_size;
}
struct ModeEncoder
{
public uint encoder_id;
public uint encoder_type;
public uint crtc_id;
public uint possible_crtcs;
public uint possible_clones;
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct ModeInfo
{
public uint clock;
public ushort hdisplay, hsync_start, hsync_end, htotal, hskew;
public ushort vdisplay, vsync_start, vsync_end, vtotal, vscan;
public uint vrefresh; // refresh rate * 1000
public uint flags;
public uint type;
public fixed sbyte name[32];
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct ModeRes
{ {
public int count_fbs; public int count_fbs;
public IntPtr fbs; //uint* public uint* fbs;
public int count_crtcs; public int count_crtcs;
public IntPtr crtcs; //uint* public uint* crtcs;
public int count_connectors; public int count_connectors;
public IntPtr connectors; //uint* public uint* connectors;
public int count_encoders; public int count_encoders;
public IntPtr encoders; //uint* public uint* encoders;
public int min_width, max_width; public int min_width, max_width;
public int min_height, max_height; public int min_height, max_height;
} }

View file

@ -29,22 +29,62 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace OpenTK.Platform.Linux namespace OpenTK.Platform.Linux
{ {
class Linux class Libc
{ {
[DllImport("glib", EntryPoint = "open", CallingConvention = CallingConvention.Cdecl)] const string lib = "libc";
public static extern int Open(string pathname, OpenFlags flags);
[DllImport(lib)]
public static extern int ioctl(int d, JoystickIoctlCode request, ref int data);
[DllImport(lib)]
public static extern int ioctl(int d, JoystickIoctlCode request, StringBuilder data);
[DllImport(lib)]
public static extern int ioctl(int d, EvdevIoctlCode request, out EvdevInputId data);
[DllImport(lib)]
public static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, OpenFlags flags);
[DllImport(lib)]
public static extern int close(int fd);
[DllImport(lib)]
unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count);
} }
[Flags] [Flags]
enum OpenFlags enum OpenFlags
{ {
ReadOnly = 0, ReadOnly = 0x0000,
WriteOnly = 1, WriteOnly = 0x0001,
ReadWrite = 2, ReadWrite = 0x0002,
CloseOnExec = 4096 NonBlock = 0x0800,
CloseOnExec = 0x0080000
}
enum EvdevIoctlCode : uint
{
Id = ((byte)'E' << 8) | (0x02 << 0) //EVIOCGID, which is _IOR('E', 0x02, struct input_id)
}
[Flags]
enum JoystickEventType : byte
{
Button = 0x01, // button pressed/released
Axis = 0x02, // joystick moved
Init = 0x80 // initial state of device
}
enum JoystickIoctlCode : uint
{
Version = 0x80046a01,
Axes = 0x80016a11,
Buttons = 0x80016a12,
Name128 = (2u << 30) | (0x6A << 8) | (0x13 << 0) | (128 << 16) //JSIOCGNAME(128), which is _IOC(_IO_READ, 'j', 0x13, len)
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -64,5 +104,21 @@ namespace OpenTK.Platform.Linux
public IntPtr mtime; /* time of last modification */ public IntPtr mtime; /* time of last modification */
public IntPtr ctime; /* time of last status change */ 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,282 @@
#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
{
class LinuxDisplayDriver : DisplayDeviceBase
{
unsafe class LinuxDisplay
{
public ModeConnector* Connector;
public ModeEncoder* Encoder;
public ModeCrtc* Crtc;
public ModeInfo Mode
{
get
{
if (Crtc == null)
throw new InvalidOperationException();
return Crtc->mode;
}
}
public ModeInfo OriginalMode;
public int Id
{
get
{
if (Crtc == null)
throw new InvalidOperationException();
return (int)Crtc->crtc_id;
}
}
public LinuxDisplay(ModeConnector* c, ModeEncoder* e, ModeCrtc* r)
{
Connector = c;
Encoder = e;
Crtc = r;
OriginalMode = Crtc->mode; // in case we change resolution later on
}
}
readonly int FD;
readonly GbmDevice Device;
readonly Dictionary<int, int> DisplayIds =
new Dictionary<int, int>();
public LinuxDisplayDriver(int fd)
{
FD = fd;
QueryDisplays();
}
void QueryDisplays()
{
unsafe
{
lock (this)
{
AvailableDevices.Clear();
DisplayIds.Clear();
ModeRes* resources = (ModeRes*)Drm.ModeGetResources(FD);
if (resources == null)
{
throw new NotSupportedException("[KMS] DRM ModeGetResources failed");
}
Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors);
// Search for a valid connector
ModeConnector* connector = null;
for (int i = 0; i < resources->count_connectors; i++)
{
connector = (ModeConnector*)Drm.ModeGetConnector(FD,
*(resources->connectors + i));
if (connector != null)
{
if (connector->connection == ModeConnection.Connected &&
connector->count_modes > 0)
{
// Connector found!
AddDisplay(connector);
}
else
{
// This is not the display we are looking for
Drm.ModeFreeConnector((IntPtr)connector);
connector = null;
}
}
}
if (AvailableDevices.Count == 0)
{
Debug.Print("[KMS] Failed to find any active displays");
}
}
}
}
unsafe void AddDisplay(ModeConnector* c)
{
// Find corresponding encoder
ModeEncoder* encoder = null;
for (int i = 0; i < c->count_encoders && encoder == null; i++)
{
ModeEncoder* e = (ModeEncoder*)Drm.ModeGetEncoder(
FD, *(c->encoders + i));
if (e != null)
{
if (e->encoder_id == c->encoder_id)
{
encoder = e;
}
else
{
Drm.ModeFreeEncoder((IntPtr)e);
}
}
}
if (encoder != null)
{
Debug.Print("[KMS] Encoder {0} found for connector {1}",
encoder->encoder_id, c->connector_id);
}
else
{
Debug.Print("[KMS] Failed to find encoder for connector {0}", c->connector_id);
return;
}
ModeCrtc* crtc = (ModeCrtc*)Drm.ModeGetCrtc(FD, encoder->crtc_id);
if (crtc != null)
{
Debug.Print("[KMS] CRTC {0} found for encoder {1}",
encoder->crtc_id, encoder->encoder_id);
}
else
{
Debug.Print("[KMS] Failed to find crtc {0} for encoder {1}",
encoder->crtc_id, encoder->encoder_id);
return;
}
LinuxDisplay display = new LinuxDisplay(c, encoder, crtc);
if (!DisplayIds.ContainsKey(display.Id))
{
DisplayIds.Add(display.Id, AvailableDevices.Count);
}
List<DisplayResolution> modes = new List<DisplayResolution>();
for (int i = 0; i < display.Connector->count_modes; i++)
{
ModeInfo* mode = display.Connector->modes + i;
DisplayResolution res = GetDisplayResolution(mode);
modes.Add(res);
}
ModeInfo current_mode = display.Mode;
DisplayResolution current = GetDisplayResolution(&current_mode);
// Note: since we are not running a display manager, we are free
// to choose the display layout for multiple displays ourselves.
// We choose the simplest layout: displays are laid out side-by-side
// from left to right. Primary display is the first display we encounter.
System.Drawing.Rectangle bounds =
new System.Drawing.Rectangle(
AvailableDevices.Count == 0 ? 0 : AvailableDevices[0].Bounds.Right,
0,
current.Width,
current.Height);
DisplayDevice device = new DisplayDevice(
current,
AvailableDevices.Count == 0,
modes,
bounds,
display);
if (AvailableDevices.Count == 0)
{
Primary = device;
}
AvailableDevices.Add(device);
}
unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode)
{
if (mode == null)
throw new ArgumentNullException();
return new DisplayResolution(
0, 0,
mode->htotal, mode->vtotal,
32, // This is actually part of the framebuffer, not the DisplayResolution
mode->vrefresh / 1000.0f);
}
unsafe static ModeInfo* GetModeInfo(LinuxDisplay display, DisplayResolution resolution)
{
for (int i = 0; i < display.Connector->count_modes; i++)
{
ModeInfo* mode = display.Connector->modes + i;
if (mode != null &&
mode->htotal == resolution.Width &&
mode->vtotal == resolution.Height)
{
return mode;
}
}
return null;
}
#region IDisplayDeviceDriver
public override bool TryChangeResolution(DisplayDevice device, DisplayResolution resolution)
{
unsafe
{
LinuxDisplay display = (LinuxDisplay)device.Id;
ModeInfo* mode = GetModeInfo(display, resolution);
uint connector_id = display.Connector->connector_id;
if (mode != null)
{
return Drm.ModeSetCrtc(FD, (uint)display.Id, 0, 0, 0,
&connector_id, 1, mode) == 0;
}
return false;
}
}
public override bool TryRestoreResolution(DisplayDevice device)
{
unsafe
{
LinuxDisplay display = (LinuxDisplay)device.Id;
uint connector_id = display.Connector->connector_id;
ModeInfo mode = display.OriginalMode;
return Drm.ModeSetCrtc(FD, (uint)display.Id, 0, 0, 0,
&connector_id, 1, &mode) == 0;
}
}
#endregion
}
}

View file

@ -32,70 +32,93 @@ using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using OpenTK.Graphics; using OpenTK.Graphics;
using OpenTK.Input; using OpenTK.Input;
using OpenTK.Platform.Egl;
namespace OpenTK.Platform.Linux namespace OpenTK.Platform.Linux
{ {
using Egl = OpenTK.Platform.Egl.Egl;
// Linux KMS platform // Linux KMS platform
class LinuxFactory : PlatformFactoryBase class LinuxFactory : PlatformFactoryBase
{ {
int fd; int fd;
GbmDevice device; GbmDevice gbm_device;
IntPtr display; IntPtr display;
IJoystickDriver2 JoystickDriver;
IDisplayDeviceDriver DisplayDriver;
public LinuxFactory() public LinuxFactory()
{
SetupEgl();
}
#region Private Members
void SetupEgl()
{ {
// Todo: support multi-GPU systems // Todo: support multi-GPU systems
string gpu = "/dev/dri/card0"; string gpu = "/dev/dri/card0";
fd = Linux.Open(gpu, OpenFlags.ReadOnly | OpenFlags.CloseOnExec); fd = Libc.open(gpu, OpenFlags.ReadWrite | OpenFlags.CloseOnExec);
if (fd < 0) if (fd < 0)
{ {
throw new NotSupportedException("No KMS-capable GPU available"); throw new NotSupportedException("[KMS] No KMS-capable GPU available");
} }
Debug.Print("GPU {0} opened as fd:{1}", gpu, fd); Debug.Print("[KMS] GPU '{0}' opened as fd:{1}", gpu, fd);
IntPtr dev = Gbm.CreateDevice(fd); IntPtr dev = Gbm.CreateDevice(fd);
if (dev == IntPtr.Zero) if (dev == IntPtr.Zero)
{ {
throw new NotSupportedException("Failed to create GBM device"); throw new NotSupportedException("[KMS] Failed to create GBM device");
} }
device = (GbmDevice)Marshal.PtrToStructure(dev, typeof(GbmDevice)); gbm_device = (GbmDevice)Marshal.PtrToStructure(dev, typeof(GbmDevice));
Debug.Print("GBM {0:x} '{1}' created successfully", Debug.Print("[KMS] GBM {0:x} created successfully", dev);
dev, Marshal.PtrToStringAnsi(device.name));
display = Egl.Egl.GetDisplay(dev); display = Egl.GetDisplay(dev);
if (display == IntPtr.Zero) if (display == IntPtr.Zero)
{ {
throw new NotSupportedException("Failed to create EGL display"); throw new NotSupportedException("[KMS] Failed to create EGL display");
} }
Debug.Print("EGL display {0:x} created successfully", display); Debug.Print("[KMS] EGL display {0:x} created successfully", display);
int major, minor; int major, minor;
if (!Egl.Egl.Initialize(display, out major, out minor)) if (!Egl.Initialize(display, out major, out minor))
{ {
int error = Egl.Egl.GetError(); int error = Egl.GetError();
throw new NotSupportedException("Failed to initialize EGL display. Error code: " + error); throw new NotSupportedException("[KMS] Failed to initialize EGL display. Error code: " + error);
} }
Debug.Print("EGL {0}.{1} initialized successfully on display {2:x}", major, minor, display); Debug.Print("[KMS] EGL {0}.{1} initialized successfully on display {2:x}", major, minor, display);
} }
public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device) #endregion
#region IPlatformFactory Members
public override INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice display_device)
{ {
return new LinuxNativeWindow(x, y, width, height, title, mode, options, device); return new LinuxNativeWindow(display, gbm_device, width, height, title, mode, options, display_device);
} }
public override IDisplayDeviceDriver CreateDisplayDeviceDriver() public override IDisplayDeviceDriver CreateDisplayDeviceDriver()
{ {
throw new NotImplementedException(); lock (this)
{
DisplayDriver = DisplayDriver ?? new LinuxDisplayDriver(fd);
return DisplayDriver;
}
} }
public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags) public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags)
{ {
throw new NotImplementedException(); return new EglUnixContext(mode, (EglWindowInfo)window, shareContext, major, minor, flags);
} }
public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext() public override GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext()
{ {
throw new NotImplementedException(); return (GraphicsContext.GetCurrentContextDelegate)delegate
{
return new ContextHandle(Egl.GetCurrentContext());
};
} }
public override IKeyboardDriver2 CreateKeyboardDriver() public override IKeyboardDriver2 CreateKeyboardDriver()
@ -110,8 +133,14 @@ namespace OpenTK.Platform.Linux
public override IJoystickDriver2 CreateJoystickDriver() public override IJoystickDriver2 CreateJoystickDriver()
{ {
throw new NotImplementedException(); lock (this)
{
JoystickDriver = JoystickDriver ?? new LinuxJoystick();
return JoystickDriver;
} }
} }
#endregion
}
} }

View file

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

View file

@ -28,42 +28,74 @@
#endregion #endregion
using System; using System;
using System.Drawing;
using OpenTK.Graphics;
using OpenTK.Platform.Egl;
namespace OpenTK.Platform.Linux namespace OpenTK.Platform.Linux
{ {
using Egl = OpenTK.Platform.Egl.Egl;
class LinuxNativeWindow : NativeWindowBase class LinuxNativeWindow : NativeWindowBase
{ {
LinuxWindowInfo window_info;
string title;
Icon icon;
bool exists;
Rectangle bounds;
Size client_size;
public LinuxNativeWindow(IntPtr display, GbmDevice device,
int width, int height, string title, GraphicsMode mode, GameWindowFlags options,
DisplayDevice display_device)
{
Title = title;
bounds = new Rectangle(0, 0, width, height);
client_size = bounds.Size;
//window_info = new LinuxWindowInfo(
// Egl.CreateWindowSurface(
exists = true;
}
#region INativeWindow Members #region INativeWindow Members
public override void Close() public override void Close()
{ {
throw new NotImplementedException(); exists = false;
} }
public override System.Drawing.Point PointToClient(System.Drawing.Point point) public override Point PointToClient(Point point)
{ {
throw new NotImplementedException(); // Todo
return point;
} }
public override System.Drawing.Point PointToScreen(System.Drawing.Point point) public override Point PointToScreen(Point point)
{ {
throw new NotImplementedException(); // Todo
return point;
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
throw new NotImplementedException(); // Todo
} }
public override System.Drawing.Icon Icon public override Icon Icon
{ {
get get
{ {
throw new NotImplementedException(); return icon;
} }
set set
{ {
throw new NotImplementedException(); if (icon != value)
{
icon = value;
OnIconChanged(EventArgs.Empty);
}
} }
} }
@ -71,11 +103,15 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return title;
} }
set set
{ {
throw new NotImplementedException(); if (title != value)
{
title = value;
OnTitleChanged(EventArgs.Empty);
}
} }
} }
@ -83,7 +119,7 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return true;
} }
} }
@ -91,11 +127,10 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return true;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
@ -103,7 +138,7 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return exists;
} }
} }
@ -111,7 +146,7 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return window_info;
} }
} }
@ -119,11 +154,10 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return WindowState.Fullscreen;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
@ -131,35 +165,32 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return WindowBorder.Hidden;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
public override System.Drawing.Rectangle Bounds public override Rectangle Bounds
{ {
get get
{ {
throw new NotImplementedException(); return bounds;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
public override System.Drawing.Size ClientSize public override Size ClientSize
{ {
get get
{ {
throw new NotImplementedException(); return client_size;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
@ -167,11 +198,10 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return false;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
@ -179,18 +209,14 @@ namespace OpenTK.Platform.Linux
{ {
get get
{ {
throw new NotImplementedException(); return MouseCursor.Empty;
} }
set set
{ {
throw new NotImplementedException();
} }
} }
#endregion #endregion
} }
} }

View file

@ -0,0 +1,43 @@
#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 OpenTK.Platform.Egl;
namespace OpenTK.Platform.Linux
{
class LinuxWindowInfo : EglWindowInfo
{
public LinuxWindowInfo(IntPtr handle, IntPtr display, IntPtr surface)
: base(handle, display, surface)
{
}
}
}

View file

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

View file

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