From 1739ed4138be8a1572600dd7b809b05359489fae Mon Sep 17 00:00:00 2001 From: aguahombre Date: Mon, 21 Jun 2021 19:42:00 +0100 Subject: [PATCH] Adds a pixel format parameter to Linux frame buffer platform setup. (#6101) * Add pixel format parameter to Linux frame buffer platform setup. Currently setup always changes the frame buffer pixel format to RGBA which results in the screen being cleared if the default pixel format is not RGBA (as on a Raspberry PI) . This clears any splash screen and leaves the screen blank for a period which is not a good UX. Now the frame buffer setup can select the correct pixel format or use null to leave the pixel format unchanged. * Remove unnecessary formatting changes. Add v0.10.x compatible constructor. * Keep old StartLinuxFbDev extension method for v0.10.x binary compatibility --- .../LinuxFramebufferPlatform.cs | 5 +- .../LockedFramebuffer.cs | 2 +- .../Output/FbdevOutput.cs | 88 ++++++++++++++----- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs index a6b70069c1..89f81a7649 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs @@ -132,7 +132,10 @@ public static class LinuxFramebufferPlatformExtensions { public static int StartLinuxFbDev(this T builder, string[] args, string fbdev = null, double scaling = 1) where T : AppBuilderBase, new() => - StartLinuxDirect(builder, args, new FbdevOutput(fbdev) {Scaling = scaling}); + StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: null) { Scaling = scaling }); + public static int StartLinuxFbDev(this T builder, string[] args, string fbdev, PixelFormat? format, double scaling) + where T : AppBuilderBase, new() => + StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: format) { Scaling = scaling }); public static int StartLinuxDrm(this T builder, string[] args, string card = null, double scaling = 1) where T : AppBuilderBase, new() => StartLinuxDirect(builder, args, new DrmOutput(card) {Scaling = scaling}); diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs index ed59166eb8..87c7b64c26 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs @@ -41,6 +41,6 @@ namespace Avalonia.LinuxFramebuffer public PixelSize Size => new PixelSize((int)_varInfo.xres, (int) _varInfo.yres); public int RowBytes => (int) _fixedInfo.line_length; public Vector Dpi { get; } - public PixelFormat Format => _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888; + public PixelFormat Format => _varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565 : _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888; } } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs index b83fe6cbe8..add744ee16 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs @@ -16,16 +16,33 @@ namespace Avalonia.LinuxFramebuffer private IntPtr _mappedAddress; public double Scaling { get; set; } - public FbdevOutput(string fileName = null) + /// + /// Create a Linux frame buffer device output + /// + /// The frame buffer device name. + /// Defaults to the value in environment variable FRAMEBUFFER or /dev/fb0 when FRAMEBUFFER is not set + public FbdevOutput(string fileName = null) : this(null, null) { - fileName = fileName ?? Environment.GetEnvironmentVariable("FRAMEBUFFER") ?? "/dev/fb0"; + } + + /// + /// Create a Linux frame buffer device output + /// + /// The frame buffer device name. + /// Defaults to the value in environment variable FRAMEBUFFER or /dev/fb0 when FRAMEBUFFER is not set + /// The required pixel format for the frame buffer. + /// A null value will leave the frame buffer in the current pixel format. + /// Otherwise sets the frame buffer to the required format + public FbdevOutput(string fileName, PixelFormat? format) + { + fileName ??= Environment.GetEnvironmentVariable("FRAMEBUFFER") ?? "/dev/fb0"; _fd = NativeUnsafeMethods.open(fileName, 2, 0); if (_fd <= 0) throw new Exception("Error: " + Marshal.GetLastWin32Error()); try { - Init(); + Init(format); } catch { @@ -34,25 +51,28 @@ namespace Avalonia.LinuxFramebuffer } } - void Init() + void Init(PixelFormat? format) { fixed (void* pnfo = &_varInfo) { if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo)) throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error()); - SetBpp(); + if (format.HasValue) + { + SetBpp(format.Value); - if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo)) - _varInfo.transp = new fb_bitfield(); + if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo)) + _varInfo.transp = new fb_bitfield(); - NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo); + NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo); - if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo)) - throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error()); + if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo)) + throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error()); - if (_varInfo.bits_per_pixel != 32) - throw new Exception("Unable to set 32-bit display mode"); + if (_varInfo.bits_per_pixel != 32) + throw new Exception("Unable to set 32-bit display mode"); + } } fixed(void*pnfo = &_fixedInfo) if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_FSCREENINFO, pnfo)) @@ -70,17 +90,43 @@ namespace Avalonia.LinuxFramebuffer } } - void SetBpp() + void SetBpp(PixelFormat format) { - _varInfo.bits_per_pixel = 32; - _varInfo.grayscale = 0; - _varInfo.red = _varInfo.blue = _varInfo.green = _varInfo.transp = new fb_bitfield + switch (format) { - length = 8 - }; - _varInfo.green.offset = 8; - _varInfo.blue.offset = 16; - _varInfo.transp.offset = 24; + case PixelFormat.Rgba8888: + _varInfo.bits_per_pixel = 32; + _varInfo.grayscale = 0; + _varInfo.red = _varInfo.blue = _varInfo.green = _varInfo.transp = new fb_bitfield + { + length = 8 + }; + _varInfo.green.offset = 8; + _varInfo.blue.offset = 16; + _varInfo.transp.offset = 24; + break; + case PixelFormat.Bgra8888: + _varInfo.bits_per_pixel = 32; + _varInfo.grayscale = 0; + _varInfo.red = _varInfo.blue = _varInfo.green = _varInfo.transp = new fb_bitfield + { + length = 8 + }; + _varInfo.green.offset = 8; + _varInfo.red.offset = 16; + _varInfo.transp.offset = 24; + break; + case PixelFormat.Rgb565: + _varInfo.bits_per_pixel = 16; + _varInfo.grayscale = 0; + _varInfo.red = _varInfo.blue = _varInfo.green = _varInfo.transp = new fb_bitfield(); + _varInfo.red.length = 5; + _varInfo.green.offset = 5; + _varInfo.green.length = 6; + _varInfo.blue.offset = 11; + _varInfo.blue.length = 5; + break; + } } public string Id { get; private set; }