diff --git a/src/Linux/Avalonia.LinuxFramebuffer/DrmConnectorType.cs b/src/Linux/Avalonia.LinuxFramebuffer/DrmConnectorType.cs
new file mode 100644
index 0000000000..4e2b3f3ffe
--- /dev/null
+++ b/src/Linux/Avalonia.LinuxFramebuffer/DrmConnectorType.cs
@@ -0,0 +1,25 @@
+namespace Avalonia.LinuxFramebuffer;
+
+///
+/// specific the type of connector is HDMI-A, DVI, DisplayPort, etc.
+///
+public enum DrmConnectorType : uint
+{
+ None,
+ VGA,
+ DVI_I,
+ DVI_D,
+ DVI_A,
+ Composite,
+ S_Video,
+ LVDS,
+ Component,
+ DIN,
+ DisplayPort,
+ HDMI_A,
+ HDMI_B,
+ TV,
+ eDP,
+ Virtual,
+ DSI,
+}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs b/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs
index 3439f0edae..6ebb8f9656 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs
@@ -1,8 +1,10 @@
-using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Media;
namespace Avalonia.LinuxFramebuffer
{
+ ///
+ /// DRM Output Options
+ ///
public class DrmOutputOptions
{
///
@@ -28,5 +30,17 @@ namespace Avalonia.LinuxFramebuffer
/// If NULL preferred mode will be used.
///
public PixelSize? VideoMode { get; set; }
+
+ ///
+ /// Specific whether our connector is HDMI-A, DVI, DisplayPort, etc.
+ /// If NULL preferred connector will be used.
+ ///
+ public DrmConnectorType? ConnectorType { get; init; }
+
+ ///
+ /// Specific whether connector id using for
+ /// If NULL preferred connector id will be used
+ ///
+ public uint? ConnectorType_Id { get; init; }
}
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs
index 850ea6d4a6..6febe6c26b 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmBindings.cs
@@ -1,7 +1,8 @@
using System;
-using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
@@ -9,6 +10,7 @@ using static Avalonia.LinuxFramebuffer.Output.LibDrm;
namespace Avalonia.LinuxFramebuffer.Output
{
+ [DebuggerDisplay("{Name}")]
public unsafe class DrmConnector
{
private static string[] KnownConnectorTypes =
@@ -25,18 +27,21 @@ namespace Avalonia.LinuxFramebuffer.Output
internal uint EncoderId { get; }
internal List EncoderIds { get; } = new List();
public List Modes { get; } = new List();
+ public DrmConnectorType ConnectorType { get; }
+ public uint ConnectorType_Id { get; }
internal DrmConnector(drmModeConnector* conn)
{
Connection = conn->connection;
Id = conn->connector_id;
SizeMm = new Size(conn->mmWidth, conn->mmHeight);
SubPixel = conn->subpixel;
- for (var c = 0; c < conn->count_encoders;c++)
+ for (var c = 0; c < conn->count_encoders; c++)
EncoderIds.Add(conn->encoders[c]);
EncoderId = conn->encoder_id;
- for(var c=0; ccount_modes; c++)
+ for (var c = 0; c < conn->count_modes; c++)
Modes.Add(new DrmModeInfo(ref conn->modes[c]));
-
+ ConnectorType = (DrmConnectorType)conn->connector_type;
+ ConnectorType_Id = conn->connector_type_id;
if (conn->connector_type > KnownConnectorTypes.Length - 1)
Name = $"Unknown({conn->connector_type})-{conn->connector_type_id}";
else
@@ -77,19 +82,17 @@ namespace Avalonia.LinuxFramebuffer.Output
}
}
}
-
-
-
+
public unsafe class DrmResources
{
- public List Connectors { get; }= new List();
+ public List Connectors { get; } = new List();
internal Dictionary Encoders { get; } = new Dictionary();
public DrmResources(int fd, bool connectorsForceProbe = false)
{
var res = drmModeGetResources(fd);
if (res == null)
throw new Win32Exception("drmModeGetResources failed");
-
+
var crtcs = new drmModeCrtc[res->count_crtcs];
for (var c = 0; c < res->count_crtcs; c++)
{
@@ -97,22 +100,20 @@ namespace Avalonia.LinuxFramebuffer.Output
crtcs[c] = *crtc;
drmModeFreeCrtc(crtc);
}
-
+
for (var c = 0; c < res->count_encoders; c++)
{
var enc = drmModeGetEncoder(fd, res->encoders[c]);
Encoders[res->encoders[c]] = new DrmEncoder(*enc, crtcs);
drmModeFreeEncoder(enc);
}
-
+
for (var c = 0; c < res->count_connectors; c++)
{
var conn = connectorsForceProbe ? drmModeGetConnector(fd, res->connectors[c]) : drmModeGetConnectorCurrent(fd, res->connectors[c]);
Connectors.Add(new DrmConnector(conn));
drmModeFreeConnector(conn);
}
-
-
}
internal void Dump()
@@ -133,38 +134,39 @@ namespace Avalonia.LinuxFramebuffer.Output
Print(2, "Modes");
foreach (var m in conn.Modes)
Print(3, $"{m.Name} {(m.IsPreferred ? "PREFERRED" : "")}");
-
-
}
}
}
-
+
public unsafe class DrmCard : IDisposable
{
public int Fd { get; private set; }
public DrmCard(string path = null)
{
- if(path == null)
+ if (path == null)
{
var files = Directory.GetFiles("/dev/dri/");
- foreach(var file in files)
+ foreach (var file in files)
{
var match = Regex.Match(file, "card[0-9]+");
- if(match.Success)
+ if (match.Success)
{
Fd = open(file, 2, 0);
- if(Fd != -1) break;
- }
+ if (Fd != -1)
+ break;
+ }
}
- if(Fd == -1) throw new Win32Exception("Couldn't open /dev/dri/card[0-9]+");
+ if (Fd == -1)
+ throw new Win32Exception("Couldn't open /dev/dri/card[0-9]+");
}
- else
+ else
{
Fd = open(path, 2, 0);
- if(Fd == -1) throw new Win32Exception($"Couldn't open {path}");
+ if (Fd == -1)
+ throw new Win32Exception($"Couldn't open {path}");
}
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
index 1cea3ac9ba..ac87c9c796 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
@@ -39,29 +39,42 @@ namespace Avalonia.LinuxFramebuffer.Output
public IPlatformGraphicsContext GetSharedContext() => _context;
}
-
+
public IPlatformGraphics PlatformGraphics { get; private set; }
public DrmOutput(DrmCard card, DrmResources resources, DrmConnector connector, DrmModeInfo modeInfo,
DrmOutputOptions options = null)
{
- if(options != null)
+ if (options != null)
_outputOptions = options;
Init(card, resources, connector, modeInfo);
}
-
+
public DrmOutput(string path = null, bool connectorsForceProbe = false, DrmOutputOptions options = null)
{
- if(options != null)
+ if (options != null)
_outputOptions = options;
-
+
var card = new DrmCard(path);
var resources = card.GetResources(connectorsForceProbe);
+ IEnumerable connectors = resources.Connectors;
+
+ if (options?.ConnectorType is { } connectorType)
+ {
+ connectors = connectors.Where(c => c.ConnectorType == connectorType);
+ }
+
+ if (options?.ConnectorType_Id is { } connectorType_Id)
+ {
+ connectors = connectors.Where(c => c.ConnectorType_Id == connectorType_Id);
+ }
+
var connector =
- resources.Connectors.FirstOrDefault(x => x.Connection == DrmModeConnection.DRM_MODE_CONNECTED);
- if(connector == null)
+ connectors.FirstOrDefault(x => x.Connection == DrmModeConnection.DRM_MODE_CONNECTED);
+
+ if (connector == null)
throw new InvalidOperationException("Unable to find connected DRM connector");
DrmModeInfo mode = null;
@@ -72,12 +85,12 @@ namespace Avalonia.LinuxFramebuffer.Output
.FirstOrDefault(x => x.Resolution.Width == options.VideoMode.Value.Width &&
x.Resolution.Height == options.VideoMode.Value.Height);
}
-
+
mode ??= connector.Modes.OrderByDescending(x => x.IsPreferred)
.ThenByDescending(x => x.Resolution.Width * x.Resolution.Height)
//.OrderByDescending(x => x.Resolution.Width * x.Resolution.Height)
.FirstOrDefault();
- if(mode == null)
+ if (mode == null)
throw new InvalidOperationException("Unable to find a usable DRM mode");
Init(card, resources, connector, mode);
}
@@ -112,15 +125,15 @@ namespace Avalonia.LinuxFramebuffer.Output
if (data != IntPtr.Zero)
return (uint)data.ToInt32();
- var w = gbm_bo_get_width(bo);
+ var w = gbm_bo_get_width(bo);
var h = gbm_bo_get_height(bo);
var stride = gbm_bo_get_stride(bo);
var handle = gbm_bo_get_handle(bo).u32;
var format = gbm_bo_get_format(bo);
// prepare for the new ioctl call
- var handles = new uint[] {handle, 0, 0, 0};
- var pitches = new uint[] {stride, 0, 0, 0};
+ var handles = new uint[] { handle, 0, 0, 0 };
+ var pitches = new uint[] { stride, 0, 0, 0 };
var offsets = new uint[4];
var ret = drmModeAddFB2(_card.Fd, w, h, format, handles, pitches,
@@ -137,12 +150,12 @@ namespace Avalonia.LinuxFramebuffer.Output
}
gbm_bo_set_user_data(bo, new IntPtr((int)fbHandle), FbDestroyDelegate);
-
-
+
+
return fbHandle;
}
-
-
+
+
void Init(DrmCard card, DrmResources resources, DrmConnector connector, DrmModeInfo modeInfo)
{
FbDestroyDelegate = FbDestroyCallback;
@@ -159,7 +172,7 @@ namespace Avalonia.LinuxFramebuffer.Output
foreach (var encId in connector.EncoderIds)
{
if (resources.Encoders.TryGetValue(encId, out encoder)
- && encoder.PossibleCrtcs.Count>0)
+ && encoder.PossibleCrtcs.Count > 0)
return encoder.PossibleCrtcs.First().crtc_id;
}
@@ -171,7 +184,7 @@ namespace Avalonia.LinuxFramebuffer.Output
var device = gbm_create_device(card.Fd);
_gbmTargetSurface = gbm_surface_create(device, modeInfo.Resolution.Width, modeInfo.Resolution.Height,
GbmColorFormats.GBM_FORMAT_XRGB8888, GbmBoFlags.GBM_BO_USE_SCANOUT | GbmBoFlags.GBM_BO_USE_RENDERING);
- if(_gbmTargetSurface == IntPtr.Zero)
+ if (_gbmTargetSurface == IntPtr.Zero)
throw new InvalidOperationException("Unable to create GBM surface");
_eglDisplay = new EglDisplay(
@@ -197,7 +210,7 @@ namespace Avalonia.LinuxFramebuffer.Output
var initialBufferSwappingColorA = _outputOptions.InitialBufferSwappingColor.A / 255.0f;
using (_deferredContext.MakeCurrent(_eglSurface))
{
- _deferredContext.GlInterface.ClearColor(initialBufferSwappingColorR, initialBufferSwappingColorG,
+ _deferredContext.GlInterface.ClearColor(initialBufferSwappingColorR, initialBufferSwappingColorG,
initialBufferSwappingColorB, initialBufferSwappingColorA);
_deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT);
_eglSurface.SwapBuffers();
@@ -208,30 +221,30 @@ namespace Avalonia.LinuxFramebuffer.Output
var connectorId = connector.Id;
var mode = modeInfo.Mode;
-
+
var res = drmModeSetCrtc(_card.Fd, _crtcId, fbId, 0, 0, &connectorId, 1, &mode);
if (res != 0)
throw new Win32Exception(res, "drmModeSetCrtc failed");
_mode = mode;
_currentBo = bo;
-
+
if (_outputOptions.EnableInitialBufferSwapping)
{
//Go trough two cycles of buffer swapping (there are render artifacts otherwise)
- for(var c=0;c<2;c++)
+ for (var c = 0; c < 2; c++)
using (CreateGlRenderTarget().BeginDraw())
{
- _deferredContext.GlInterface.ClearColor(initialBufferSwappingColorR, initialBufferSwappingColorG,
+ _deferredContext.GlInterface.ClearColor(initialBufferSwappingColorR, initialBufferSwappingColorG,
initialBufferSwappingColorB, initialBufferSwappingColorA);
_deferredContext.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT);
}
}
-
+
}
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() => new RenderTarget(this);
-
+
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
@@ -240,7 +253,7 @@ namespace Avalonia.LinuxFramebuffer.Output
"This platform backend can only create render targets for its primary context");
return CreateGlRenderTarget();
}
-
+
class RenderTarget : IGlPlatformSurfaceRenderTarget
{
private readonly DrmOutput _parent;
@@ -269,7 +282,7 @@ namespace Avalonia.LinuxFramebuffer.Output
{
_parent._deferredContext.GlInterface.Flush();
_parent._eglSurface.SwapBuffers();
-
+
var nextBo = gbm_surface_lock_front_buffer(_parent._gbmTargetSurface);
if (nextBo == IntPtr.Zero)
{
@@ -292,7 +305,8 @@ namespace Avalonia.LinuxFramebuffer.Output
var cbHandle = GCHandle.Alloc(flipCb);
var ctx = new DrmEventContext
{
- version = 4, page_flip_handler = Marshal.GetFunctionPointerForDelegate(flipCb)
+ version = 4,
+ page_flip_handler = Marshal.GetFunctionPointerForDelegate(flipCb)
};
while (waitingForFlip)
{