NAV Navbar
c++
  • Welcome
  • API Headers
  • Platform Code
  • The Update Loop
  • Views
  • Calling JavaScript
  • Welcome

    Welcome to the Ultralight API!

    This documentation will give you a brief overview of everything you need to know to set up, configure, and integrate the library into your application.

    API Headers

    #include <Ultralight/Ultralight.h>
    

    Simply include Ultralight/Ultralight.h in your code to import the API.

    Platform Code

    auto& platform = Platform::instance();
    
    platform.set_config(config_);
    platform.set_gpu_driver(gpu_driver_);
    platform.set_file_system(file_system_);
    platform.set_font_loader(font_loader_);
    

    Using the Platform singleton, embedders must implement Platform-specific handlers for common platform tasks such as file access, font loading, GPU rendering, and more.

    Set the Config

    Config config_;
    config.face_winding = kFaceWinding_Clockwise;
    config.device_scale_hint = 1.0;
    config.font_family_standard = "Segoe UI";
    
    Platform::instance().set_config(config_);
    

    The Config class allows you to configure renderer behavior and runtime WebKit options.

    The most common things to customize are face_winding for front-facing triangles (usually Clockwise in Direct3D and Counter-Clockwise in OpenGL) and device_scale_hint for application DPI scaling (used for oversampling raster output, default is 1.0).

    You can also set the default font (instead of default Times New Roman).

    Set the GPUDriver

    #include "common/d3d/GPUDriverD3D.h"
    #include "common/d3d/D3DRenderer.h"
    
    D3DContext* context = new D3DRenderer(hWnd, fullscreen, sRGB, samples);
    GPUDriver* driver = new GPUDriverD3D(context);
    
    Platform::instance().set_gpu_driver(driver);
    

    All rendering is dispatched via the GPUDriver class, you must provide an implementation to draw WebViews. We provide stock implementations for Direct3D11 but you can also write your own by sub-classing the GPUDriver class.

    See the SDK samples for GPUDriverD3D.

    Set the FontLoader

    #include "common/win/FontLoaderWin.h"
    
    FontLoader* font_loader = new FontLoaderWin();
    Platform::instance().set_font_loader(font_loader);
    

    To render text you'll need to provide a FontLoader. We provide stock implementations for Windows but you can provide your own by sub-classing FontLoader.

    Set the FileSystem

    #include "common/win/FileSystemWin.h"
    
    const wchar_t* base_dir = L"C:\\my\\application\\assets\\";
    FileSystem* file_system = new FileSystemWin(base_dir);
    
    Platform::instance().set_file_system(file_system);
    

    To load File URLs and support JavaScript file system access you must provide a FileSystem. We provide stock implementations for Windows but you can provide your own by sub-classing FileSystem.

    The Update Loop

    Create the Renderer

    Renderer* renderer_ = Renderer::Create();
    

    To create Views and render them, you'll need to create a Renderer. This object should only be created once in the lifetime of your application.

    The Update Function

    void MyApplication::Update() {
      renderer_->Update();
      renderer_->Render();
    }
    

    In your Update Loop you should call Renderer::Update() to allow the library to do work (dispatching timers, process resource loading callbacks, etc.) and Renderer::Render() to draw any Views to command lists.

    The Draw Function

    void MyApplication::Draw(D3DContext* context) {
      Platform& platform = Platform::instance();
      GPUDriver* driver = platform.gpu_driver();
      if (driver->HasCommandsPending()) {
        driver->DrawCommandList();
        DrawOverlays();
        context->swap_chain()->Present(0, 0);
      }
    }
    

    Once any Views have been drawn to command lists, you can translate those commands to native draw calls via GPUDriver::DrawCommandList().

    Drawing Overlays

    All Views are rendered to offscreen textures by default so to actually display them on screen you'll need to draw the texture via an overlay.

    Check out common/d3d/Overlay.h and common/d3d/Overlay.cc in the SDK for a complete example of how to implement this.

    Views

    Ref<View> view = renderer_->CreateView(width, height, is_transparent);
    

    Views are certain fixed-size containers for rendering web-content. You can create a new View via Renderer::CreateView().

    Loading Content

    view->LoadHTML("<h1>Hello World</h1>"); // HTML string
    
    view->LoadURL("http://www.google.com"); // Remote URL
    
    view->LoadURL("file:///asset.html");    // Local URL
    

    The easiest way to load HTML into a View is to pass a string of HTML directly to View::LoadHTML().

    To load URLs asynchronously, simply call View::LoadURL().

    Input Events

    void MyApplication::MouseMove(int x, int y) {
      MouseEvent evt;
      evt.type = MouseEvent::kType_MouseMoved;
      evt.button = MouseEvent::kButton_None;
      evt.x = x;
      evt.y = y;
    
      view_->FireMouseEvent(evt);
    }
    
    void MyApplication::KeyDown(WPARAM wparam, LPARAM lparam, bool is_system_key) {
      KeyEvent evt(KeyEvent::kType_RawKeyDown, wparam, lparam, is_system_key);
      view_->FireKeyEvent(evt);
    }
    

    You can pass input events to a View by calling View::FireKeyEvent(), View::FireMouseEvent(), and View::FireScrollEvent() respectively.

    You can pass native Windows key event params directly to KeyEvent to conveniently synthesize keyboard events.

    Handling Events

    struct MyListener : public ViewListener,
                        public LoadListener {
      // View Listener
      virtual void OnChangeTitle(View* caller, const String& title) {}
      virtual void OnChangeURL(View* caller, const String& url) {}
      virtual void OnChangeTooltip(View* caller, const String& tooltip) {}
      virtual void OnChangeCursor(View* caller, Cursor cursor) {}
      virtual void OnAddConsoleMessage(View* caller, MessageSource source,
        MessageLevel level, const String& message, uint32_t line_number,
        uint32_t column_number, const String& source_id) {}
    
      // Load Listener
      virtual void OnBeginLoading(View* caller) {}
      virtual void OnFinishLoading(View* caller) {}
      virtual void OnUpdateHistory(View* caller) {}
      virtual void OnDOMReady(View* caller) {}
    };
    
    //...
    
    MyListener* listener = new MyListener();
    view->set_view_listener(listener);
    view->set_load_listener(listener);
    

    You can register listeners for various events via View::set_view_listener() and View::set_load_listener().

    See Listener.h for more details about ViewListener and LoadListener.

    Calling JavaScript

    JavaScriptCore API

    #include <JavaScriptCore/Javascript.h>
    

    We've exposed the entire JavaScriptCore API to users, simply include JavaScriptCore/Javascript.h in your headers to get started.

    Obtaining a JSContext

    JSContextRef context = view->js_context();
    

    You can get a JSContextRef from a View by calling View::js_context(). This object is necessary for many JavaScriptCore API calls.

    DOMReady Event

    struct MyListener : public LoadListener {
      virtual void OnDOMReady(View* caller) override {
        JSContextRef ctx = view->js_context();
    
        // Call any initial JS code here.
    
      }
    };
    

    Before calling JavaScript functions and scripts on a page, you'll first need to know if the page is ready (all scripts parsed and DOM loaded).

    The easiest way to do this is by handling the LoadListener::OnDOMReady() event in your code.

    Evaluating Scripts

    JSValueRef result = view->EvaluateScript("1 + 1");
    
    // Or, through JavaScriptCore...
    
    String script = "1 + 1";
    JSContextRef ctx = js_context();
    JSStringRef str = JSStringCreateWithCharacters(string.utf16().data(), string.utf16().length());
    JSValueRef exception;
    JSValueRef result = JSEvaluateScript(ctx, str, 0, 0, 0, &exception);
    

    The easiest way to run a string of JavaScript is to use View::EvaluateScript().

    Alternatively, you can use JavaScriptCore's JSEvaluateScript() yourself which offers more control (exception handling, defining the this object, etc.).

    Calling JS Functions from C++

    /**
     * To call the following JavaScript function from C++...
     *
     *  function myFunc(a, b) { return a + b; }
     *
     */
    
    // Define our Helper Function...
    
    JSObjectRef JSGetFunction(View* view, const String& func_name) {
      JSValueRef result = view->EvaluateScript(func_name);
      JSContextRef ctx = view->js_context();
      if (JSValueIsObject(ctx, result)) {
        JSObjectRef obj = JSValueToObject(ctx, result, 0);
        if (JSObjectIsFunction(ctx, obj))
          return obj;
      }
    
      return nullptr;
    }
    
    // Then, to cache the JavaScript function in an object...
    
    JSObjectRef myFunc = JSGetFunction(view, "myFunc");
    
    // Later, when you want to call the function...
    
    JSContextRef ctx = view->js_context();
    JSValueRef args[] = { JSValueMakeNumber(ctx, 2.0), JSValueMakeNumber(ctx, 7.0) };
    JSValueRef result = JSObjectCallAsFunction(ctx, myFunc, 0, 2, args, 0); 
    

    For simple cases, you can use View::EvaluateScript() to call JS functions as well.

    For more complex cases where performance is a concern, you're going to want to get the actual function object from JavaScriptCore and call it programatically. You can then call this function object using JSObjectCallAsFunction().

    Calling C++ Functions from JS

    // Define our static C++ callback, it must have the following signature...
    
    JSValueRef MyNativeFunc(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
      size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) {
    
      // Handle arguments here...
    
      return JSValueMakeNull(ctx);
    }
    
    // Define helper function for binding C++ callbacks to JS
    
    void BindJSCallback(JSContextRef ctx, const String& name, JSObjectCallAsFunctionCallback cb) {
      JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
      JSStringRef funcName = JSStringCreateWithCharacters(name.utf16().data(), name.utf16().length());
      JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, funcName, cb);
      JSObjectSetProperty(ctx, globalObject, funcName, func, 0, 0);
    }
    
    // Then, within your OnDOMReady callback, you can bind the callback
    
    void MyApp::OnDOMReady(View* caller) {
      JSContextRef ctx = caller->js_context();
      BindJSCallback(ctx, "MyNativeFunc", &MyNativeFunc);
    }
    
    // Later, you can call the C++ function "MyNativeFunc" directly from JavaScript
    
    /**
     * JavaScript Code:
     *
     *   document.getElementById('button').onclick = MyNativeFunc();
     *
     */
    

    JavaScriptCore allows you to bind JavaScript function objects to static C callbacks via JSObjectMakeFunctionWithCallback(). You can then expose this function to your page by setting it as a property of the global object.

    See the code example for more details.

    c++