1 /// License: MIT
2 module ultralight;
3 
4 import std.conv: to;
5 import std.string: fromStringz, toStringz;
6 
7 import ultralight.bindings;
8 public import ultralight.enums;
9 
10 /// Global configuration singleton, manages user-defined configuration.
11 static Config config;
12 
13 /// Create the `Renderer` singleton.
14 static this() {
15   config = Config(ulCreateConfig());
16 }
17 
18 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___string_8h.html
19 class String {
20   ///
21   ULString ptr;
22 
23   package this(ULString str) {
24     assert(str);
25     ptr = str;
26   }
27   /// Create string from a D string.
28   this(string source) {
29     ptr = ulCreateString(source.toStringz);
30   }
31   /// Destroy string (you should destroy any strings you explicitly Create).
32   ~this() {
33     assert(ptr);
34     ulDestroyString(ptr);
35     ptr = null;
36   }
37 
38   /// Create string from a UTF-8 buffer.
39   String utf8(string str) {
40     return new String(ulCreateStringUTF8(str.ptr, str.length));
41   }
42 
43   /// Create string from a UTF-16 buffer.
44   String utf16(wstring str) {
45     return new String(ulCreateStringUTF16(cast(ushort*) str.ptr, str.length));
46   }
47 
48   /// Create string from copy of this string.
49   String idup() {
50     return new String(ulCreateStringFromCopy(ptr));
51   }
52 
53   /// Whether this string is empty or not.
54   bool empty() const {
55     return ulStringIsEmpty(cast(C_String*) ptr);
56   }
57 
58   /// Replaces the contents of this string with the contents of a string.
59   auto opAssign(T)(string str) {
60     ulStringAssignCString(ptr, str.toStringz);
61     return this;
62   }
63 
64   /// Replaces the contents of this string with the contents of `str`.
65   auto opAssign(T)(String str) {
66     ulStringAssignString(ptr, str.ptr);
67     return this;
68   }
69 
70   /// Replaces the contents of this string with the contents of `str`.
71   auto opAssign(T)(ULString str) {
72     ulStringAssignString(ptr, str);
73     return this;
74   }
75 
76   ///
77   override string toString() const @trusted nothrow {
78     import std.exception : assumeWontThrow;
79 
80     return assumeWontThrow(
81       ulStringGetData(cast(C_String*) ptr)[0 .. ulStringGetLength(cast(C_String*) ptr)].to!string
82     );
83   }
84 }
85 
86 /// Convert a `string` to an Ultralight `String`.
87 String toUlString(string str) {
88   return new String(str);
89 }
90 
91 /// Convert an unmanaged Ultralight string directly to a managed `string`.
92 /// Remarks: This makes a copy of the unmanaged string.
93 string toString(ULString str) {
94   return ulStringGetData(str)[0 .. ulStringGetLength(str)].to!string.idup;
95 }
96 
97 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___config_8h.html
98 struct Config {
99   ///
100   ULConfig ptr;
101 
102   ~this() {
103     assert(ptr);
104     ulDestroyConfig(ptr);
105     ptr = null;
106   }
107 
108   /// A writable OS file path to store persistent Session data in.
109   static void setCachePath(string cachePath) {
110     ulConfigSetCachePath(config.ptr, cachePath.toUlString.ptr);
111   }
112 
113   /// The relative path to the resources folder (loaded via the FileSystem API).
114   static void setResourcePathPrefix(string resourcePathPrefix) {
115     ulConfigSetResourcePathPrefix(config.ptr, resourcePathPrefix.toUlString.ptr);
116   }
117 
118   /// The winding order for front-facing triangles.
119   static void setFaceWinding(FaceWinding winding) {
120     ulConfigSetFaceWinding(config.ptr, winding);
121   }
122 
123   /// The hinting algorithm to use when rendering fonts.
124   static void setFontHinting(FontHinting fontHinting) {
125     ulConfigSetFontHinting(config.ptr, fontHinting);
126   }
127 
128   /// The gamma to use when compositing font glyphs, change this value to adjust contrast (Adobe and Apple prefer 1.8, others may prefer 2.2).
129   static void setFontGamma(double fontGamma) {
130     ulConfigSetFontGamma(config.ptr, fontGamma);
131   }
132 
133   /// Global user-defined CSS string (included before any CSS on the page).
134   static void setUserStylesheet(string cssString) {
135     ulConfigSetUserStylesheet(config.ptr, cssString.toUlString.ptr);
136   }
137 
138   /// Whether or not to continuously repaint any Views, regardless if they are dirty.
139   static void setForceRepaint(bool enabled) {
140     ulConfigSetForceRepaint(config.ptr, enabled);
141   }
142 
143   /// The delay (in seconds) between every tick of a CSS animation.
144   static void setAnimationTimerDelay(double delay) {
145     ulConfigSetAnimationTimerDelay(config.ptr, delay);
146   }
147 
148   /// The delay (in seconds) between every tick of a smooth scroll animation.
149   static void setScrollTimerDelay(double delay) {
150     ulConfigSetScrollTimerDelay(config.ptr, delay);
151   }
152 
153   /// The delay (in seconds) between every call to the recycler.
154   static void setRecycleDelay(double delay) {
155     ulConfigSetRecycleDelay(config.ptr, delay);
156   }
157 
158   /// The size of WebCore's memory cache in bytes.
159   static void setMemoryCacheSize(uint size) {
160     ulConfigSetMemoryCacheSize(config.ptr, size);
161   }
162 
163   /// The number of pages to keep in the cache.
164   static void setPageCacheSize(uint size) {
165     ulConfigSetPageCacheSize(config.ptr, size);
166   }
167 
168   /// The system's physical RAM size in bytes.
169   static void setOverrideRAMSize(uint size) {
170     ulConfigSetOverrideRAMSize(config.ptr, size);
171   }
172 
173   /// The minimum size of large VM heaps in JavaScriptCore.
174   static void setMinLargeHeapSize(uint size) {
175     ulConfigSetMinLargeHeapSize(config.ptr, size);
176   }
177 
178   /// The minimum size of small VM heaps in JavaScriptCore.
179   static void setMinSmallHeapSize(uint size) {
180     ulConfigSetMinSmallHeapSize(config.ptr, size);
181   }
182 
183   /// The number of threads to use in the Renderer (for parallel painting on the CPU, etc.).
184   static void setNumRendererThreads(uint numRendererThreads) {
185     ulConfigSetNumRendererThreads(config.ptr, numRendererThreads);
186   }
187 
188   /// The max amount of time (in seconds) to allow repeating timers to run during each call to `Renderer.update`.
189   static void setMaxUpdateTime(double maxUpdateTime) {
190     ulConfigSetMaxUpdateTime(config.ptr, maxUpdateTime);
191   }
192 
193   /// The alignment (in bytes) of the BitmapSurface when using the CPU renderer.
194   static void setBitmapAlignment(uint bitmapAlignment) {
195     ulConfigSetBitmapAlignment(config.ptr, bitmapAlignment);
196   }
197 }
198 
199 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___renderer_8h.html
200 class Renderer {
201   ///
202   ULRenderer ptr;
203 
204   /// Create a new renderer given Ultralight's default configuration.
205   this() {
206     this(config);
207   }
208   /// Create a new renderer.
209   this(Config config) {
210     assert(config.ptr);
211     ptr = ulCreateRenderer(config.ptr);
212   }
213   ~this() {
214     ulDestroyRenderer(ptr);
215   }
216 
217   /// Create a Session to store local data in (such as cookies, local storage, application cache, indexed db, etc).
218   Session createSession(bool isPersistent, string name) {
219     return this.createSession(isPersistent, name.toUlString);
220   }
221   /// ditto
222   Session createSession(bool isPersistent, String name) {
223     return new Session(ulCreateSession(ptr, isPersistent, name.ptr));
224   }
225 
226   /// Get the default session (persistent session named "default").
227   Session defaultSession() {
228     return new Session(ulDefaultSession(ptr));
229   }
230 
231   /// Update timers and dispatch internal callbacks (JavaScript and network).
232   void update() {
233     ulUpdate(ptr);
234   }
235 
236   /// Render all active Views.
237   void render() {
238     ulRender(ptr);
239   }
240 
241   /// Attempt to release as much memory as possible.
242   void purgeMemory() {
243     ulPurgeMemory(ptr);
244   }
245 
246   /// Print detailed memory usage statistics to the log.
247   void logMemoryUsage() {
248     ulLogMemoryUsage(ptr);
249   }
250 
251   /// Start the remote inspector server.
252   bool startRemoteInspectorServer(string address, ushort port) {
253     return ulStartRemoteInspectorServer(ptr, address.toStringz, port);
254   }
255 
256   /// Describe the details of a gamepad, to be used with `ulFireGamepadEvent` and related events below.
257   void setGamepadDetails(uint index, string id, uint axis_count, uint button_count) {
258     ulSetGamepadDetails(ptr, index, cast(C_String*) id.toStringz, axis_count, button_count);
259   }
260 
261   /// Fire a gamepad event (connection / disconnection).
262   void fireGamepadEvent(ULGamepadEvent evt) {
263     ulFireGamepadEvent(ptr, evt);
264   }
265 
266   /// Fire a gamepad axis event (to be called when an axis value is changed).
267   void fireGamepadAxisEvent(ULGamepadAxisEvent evt) {
268     ulFireGamepadAxisEvent(ptr, evt);
269   }
270 
271   /// Fire a gamepad button event (to be called when a button value is changed).
272   void fireGamepadButtonEvent(ULGamepadButtonEvent evt) {
273     ulFireGamepadButtonEvent(ptr, evt);
274   }
275 }
276 
277 /// Global platform singleton, manages user-defined platform handlers.
278 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___platform_8h.html
279 class Platform {
280   ///
281   static void enablePlatformFontLoader() {
282     ulEnablePlatformFontLoader();
283   }
284 
285   ///
286   static void enablePlatformFileSystem(string baseDir) {
287     ulEnablePlatformFileSystem(baseDir.toUlString.ptr);
288   }
289 
290   /// Set a custom Logger implementation.
291   static void setLogger(ULLogger logger) {
292     ulPlatformSetLogger(logger);
293   }
294 
295   /// Set a custom FileSystem implementation.
296   static void setFileSystem(ULFileSystem file_system) {
297     ulPlatformSetFileSystem(file_system);
298   }
299 
300   /// Set a custom Surface implementation.
301   static void setSurfaceDefinition(ULSurfaceDefinition surface_definition) {
302     ulPlatformSetSurfaceDefinition(surface_definition);
303   }
304 
305   /// Set a custom GPUDriver implementation.
306   static void setGPUDriver(ULGPUDriver gpu_driver) {
307     ulPlatformSetGPUDriver(gpu_driver);
308   }
309 
310   /// Set a custom Clipboard implementation.
311   static void setClipboard(ULClipboard clipboard) {
312     ulPlatformSetClipboard(clipboard);
313   }
314 }
315 
316 /// See_Also: `Renderer.createSession`
317 /// See_Also: `Renderer.defaultSession`
318 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___session_8h.html
319 class Session {
320   ///
321   ULSession ptr;
322 
323   package this(ULSession ptr) {
324     assert(ptr);
325     this.ptr = ptr;
326   }
327   /// Destroy a Session.
328   ~this() {
329     ulDestroySession(ptr);
330   }
331 
332   /// Whether or not is persistent (backed to disk).
333   bool isPersistent() {
334     return ulSessionIsPersistent(ptr);
335   }
336 
337   /// Unique name identifying the session (used for unique disk path).
338   string name() {
339     return ulSessionGetName(ptr).toString;
340   }
341 
342   /// Unique numeric ID for the session.
343   ulong getId() {
344     return ulSessionGetId(ptr);
345   }
346 
347   /// The disk path to write to (used by persistent sessions only).
348   string getDiskPath() {
349     return ulSessionGetDiskPath(ptr).toString;
350   }
351 }
352 
353 /// View is a web-page container rendered to an offscreen surface that you display yourself.
354 ///
355 /// The View object is responsible for loading and rendering web-pages to an offscreen surface.
356 /// It is completely isolated from the OS windowing system, you must forward all input events to it from your application.
357 ///
358 /// Remarks: The API is not thread-safe, all calls must be made on the same thread that the Renderer/App was created on.
359 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___view_8h.html
360 class View {
361 }
362 
363 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___surface_8h.html
364 class Surface {
365 }
366 
367 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___bitmap_8h.html
368 class Bitmap {
369   ///
370   ULBitmap ptr;
371   private bool owned = true;
372 
373   /// Create empty bitmap.
374   this() {
375     this(ulCreateEmptyBitmap());
376   }
377   /// Create bitmap with certain dimensions and pixel format.
378   this(uint width, uint height, BitmapFormat format) {
379     this(ulCreateBitmap(width, height, format));
380   }
381   ///
382   package this(ULBitmap ptr) {
383     assert(ptr);
384     this.ptr = ptr;
385     this.owned = this.ownsPixels;
386   }
387   /// Destroy a bitmap.
388   /// Remarks: You should only destroy Bitmaps you have explicitly created via one of the creation functions above.
389   ~this() {
390     if (owned)
391       ulDestroyBitmap(ptr);
392   }
393 
394   /// Create bitmap from existing pixel buffer.
395   static Bitmap fromPixels(
396     uint width, uint height, BitmapFormat format, uint row_bytes, ubyte[] pixels, bool should_copy = true
397   ) {
398     return new Bitmap(
399       ulCreateBitmapFromPixels(width, height, format, row_bytes, pixels.ptr, pixels.length, should_copy)
400     );
401   }
402 
403   /// Create bitmap from copy.
404   static Bitmap fromCopy(Bitmap existingBitmap) {
405     return new Bitmap(ulCreateBitmapFromCopy(existingBitmap.ptr));
406   }
407 
408   /// Get the width in pixels.
409   uint width() const {
410     return ulBitmapGetWidth(cast(C_Bitmap*) ptr);
411   }
412 
413   /// Get the height in pixels.
414   uint height() const {
415     return ulBitmapGetHeight(cast(C_Bitmap*) ptr);
416   }
417 
418   /// Get the pixel format.
419   BitmapFormat format() const {
420     return ulBitmapGetFormat(cast(C_Bitmap*) ptr).to!uint.to!BitmapFormat;
421   }
422 
423   /// Get the bytes per pixel.
424   uint bpp() const {
425     return ulBitmapGetBpp(cast(C_Bitmap*) ptr);
426   }
427 
428   /// Get the number of bytes per row.
429   uint rowBytes() const {
430     return ulBitmapGetRowBytes(cast(C_Bitmap*) ptr);
431   }
432 
433   /// Get the size in bytes of the underlying pixel buffer.
434   size_t size() const {
435     return ulBitmapGetSize(cast(C_Bitmap*) ptr);
436   }
437 
438   /// Whether or not this bitmap owns its own pixel buffer.
439   bool ownsPixels() const {
440     return ulBitmapOwnsPixels(cast(C_Bitmap*) ptr);
441   }
442 
443   /// Lock pixels for reading/writing.
444   /// Returns: Slice of pixel buffer.
445   ubyte[] lockPixels() {
446     return cast(ubyte[]) ulBitmapLockPixels(ptr)[0 .. this.size];
447   }
448 
449   /// Unlock pixels after locking.
450   void unlockPixels() {
451     ulBitmapUnlockPixels(ptr);
452   }
453 
454   /// Get raw pixel buffer.
455   ///
456   /// You should only call this if Bitmap is already locked.
457   ubyte[] rawPixels() {
458     return cast(ubyte[]) ulBitmapRawPixels(ptr)[0 .. this.size];
459   }
460 
461   /// Whether or not this bitmap is empty.
462   bool isEmpty() {
463     return ulBitmapIsEmpty(ptr);
464   }
465 
466   /// Reset bitmap pixels to 0.
467   void erase() {
468     ulBitmapErase(ptr);
469   }
470 
471   /// Write bitmap to a PNG on disk.
472   bool writePng(string path) {
473     return ulBitmapWritePNG(ptr, path.toStringz);
474   }
475 
476   /// Converts a BGRA bitmap to RGBA bitmap and vice-versa by swapping the red and blue channels.
477   void swapRedBlueChannels() {
478     ulBitmapSwapRedBlueChannels(ptr);
479   }
480 }
481 
482 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___mouse_event_8h.html
483 class MouseEvent {
484   ///
485   ULMouseEvent ptr;
486 
487   /// Create a mouse event.
488   this() {
489   }
490 
491   ~this() {
492     assert(ptr);
493     ulDestroyMouseEvent(ptr);
494     ptr = null;
495   }
496 }
497 
498 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___key_event_8h.html
499 class KeyEvent {
500   ///
501   ULKeyEvent ptr;
502 
503   /// Create a key event.
504   this() {
505   }
506 
507   ~this() {
508     assert(ptr);
509     ulDestroyKeyEvent(ptr);
510     ptr = null;
511   }
512 }
513 
514 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___scroll_event_8h.html
515 class ScrollEvent {
516   ///
517   ULScrollEvent ptr;
518 
519   /// Create a scroll event.
520   this() {
521   }
522 
523   ~this() {
524     assert(ptr);
525     ulDestroyScrollEvent(ptr);
526     ptr = null;
527   }
528 }
529 
530 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___gamepad_event_8h.html
531 class GamepadEvent {
532   ///
533   ULGamepadEvent ptr;
534 
535   /// Create a gamepad event.
536   this() {
537   }
538 
539   ~this() {
540     assert(ptr);
541     ulDestroyGamepadEvent(ptr);
542     ptr = null;
543   }
544 }
545 
546 /// Get the version string of the library in MAJOR.MINOR.PATCH format.
547 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___defines_8h.html
548 string version_() {
549   return ulVersionString().fromStringz.to!string;
550 }
551 
552 /// Get the numeric major version of the library.
553 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___defines_8h.html
554 uint versionMajor() {
555   return ulVersionMajor();
556 }
557 
558 /// Get the numeric minor version of the library.
559 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___defines_8h.html
560 uint versionMinor() {
561   return ulVersionMinor();
562 }
563 
564 /// Get the numeric patch version of the library.
565 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___defines_8h.html
566 uint versionPatch() {
567   return ulVersionPatch();
568 }
569 
570 /// Get the full WebKit version string.
571 /// See_Also: https://ultralig.ht/api/c/1_3_0/_c_a_p_i___defines_8h.html
572 string webKitVersion() {
573   return ulWebKitVersionString().fromStringz.to!string;
574 }