A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

131 lines
4.4 KiB

using System;
using System.Runtime.InteropServices;
using System.Text;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer
{
public sealed unsafe class LinuxFramebuffer : IFramebufferPlatformSurface, IDisposable
{
private readonly Vector _dpi;
private int _fd;
private fb_fix_screeninfo _fixedInfo;
private fb_var_screeninfo _varInfo;
private IntPtr _mappedLength;
private IntPtr _mappedAddress;
public LinuxFramebuffer(string fileName = null, Vector? dpi = null)
{
_dpi = dpi ?? new Vector(96, 96);
fileName = fileName ?? Environment.GetEnvironmentVariable("FRAMEBUFFER") ?? "/dev/fb0";
_fd = NativeUnsafeMethods.open(fileName, 2, 0);
if (_fd <= 0)
throw new Exception("Error: " + Marshal.GetLastWin32Error());
try
{
Init();
}
catch
{
Dispose();
throw;
}
}
void Init()
{
fixed (void* pnfo = &_varInfo)
{
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo))
throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error());
SetBpp();
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo))
_varInfo.transp = new fb_bitfield();
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 (_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))
throw new Exception("FBIOGET_FSCREENINFO error: " + Marshal.GetLastWin32Error());
_mappedLength = new IntPtr(_fixedInfo.line_length * _varInfo.yres);
_mappedAddress = NativeUnsafeMethods.mmap(IntPtr.Zero, _mappedLength, 3, 1, _fd, IntPtr.Zero);
if (_mappedAddress == new IntPtr(-1))
throw new Exception($"Unable to mmap {_mappedLength} bytes, error {Marshal.GetLastWin32Error()}");
fixed (fb_fix_screeninfo* pnfo = &_fixedInfo)
{
int idlen;
for (idlen = 0; idlen < 16 && pnfo->id[idlen] != 0; idlen++) ;
Id = Encoding.ASCII.GetString(pnfo->id, idlen);
}
}
void SetBpp()
{
_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;
}
public string Id { get; private set; }
public Size PixelSize
{
get
{
fb_var_screeninfo nfo;
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, &nfo))
throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error());
return new Size(nfo.xres, nfo.yres);
}
}
public ILockedFramebuffer Lock()
{
if (_fd <= 0)
throw new ObjectDisposedException("LinuxFramebuffer");
return new LockedFramebuffer(_fd, _fixedInfo, _varInfo, _mappedAddress, _dpi);
}
private void ReleaseUnmanagedResources()
{
if (_mappedAddress != IntPtr.Zero)
{
NativeUnsafeMethods.munmap(_mappedAddress, _mappedLength);
_mappedAddress = IntPtr.Zero;
}
if(_fd == 0)
return;
NativeUnsafeMethods.close(_fd);
_fd = 0;
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~LinuxFramebuffer()
{
ReleaseUnmanagedResources();
}
}
}