Files
Andrew Zambazos c0395a49bd Added SDK
2026-06-11 14:01:22 +12:00

260 lines
7.3 KiB
C

#include <AppCore/CAPI.h>
#include <JavaScriptCore/JavaScript.h>
///
/// Welcome to Sample 6!
///
/// In this sample we'll demonstrate how to set up a simple JavaScript app using only the C API.
///
/// We will bind a JavaScript function to a C callback and use it to display a welcome message
/// in our HTML view.
///
/// __About the C API__
///
/// The C API is useful for porting the library to other languages or using it in scenarios that
/// are unsuitable for C++.
///
/// Most of the C++ API functionality is currently available via the CAPI headers with the
/// exception of some of the Platform API.
///
/// JavaScriptCore's native API is already in C so we can use it directly.
///
/// Both Ultralight and JavaScriptCore follow the same paradigm when it comes to
/// ownership/destruction: You should explicitly Destroy/Release anything you Create.
///
/// Various globals
ULApp app = 0;
ULWindow window = 0;
ULOverlay overlay = 0;
ULView view = 0;
/// Forward declaration of our OnUpdate callback.
void OnUpdate(void* user_data);
/// Forward declaration of our OnClose callback.
void OnClose(void* user_data, ULWindow window);
/// Forward declaration of our OnResize callback.
void OnResize(void* user_data, ULWindow window, unsigned int width, unsigned int height);
/// Forward declaration of our OnDOMReady callback.
void OnDOMReady(void* user_data, ULView caller, unsigned long long frame_id,
bool is_main_frame, ULString url);
///
/// We set up our application here.
///
void Init() {
///
/// Create default settings/config
///
ULSettings settings = ulCreateSettings();
ULConfig config = ulCreateConfig();
///
/// Create our App
///
app = ulCreateApp(settings, config);
///
/// Register a callback to handle app update logic.
///
ulAppSetUpdateCallback(app, OnUpdate, 0);
///
/// Done using settings/config, make sure to destroy anything we create
///
ulDestroySettings(settings);
ulDestroyConfig(config);
///
/// Create our window, make it 500x500 with a titlebar and resize handles.
///
window = ulCreateWindow(ulAppGetMainMonitor(app), 500, 500, false,
kWindowFlags_Titled | kWindowFlags_Resizable);
///
/// Set our window title.
///
ulWindowSetTitle(window, "Ultralight Sample 6 - Intro to C API");
///
/// Register a callback to handle window close.
///
ulWindowSetCloseCallback(window, OnClose, 0);
///
/// Register a callback to handle window resize.
///
ulWindowSetResizeCallback(window, OnResize, 0);
///
/// Create an overlay same size as our window at 0,0 (top-left) origin. Overlays also create an
/// HTML view for us to display content in.
///
/// **Note**:
/// Ownership of the view remains with the overlay since we don't explicitly create it.
///
overlay = ulCreateOverlay(window, ulWindowGetWidth(window), ulWindowGetHeight(window), 0, 0);
///
/// Get the overlay's view.
///
view = ulOverlayGetView(overlay);
///
/// Register a callback to handle our view's DOMReady event. We will use this event to setup any
/// JavaScript <-> C bindings and initialize our page.
///
ulViewSetDOMReadyCallback(view, OnDOMReady, 0);
///
/// Load a file from the FileSystem.
///
/// **IMPORTANT**: Make sure `file:///` has three (3) forward slashes.
///
/// **Note**: You can configure the base path for the FileSystem in the Settings we passed to
/// ulCreateApp earlier.
///
ULString url = ulCreateString("file:///app.html");
ulViewLoadURL(view, url);
ulDestroyString(url);
}
///
/// This is called continuously from the app's main run loop. You should update any app logic
/// inside this callback.
///
void OnUpdate(void* user_data) {
/// We don't use this in this tutorial, just here for example.
}
///
/// This is called when the window is closed.
///
void OnClose(void* user_data, ULWindow window) {
ulAppQuit(app);
}
///
/// This is called whenever the window resizes. Width and height are in DPI-independent logical
/// coordinates (not pixels).
///
void OnResize(void* user_data, ULWindow window, unsigned int width, unsigned int height) {
ulOverlayResize(overlay, width, height);
}
///
/// This callback is bound to a JavaScript function on the page.
///
JSValueRef GetMessage(JSContextRef ctx, JSObjectRef function,
JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[],
JSValueRef* exception) {
///
/// Create a JavaScript String from a C-string, initialize it with our
/// welcome message.
///
JSStringRef str = JSStringCreateWithUTF8CString("Hello from C!");
///
/// Create a garbage-collected JSValue using the String we just created.
///
/// **Note**:
/// Both JSValueRef and JSObjectRef types are garbage-collected types. (And actually,
/// JSObjectRef is just a typedef of JSValueRef, they share definitions).
///
/// The garbage collector in JavaScriptCore periodically scans the entire stack to check if
/// there are any active JSValueRefs, and marks those with no references for destruction.
///
/// If you happen to store a JSValueRef/JSObjectRef in heap memory or in memory unreachable
/// by the stack-based garbage-collector, you should explicitly call JSValueProtect() and
/// JSValueUnprotect() on the reference to ensure it is kept alive.
///
JSValueRef value = JSValueMakeString(ctx, str);
///
/// Release the string we created earlier (we only Release what we Create).
///
JSStringRelease(str);
return value;
}
///
/// This is called when the page has finished parsing the document and is ready to execute scripts.
///
/// We will use this event to set up our JavaScript <-> C callback.
///
void OnDOMReady(void* user_data, ULView caller, unsigned long long frame_id,
bool is_main_frame, ULString url) {
///
/// Acquire the page's JavaScript execution context.
///
/// This locks the JavaScript context so we can modify it safely on this thread, we need to
/// unlock it when we're done via ulViewUnlockJSContext().
///
JSContextRef ctx = ulViewLockJSContext(view);
///
/// Create a JavaScript String containing the name of our callback.
///
JSStringRef name = JSStringCreateWithUTF8CString("GetMessage");
///
/// Create a garbage-collected JavaScript function that is bound to our native C callback
/// 'GetMessage()'.
///
JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, name, GetMessage);
///
/// Store our function in the page's global JavaScript object so that it is accessible from the
/// page as 'GetMessage()'.
///
/// The global JavaScript object is also known as 'window' in JS.
///
JSObjectSetProperty(ctx, JSContextGetGlobalObject(ctx), name, func, 0, 0);
///
/// Release the JavaScript String we created earlier.
///
JSStringRelease(name);
///
/// Unlock the JS context so other threads can modify JavaScript state.
///
ulViewUnlockJSContext(view);
}
///
/// We tear down our application here.
///
void Shutdown() {
///
/// Explicitly destroy everything we created in Init().
///
ulDestroyOverlay(overlay);
ulDestroyWindow(window);
ulDestroyApp(app);
}
int main() {
///
/// Initialize the app.
///
Init();
///
/// Run the app until the window is closed. (This is a modal operation)
///
ulAppRun(app);
///
/// Shutdown the app.
///
Shutdown();
return 0;
}