Skip to content

Instantly share code, notes, and snippets.

@TinkerWorX
Created November 10, 2019 03:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TinkerWorX/0162901e8d9b04c93b7f603737adcec2 to your computer and use it in GitHub Desktop.
Save TinkerWorX/0162901e8d9b04c93b7f603737adcec2 to your computer and use it in GitHub Desktop.
using System;
using System.Runtime.InteropServices;
namespace Ultralight.Net.Interop
{
[StructLayout(LayoutKind.Sequential)]
public struct Platform
{
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate IntPtr GetterDelegate(IntPtr that);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void SetterDelegate(IntPtr that, IntPtr value);
public struct VirtualTable
{
public IntPtr dtor;
public IntPtr comparer;
public IntPtr config;
public IntPtr set_gpu_driver;
public IntPtr gpu_driver;
public IntPtr set_font_loader;
public IntPtr font_loader;
public IntPtr set_file_system;
public IntPtr file_system;
}
private readonly unsafe VirtualTable* methods;
public IntPtr config()
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<GetterDelegate>(this.methods->config);
return function(new IntPtr(pThis));
}
}
}
public void set_gpu_driver(IntPtr value)
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<SetterDelegate>(this.methods->set_gpu_driver);
function(new IntPtr(pThis), value);
}
}
}
public IntPtr gpu_driver()
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<GetterDelegate>(this.methods->gpu_driver);
return function(new IntPtr(pThis));
}
}
}
public IntPtr font_loader()
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<GetterDelegate>(this.methods->font_loader);
return function(new IntPtr(pThis));
}
}
}
public void set_font_loader(IntPtr value)
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<SetterDelegate>(this.methods->set_font_loader);
function(new IntPtr(pThis), value);
}
}
}
public IntPtr file_system()
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<GetterDelegate>(this.methods->file_system);
return function(new IntPtr(pThis));
}
}
}
public void set_file_system(IntPtr value)
{
unsafe
{
fixed (Platform* pThis = &this)
{
var function = Marshal.GetDelegateForFunctionPointer<SetterDelegate>(this.methods->set_file_system);
function(new IntPtr(pThis), value);
}
}
}
}
}
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Ultralight.Net.ConsoleDemo
{
public class FileSystem
{
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr EmptyMethodDelegate(IntPtr pThis);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool FileExistsDelegate(IntPtr pThis, IntPtr pPath);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool GetFileSizeADelegate(IntPtr pThis, IntPtr pFile, out long length);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool GetFileSizeBDelegate(IntPtr pThis, IntPtr pPath, out long length);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool GetFileMimeTypeDelegate(IntPtr pThis, IntPtr pPath, IntPtr pResult);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr OpenFileDelegate(IntPtr pThis, IntPtr pPath, [MarshalAs(UnmanagedType.I1)] bool openForWriting);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate void CloseFileDelegate(IntPtr pThis, IntPtr pFile);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate long ReadFromFileDelegate(IntPtr pThis, IntPtr pFile, IntPtr pBuffer, long length);
public struct VirtualTable
{
public IntPtr dtor;
public IntPtr FileExists; // must implement
public IntPtr DeleteFile_;
public IntPtr DeleteEmptyDirectory;
public IntPtr MoveFile_;
public IntPtr GetFileSizeA; // must implement
public IntPtr GetFileSizeB; // must implement
public IntPtr GetFileMimeType; // must implement
public IntPtr GetFileModificationTime;
public IntPtr GetFileCreationTime;
public IntPtr GetMetadataType;
public IntPtr GetPathByAppendingComponent;
public IntPtr CreateDirectory_;
public IntPtr GetHomeDirectory;
public IntPtr GetFilenameFromPath;
public IntPtr GetDirectoryNameFromPath;
public IntPtr GetVolumeFreeSpace;
public IntPtr GetVolumeId;
public IntPtr ListDirectory;
public IntPtr OpenTemporaryFile;
public IntPtr OpenFile; // must implement
public IntPtr CloseFile; // must implement
public IntPtr SeekFile;
public IntPtr TruncateFile;
public IntPtr WriteToFile;
public IntPtr ReadFromFile; // must implement
public IntPtr CopyFile_;
}
public readonly unsafe VirtualTable* Methods;
public FileSystem()
{
unsafe
{
this.Methods = (VirtualTable*)Marshal.AllocHGlobal(sizeof(VirtualTable));
this.Methods->dtor = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->FileExists = Marshal.GetFunctionPointerForDelegate<FileExistsDelegate>(this.FileExists);
this.Methods->DeleteFile_ = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->DeleteEmptyDirectory = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->MoveFile_ = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetFileSizeA = Marshal.GetFunctionPointerForDelegate<GetFileSizeADelegate>(this.GetFileSizeA);
this.Methods->GetFileSizeB = Marshal.GetFunctionPointerForDelegate<GetFileSizeBDelegate>(this.GetFileSizeB);
this.Methods->GetFileMimeType = Marshal.GetFunctionPointerForDelegate<GetFileMimeTypeDelegate>(this.GetFileMimeType);
this.Methods->GetFileModificationTime = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetFileCreationTime = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetMetadataType = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetPathByAppendingComponent = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->CreateDirectory_ = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetHomeDirectory = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetFilenameFromPath = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetDirectoryNameFromPath = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetVolumeFreeSpace = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->GetVolumeId = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->ListDirectory = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->OpenTemporaryFile = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->OpenFile = Marshal.GetFunctionPointerForDelegate<OpenFileDelegate>(this.OpenFile);
this.Methods->CloseFile = Marshal.GetFunctionPointerForDelegate<CloseFileDelegate>(this.CloseFile);
this.Methods->SeekFile = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->TruncateFile = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->WriteToFile = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
this.Methods->ReadFromFile = Marshal.GetFunctionPointerForDelegate<ReadFromFileDelegate>(this.ReadFromFile);
this.Methods->CopyFile_ = Marshal.GetFunctionPointerForDelegate<EmptyMethodDelegate>(this.EmptyMethod);
}
}
private IntPtr EmptyMethod(IntPtr pThis)
{
Console.WriteLine($"EmptyMethod({pThis.ToString("X16")})");
return IntPtr.Zero;
}
private bool FileExists(IntPtr pThis, IntPtr pPath)
{
Console.WriteLine($"FileExists({pThis.ToString("X16")}, ...)");
return false;
}
private bool GetFileSizeA(IntPtr pThis, IntPtr pFile, out long length)
{
Console.WriteLine($"GetFileSize({pThis.ToString("X16")}, ...)");
length = file.Length;
return true;
}
private bool GetFileSizeB(IntPtr pThis, IntPtr pPath, out long length)
{
var path = Marshal.PtrToStringUni(Marshal.ReadIntPtr(pPath));
Console.WriteLine($"GetFileSize({pThis.ToString("X16")}, {path}, ...)");
length = 0;
return false;
}
private bool GetFileMimeType(IntPtr pThis, IntPtr pPath, IntPtr pResult)
{
var path = Marshal.PtrToStringUni(Marshal.ReadIntPtr(pPath));
Console.WriteLine($"GetFileMimeType({pThis.ToString("X16")}, {path}, ...)");
Interop.UltralightApi.String16__Create(pResult, "text/html");
return true;
}
private System.IO.FileStream file;
private IntPtr OpenFile(IntPtr pThis, IntPtr pPath, bool openForWriting)
{
var path = Marshal.PtrToStringUni(Marshal.ReadIntPtr(pPath));
Console.WriteLine($"OpenFile({pThis.ToString("X16")}, {path}, {openForWriting})");
this.file = System.IO.File.OpenRead(path);
return IntPtr.Zero;
}
private void CloseFile(IntPtr pThis, IntPtr pFile)
{
Console.WriteLine($"CloseFile({pThis.ToString("X16")}, ...)");
this.file?.Close();
this.file?.Dispose();
this.file = null;
}
private long ReadFromFile(IntPtr pThis, IntPtr pFile, IntPtr pBuffer, long length)
{
Console.WriteLine($"ReadFromFile({pThis.ToString("X16")}, ..., {length})");
unsafe
{
return file.Read(new Span<byte>((void*)pBuffer, (int)length));
}
}
}
public class Program
{
public unsafe static void Main(string[] args)
{
ULRenderer.Initialize(new ULConfig());
var instance = Interop.UltralightApi.Platform__Instance();
var fileSystem = new FileSystem();
var pFileSystem = fileSystem.Methods;
instance->set_file_system(new IntPtr(&pFileSystem));
var view = ULRenderer.Instance.CreateView(512, 512, true);
view.ConsoleMessage += (sender, e) => { Console.WriteLine($"ConsoleMessage({e.Message})"); };
view.CursorChanged += (sender, e) => { Console.WriteLine($"CursorChanged({e.Cursor})"); };
view.DocumentObjectModelReady += (sender, e) => { Console.WriteLine("DocumentObjectModelReady()"); };
view.HistoryUpdated += (sender, e) => { Console.WriteLine("HistoryUpdated()"); };
view.LoadingBegun += (sender, e) => { Console.WriteLine("LoadingBegun()"); };
view.LoadingFinished += (sender, e) => { Console.WriteLine("LoadingFinished()"); };
view.TitleChanged += (sender, e) => { Console.WriteLine($"TitleChanged(\"{e.Title}\")"); };
view.TooltipChanged += (sender, e) => { Console.WriteLine($"TooltipChanged(\"{e.Tooltip}\")"); };
view.UrlChanged += (sender, e) => { Console.WriteLine($"UrlChanged(\"{e.Url}\")"); };
view.LoadUrl("file:///D:/Development/Desolation/Prototyping/UltralightMvvm/UltralightMvvm/Views/Main.html");
while (true)
{
ULRenderer.Instance.Update();
ULRenderer.Instance.Render();
if (view.IsBitmapDirty)
{
Console.WriteLine("Bitmap is dirty");
unsafe
{
using var path = new ULString(System.IO.Path.Combine(Environment.CurrentDirectory, "output.png"));
Console.WriteLine("Saving to " + path);
Interop.UltralightApi.ulBitmapWritePNG(view.GetBitmap().DangerousGetHandle() /* clears the dirty flag */, path.DangerousGetHandle());
}
}
Thread.Sleep(100);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment