diff --git a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
index 06f43d61..c0704837 100644
--- a/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
+++ b/Source/OpenTK/Platform/Linux/Bindings/Drm.cs
@@ -61,6 +61,9 @@ namespace OpenTK.Platform.Linux
[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);
diff --git a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
index 4c11eda6..05e1ffd9 100644
--- a/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
+++ b/Source/OpenTK/Platform/Linux/Bindings/Gbm.cs
@@ -65,6 +65,9 @@ namespace OpenTK.Platform.Linux
[DllImport(lib, EntryPoint = "gbm_create_device", CallingConvention = CallingConvention.Cdecl)]
public static extern Device CreateDevice(int fd);
+ [DllImport(lib, EntryPoint = "gbm_destroy_device", 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);
diff --git a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
index 14f6fb82..ad38c523 100644
--- a/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxDisplayDriver.cs
@@ -102,7 +102,7 @@ namespace OpenTK.Platform.Linux
try
{
FD = fd;
- QueryDisplays();
+ UpdateDisplays(fd);
}
finally
{
@@ -110,7 +110,76 @@ namespace OpenTK.Platform.Linux
}
}
- void QueryDisplays()
+ /// \internal
+ ///
+ /// Queries the specified GPU for connected displays and, optionally,
+ /// returns the list of displays.
+ ///
+ /// true, if at least one display is connected, false otherwise.
+ /// The fd for the GPU to query, obtained through open("/dev/dri/card0").
+ ///
+ /// If not null, this will contain a list instances,
+ /// one for each connected display.
+ ///
+ internal static bool QueryDisplays(int fd, List displays)
+ {
+ unsafe
+ {
+ bool has_displays = false;
+ if (displays != null)
+ {
+ displays.Clear();
+ }
+
+ ModeRes* resources = (ModeRes*)Drm.ModeGetResources(fd);
+ if (resources == null)
+ {
+ Debug.Print("[KMS] Drm.ModeGetResources failed.");
+ return false;
+ }
+ Debug.Print("[KMS] DRM found {0} connectors", resources->count_connectors);
+
+ // Search for a valid connector
+ ModeConnector* connector = null;
+ for (int i = 0; i < resources->count_connectors; i++)
+ {
+ connector = (ModeConnector*)Drm.ModeGetConnector(fd,
+ *(resources->connectors + i));
+ if (connector != null)
+ {
+ bool success = false;
+ LinuxDisplay display = null;
+ try
+ {
+ if (connector->connection == ModeConnection.Connected &&
+ connector->count_modes > 0)
+ {
+ success = QueryDisplay(fd, connector, out display);
+ has_displays |= success;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Print("[KMS] Failed to add display. Error: {0}", e);
+ }
+
+ if (success && displays != null)
+ {
+ displays.Add(display);
+ }
+ else
+ {
+ Drm.ModeFreeConnector((IntPtr)connector);
+ connector = null;
+ }
+ }
+ }
+
+ return has_displays;
+ }
+ }
+
+ void UpdateDisplays(int fd)
{
unsafe
{
@@ -119,33 +188,12 @@ namespace OpenTK.Platform.Linux
AvailableDevices.Clear();
DisplayIds.Clear();
- ModeRes* resources = (ModeRes*)Drm.ModeGetResources(FD);
- if (resources == null)
+ List displays = new List();
+ if (QueryDisplays(fd, displays))
{
- 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)
+ foreach (LinuxDisplay display in displays)
{
- 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;
- }
+ AddDisplay(display);
}
}
@@ -157,14 +205,13 @@ namespace OpenTK.Platform.Linux
}
}
- unsafe void AddDisplay(ModeConnector* c)
+ unsafe static ModeEncoder* GetEncoder(int fd, 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));
+ fd, *(c->encoders + i));
if (e != null)
{
if (e->encoder_id == c->encoder_id)
@@ -186,10 +233,14 @@ namespace OpenTK.Platform.Linux
else
{
Debug.Print("[KMS] Failed to find encoder for connector {0}", c->connector_id);
- return;
}
- ModeCrtc* crtc = (ModeCrtc*)Drm.ModeGetCrtc(FD, encoder->crtc_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}",
@@ -199,19 +250,14 @@ namespace OpenTK.Platform.Linux
{
Debug.Print("[KMS] Failed to find crtc {0} for encoder {1}",
encoder->crtc_id, encoder->encoder_id);
- return;
- }
-
- LinuxDisplay display = new LinuxDisplay((IntPtr)c, (IntPtr)encoder, (IntPtr)crtc);
- if (!DisplayIds.ContainsKey(display.Id))
- {
- Debug.Print("[KMS] Adding display {0} as {1}", display.Id, AvailableDevices.Count);
- DisplayIds.Add(display.Id, AvailableDevices.Count);
}
+ 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} modes", mode_count);
- List modes = new List(mode_count);
for (int i = 0; i < mode_count; i++)
{
ModeInfo* mode = display.pConnector->modes + i;
@@ -220,33 +266,89 @@ namespace OpenTK.Platform.Linux
Debug.Print("Mode {0}: {1}x{2} @{3}", i,
mode->hdisplay, mode->vdisplay, mode->vrefresh);
DisplayResolution res = GetDisplayResolution(mode);
- modes.Add(res);
+ modes[i] = res;
}
}
- ModeInfo current_mode = display.pCrtc->mode;
- DisplayResolution current = GetDisplayResolution(¤t_mode);
+ 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.
- System.Drawing.Rectangle bounds =
- new System.Drawing.Rectangle(
- AvailableDevices.Count == 0 ? 0 : AvailableDevices[0].Bounds.Right,
- 0,
- current.Width,
- current.Height);
+ 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((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, bounds, display);
+ modes, GetBounds(current), display);
- if (AvailableDevices.Count == 0)
+ if (is_primary)
{
Primary = device;
}
+ UpdateDisplayIndices(display, device);
+
Debug.Print("[KMS] Added DisplayDevice {0}", device);
- AvailableDevices.Add(device);
}
unsafe static DisplayResolution GetDisplayResolution(ModeInfo* mode)
diff --git a/Source/OpenTK/Platform/Linux/LinuxFactory.cs b/Source/OpenTK/Platform/Linux/LinuxFactory.cs
index 11e5e80b..45315d85 100644
--- a/Source/OpenTK/Platform/Linux/LinuxFactory.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxFactory.cs
@@ -69,8 +69,7 @@ namespace OpenTK.Platform.Linux
{
try
{
- DisplayDriver = new LinuxDisplayDriver(test_fd);
- if (DisplayDriver.GetDisplay(DisplayIndex.Primary) != null)
+ if (LinuxDisplayDriver.QueryDisplays(test_fd, null))
{
fd = test_fd;
break;
@@ -135,6 +134,30 @@ namespace OpenTK.Platform.Linux
#endregion
+ #region Protected Members
+
+ protected override void Dispose(bool manual)
+ {
+ if (display != IntPtr.Zero)
+ {
+ Egl.Terminate(display);
+ display = IntPtr.Zero;
+ }
+ if (gbm_device != IntPtr.Zero)
+ {
+ Gbm.DestroyDevice(gbm_device);
+ gbm_device = IntPtr.Zero;
+ }
+ if (fd >= 0)
+ {
+ 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)
@@ -144,7 +167,7 @@ namespace OpenTK.Platform.Linux
public override IDisplayDeviceDriver CreateDisplayDeviceDriver()
{
- return DisplayDriver;
+ return new LinuxDisplayDriver(fd);
}
public override IGraphicsContext CreateGLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags)
diff --git a/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
index f42d1708..eaa6be42 100644
--- a/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
+++ b/Source/OpenTK/Platform/Linux/LinuxGraphicsContext.cs
@@ -295,3 +295,5 @@ namespace OpenTK.Platform.Linux
}
}
+
+