Multi-platform .NET bindings to the Ultralight project.
1using System;
2using System.IO;
3using System.Runtime.CompilerServices;
4using System.Runtime.InteropServices;
5using UnityEngine;
6using ImpromptuNinjas.UltralightSharp;
7using UltralightSharp.Enums;
8using Unity.Collections.LowLevel.Unsafe;
9using UnityEngine.Experimental.Rendering;
10using Renderer = ImpromptuNinjas.UltralightSharp.Renderer;
11using String = ImpromptuNinjas.UltralightSharp.String;
12
13[RequireComponent(typeof(UnityEngine.Renderer))]
14public class UltralightBrowserDemo : MonoBehaviour {
15
16 [NonSerialized]
17 private unsafe Renderer* _ulRenderer;
18
19 [NonSerialized]
20 private unsafe View* _ulView;
21
22 [NonSerialized]
23 private Texture2D _texture;
24
25 [SerializeField]
26 private string url;
27
28 [NonSerialized]
29 private bool _isDomReady;
30
31 [NonSerialized]
32 private bool _isWindowReady;
33
34 [NonSerialized]
35 private bool _failed;
36
37 [NonSerialized]
38 private bool _isLoaded;
39
40 private bool _willRender;
41
42 public unsafe string Title => _ulView != null ? _ulView->GetTitle()->Read() : null;
43
44 public unsafe bool IsLoading => _ulView != null && _ulView->IsLoading();
45
46 public bool IsDomReady => _isDomReady;
47
48 public bool IsWindowReady => _isWindowReady;
49
50 public bool Failed => _failed;
51
52 public bool IsLoaded => _isLoaded;
53
54 public bool WillRender => _willRender;
55
56 public unsafe string Url {
57 get => url;
58 set {
59 if (_ulView == null) return;
60
61 url = value;
62 var s = String.Create(value);
63 _ulView->LoadUrl(s);
64 s->Destroy();
65 }
66 }
67
68 // Start is called before the first frame update
69 unsafe void OnEnable() {
70 var texSize = new Vector2(640, 480);
71 _texture = new Texture2D((int) texSize.x, (int) texSize.y,
72 GraphicsFormat.B8G8R8A8_UNorm, TextureCreationFlags.None) {
73 hideFlags = HideFlags.HideAndDontSave
74 };
75 var cfg = Config.Create();
76
77 {
78 var cachePath = String.Create(Path.Combine(Application.streamingAssetsPath, "ultralight", "cache"));
79 cfg->SetCachePath(cachePath);
80 cachePath->Destroy();
81 }
82
83 {
84 // ReSharper disable once AssignNullToNotNullAttribute
85 var resourcePath = String.Create(Path.Combine(Application.streamingAssetsPath, "ultralight", "resources"));
86 cfg->SetResourcePath(resourcePath);
87 resourcePath->Destroy();
88 }
89
90 cfg->SetUseGpuRenderer(false);
91 cfg->SetEnableImages(true);
92 cfg->SetEnableJavaScript(false);
93
94 AppCore.EnablePlatformFontLoader();
95
96 {
97 var assetsPath = String.Create(Path.Combine(Application.streamingAssetsPath, "ultralight", "assets"));
98 AppCore.EnablePlatformFileSystem(assetsPath);
99 assetsPath->Destroy();
100 }
101
102 _ulRenderer = Renderer.Create(cfg);
103 var sessionName = String.Create("Demo");
104 var session = Session.Create(_ulRenderer, false, sessionName);
105
106 _ulView = View.Create(_ulRenderer, (uint) texSize.x, (uint) texSize.y, false, session);
107
108 GetComponent<UnityEngine.Renderer>().material.mainTexture = _texture;
109
110 var gch = GCHandle.Alloc(this);
111 var gchPtr = (void*) GCHandle.ToIntPtr(gch);
112
113 _ulView->SetAddConsoleMessageCallback((userData, caller, source, level, msg, lineNumber, columnNumber, sourceId) => {
114 switch (level) {
115 default:
116 Debug.Log($"[Ultralight Console] [{level}] {sourceId->Read()}:{lineNumber}:{columnNumber} {msg->Read()}");
117 break;
118 case MessageLevel.Error:
119 Debug.LogError($"[Ultralight Console] {sourceId->Read()}:{lineNumber}:{columnNumber} {msg->Read()}");
120 break;
121 case MessageLevel.Warning:
122 Debug.LogError($"[Ultralight Console] {sourceId->Read()}:{lineNumber}:{columnNumber} {msg->Read()}");
123 break;
124 }
125 }, gchPtr);
126
127 _ulView->SetDomReadyCallback((userData, caller, id, isMainFrame, readyUrl) => {
128 if (!isMainFrame)
129 return;
130
131 var r = (UltralightBrowserDemo) GCHandle.FromIntPtr((IntPtr) userData).Target;
132
133 r._isDomReady = true;
134 }, gchPtr);
135
136 _ulView->SetFailLoadingCallback((userData, caller, id, isMainFrame, failedUrl, failDesc, errorDomain, errorCode) => {
137 if (!isMainFrame)
138 return;
139
140 var r = (UltralightBrowserDemo) GCHandle.FromIntPtr((IntPtr) userData).Target;
141
142 r._failed = true;
143 r._isLoaded = true;
144 }, gchPtr);
145
146 _ulView->SetFinishLoadingCallback((userData, caller, id, isMainFrame, finishedUrl) => {
147 if (!isMainFrame)
148 return;
149
150 var r = (UltralightBrowserDemo) GCHandle.FromIntPtr((IntPtr) userData).Target;
151
152 r._failed = false;
153 r._isLoaded = true;
154 }, gchPtr);
155
156 _ulView->SetWindowObjectReadyCallback((userData, caller, id, isMainFrame, readyUrl) => {
157 if (!isMainFrame)
158 return;
159
160 var r = (UltralightBrowserDemo) GCHandle.FromIntPtr((IntPtr) userData).Target;
161
162 r._isWindowReady = true;
163 }, gchPtr);
164
165 _ulView->SetChangeUrlCallback((userData, caller, newUrl) => {
166 if (!caller->IsLoading())
167 return;
168
169 var r = (UltralightBrowserDemo) GCHandle.FromIntPtr((IntPtr) userData).Target;
170
171 r._isDomReady = false;
172 r._failed = false;
173 r._isLoaded = false;
174 r._isWindowReady = false;
175 }, gchPtr);
176
177 Url = url;
178 }
179
180 public unsafe void LoadHtml(string htmlString) {
181 if (_ulView == null)
182 return;
183
184 var s = String.Create(htmlString);
185 _ulView->LoadHtml(s);
186 s->Destroy();
187 }
188
189 private unsafe void OnDisable() {
190 _ulView->Destroy();
191 _ulView = null;
192 _ulRenderer->Destroy();
193 _ulRenderer = null;
194 Destroy(_texture);
195 _texture = null;
196
197 _isDomReady = false;
198 _failed = false;
199 _isLoaded = false;
200 _isWindowReady = false;
201 url = null;
202 }
203
204 public unsafe void OnWillRenderObject() {
205 _willRender = true;
206 if (_ulRenderer == null || _ulView == null || _texture == null)
207 return;
208
209 Ultralight.Update(_ulRenderer);
210 Ultralight.Render(_ulRenderer);
211
212 var surface = _ulView->GetSurface();
213 var bitmap = surface->GetBitmap();
214 var pixels = bitmap->LockPixels();
215 var rawData = _texture.GetRawTextureData<byte>();
216 var size = Math.Min((uint) rawData.Length, bitmap->GetSize().ToUInt32());
217 Unsafe.CopyBlockUnaligned(rawData.GetUnsafePtr(), pixels, size);
218 _texture.Apply();
219 bitmap->UnlockPixels();
220 }
221
222 private void OnPostRender()
223 => _willRender = false;
224
225 static unsafe UltralightBrowserDemo() {
226 // setup logging
227 LoggerLogMessageCallback cb = LoggerCallback;
228 Ultralight.PlatformSetLogger(new ImpromptuNinjas.UltralightSharp.Logger {LogMessage = cb});
229 }
230
231 private static unsafe void LoggerCallback(LogLevel logLevel, String* msg) {
232 switch (logLevel) {
233 default:
234 Debug.Log($"[Ultralight] [{logLevel}] {msg->Read()}");
235 break;
236 case LogLevel.Error:
237 Debug.LogError($"[Ultralight] {msg->Read()}");
238 break;
239 case LogLevel.Warning:
240 Debug.LogError($"[Ultralight] {msg->Read()}");
241 break;
242 }
243 }
244
245}