### Build Ultralight Samples with CMake Source: https://docs.ultralig.ht/docs/trying-the-samples Use this command to configure, build, and install the Ultralight samples. Ensure you have CMake and a compatible C++ compiler installed. ```shell cmake -B build && cmake --build build --config Release && cmake --install build --config Release ``` -------------------------------- ### Clone the Ultralight Quick Start Repo Source: https://docs.ultralig.ht/docs/writing-your-first-app Use this command to clone the starter project for your Ultralight application. ```shell git clone git@github.com:ultralight-ux/ultralight-quick-start.git ``` -------------------------------- ### HTML and JavaScript Setup Source: https://docs.ultralig.ht/docs/calling-a-js-function-from-c This is the HTML and JavaScript code that sets up the page and defines the ShowMessage function. ```html
``` -------------------------------- ### Handle Console Messages with ViewListener Source: https://docs.ultralig.ht/docs/logging-console-messages Implement this method to receive and process console messages from a View. Ensure your `MyApp` class is bound to the `View` using `View::set_view_listener()` to receive these events. This example logs detailed information about each console message, including source, type, level, message content, line/column numbers, source ID, and arguments. ```c++ #include #include using namespace ultralight; // // Inherited from ViewListener::OnAddConsoleMessage // // Make sure that you bind 'MyApp' to 'View::set_view_listener' // to receive this event. // void MyApp::OnAddConsoleMessage(View* caller, const ConsoleMessage& msg) { std::cout << "[OnAddConsoleMessage]\n\t" << "\n\tsource:\t" << (uint32_t)msg.source() << "\n\ttype:\t" << (uint32_t)msg.type() << "\n\tlevel:\t" << (uint32_t)msg.level() << "\n\tmessage:\t" << msg.message().utf8().data() << "\n\tline_number:\t" << msg.line_number() << "\n\tcolumn_number:\t" << msg.column_number() << "\n\tsource_id:\t" << msg.source_id().utf8().data() << "\n\tnum_arguments:\t" << msg.num_arguments() << std::endl; // console.log() in JavaScript can be passed multiple arguments-- you can // get the raw JavaScript values passed to the function via the following: uint32_t num_args = msg.num_arguments(); if (num_args > 0) { for (uint32_t i = 0; i < num_args; i++) { JSValueRef arg = msg.argument_at(i); // Optionally print the argument here, see the JavaScriptCore API // for converting these values to a string. } } } ``` -------------------------------- ### Create and Resize Inspector View Source: https://docs.ultralig.ht/docs/using-the-inspector-view Get the Inspector View associated with a parent View and resize it. You are responsible for displaying this Inspector View within your application, treating it like any other View. ```cplusplus RefPtr inspector_view = view->inspector(); inspector_view->Resize(500, 500); // Display inspector_view in your application here, treat it like a newly-created View // If you are using AppCore, you would use Overlay::Create(window, inspector_view, x, y) // If you are using the Renderer directly, you would get render_target() or surface() // from inspector_view and display that just like a regular View. // The Inspector View is owned by the parent View. ``` -------------------------------- ### Getting View Render Target Information Source: https://docs.ultralig.ht/docs/using-a-custom-gpudriver Retrieve the render target information for a View to access its texture ID and UV coordinates. Adjust UV coordinates to account for potential texture padding. ```cpp RenderTarget rtt_info = view->render_target(); // Get the Ultralight texture ID, use this with GPUDriver::BindTexture() uint32_t tex_id = rtt_info.texture_id; // Textures may have extra padding-- to compensate for this you'll need // to adjust your UV coordinates when mapping onto geometry. Rect uv_coords = rtt_info.uv_coords; ``` -------------------------------- ### Get View Surface Source: https://docs.ultralig.ht/docs/integrating-with-games Retrieve the underlying pixel buffer Surface for a View. This is necessary for accessing bitmap data when using the CPU renderer. ```c++ /// /// Get the pixel-buffer Surface for a View. /// Surface* surface = view->surface(); ``` -------------------------------- ### Set Up Include Directories for C++ API Source: https://docs.ultralig.ht/docs/using-the-cpp-api Add this path to your project's include directories to use Ultralight in your C++ code. Replace `` with the actual path to your Ultralight SDK. ```bash /include/ ``` -------------------------------- ### Initialize Platform with Custom GPUDriver Source: https://docs.ultralig.ht/docs/using-a-custom-gpudriver Set up the Platform singleton with your custom GPUDriver, FileSystem, and FontLoader implementations. This is required before creating the Renderer. ```cplusplus #include using namespace ultralight; void InitRenderer() { // Pseudo-code to create our custom Platform implementations MyGPUDriver* gpu_driver = new MyGPUDriver(); MyFileSystem* file_system = new MyFileSystem(); MyFontLoader* font_loader = new MyFontLoader(); // Notify the Platform singleton of our implementations Platform::instance().set_gpu_driver(gpu_driver); Platform::instance().set_file_system(file_system); Platform::instance().set_font_loader(font_loader); // We can now create the Renderer singleton Ref renderer = Renderer::Create(); } ``` -------------------------------- ### Initialize Default Platform Handlers Source: https://docs.ultralig.ht/docs/integrating-with-games Set up default platform handlers for font loading, file system access (with a specified base directory), and logging. Ensure AppCore/Platform.h is included. ```cplusplus #include #include using namespace ultralight; void InitPlatform() { /// /// Use the OS's native font loader /// Platform::instance().set_font_loader(GetPlatformFontLoader()); /// /// Use the OS's native file loader, with a base directory of "." /// All file:/// URLs will load relative to this base directory. /// Platform::instance().set_file_system(GetPlatformFileSystem(".")); /// /// Use the default logger (writes to a log file) /// Platform::instance().set_logger(GetDefaultLogger("ultralight.log")); } ``` -------------------------------- ### Windows: Additional Dependencies Source: https://docs.ultralig.ht/docs/linking-to-the-library Specify the Ultralight library files to link against in Visual Studio. Link `AppCore.lib` only if you are using the AppCore API. ```text Ultralight.lib UltralightCore.lib WebCore.lib AppCore.lib ``` -------------------------------- ### Configure Ultralight Settings Source: https://docs.ultralig.ht/docs/integrating-with-games Customize global CSS, layout, performance, and styling by setting a Config object. Pass the configuration to the Platform singleton before initializing the Renderer. ```cplusplus #include using namespace ultralight; void Init() { Config config; /// /// Let's set some custom global CSS to make our background /// purple by default. /// config.user_stylesheet = "body { background: purple; }"; /// /// Pass our configuration to the Platform singleton so that /// the library can use it. /// Platform::instance().set_config(config); } ``` -------------------------------- ### Windows: Additional Library Directories Source: https://docs.ultralig.ht/docs/linking-to-the-library Configure Visual Studio to find the Ultralight library files. Ensure the path points to the correct 64-bit version. ```text $(ULTRALIGHT_SDK_ROOT)/lib/win/x64/ ``` -------------------------------- ### Load URL from Assets Folder in C++ Source: https://docs.ultralig.ht/docs/writing-your-first-app Demonstrates how to load a URL from the application's assets directory using the Ultralight SDK in C++. ```cplusplus void MyApp::InitializeView() { // Load "app.html" from the "assets" directory myView->LoadURL("file:///app.html"); } ``` -------------------------------- ### Create the Ultralight Renderer Source: https://docs.ultralig.ht/docs/integrating-with-games Initialize the Renderer singleton, which manages the library's lifetime and painting process. This should be called only once and after platform handlers are set. ```cplusplus RefPtr renderer; void CreateRenderer() { /// /// Create our Renderer (call this only once per application). /// /// The Renderer singleton maintains the lifetime of the library /// and is required before creating any Views. /// /// You should set up the Platform handlers before this. /// renderer = Renderer::Create(); } ``` -------------------------------- ### macOS: Linked Frameworks and Libraries Source: https://docs.ultralig.ht/docs/linking-to-the-library Add these dynamic libraries to your target in Xcode. Include `libAppCore.dylib` if you are using the AppCore API. ```text libUltralightCore.dylib libUltralight.dylib libWebCore.dylib libAppCore.dylib ``` -------------------------------- ### Create an Offscreen View Source: https://docs.ultralig.ht/docs/integrating-with-games Configure and create an offscreen web view. Ensure acceleration is disabled if using the CPU renderer. The view is initialized with specified dimensions and an optional configuration. ```c++ RefPtr view; void CreateView() { /// /// Configure our View, make sure it uses the CPU renderer by /// disabling acceleration. /// ViewConfig view_config; view_config.is_accelerated = false; /// /// Create an offscreen web view, 500 by 500 pixels large. /// view = renderer->CreateView(500, 500, view_config, nullptr); /// /// Load raw HTML asynchronously into the view. /// view->LoadHTML("

Hello World!

"); } ``` -------------------------------- ### Set Initial Device Scale for a View Source: https://docs.ultralig.ht/docs/porting-from-12-to-13 Configure the initial device scale (DPI scale) for a View using the `initial_device_scale` option within `ViewConfig`. This value can be modified at runtime using `View::set_device_scale()`. ```text double initial_device_scale = 1.0; ``` -------------------------------- ### Register Custom SurfaceFactory Source: https://docs.ultralig.ht/docs/using-a-custom-surface Register your custom SurfaceFactory with Ultralight's Platform instance. This must be done before creating the Renderer or any Views. Keep the factory instance alive for the program's duration. ```cplusplus /// /// You should keep this instance alive for the duration of your program. /// std::unique_ptr factory(new GLTextureSurfaceFactory()); Platform::instance().set_surface_factory(factory.get()); ``` -------------------------------- ### Include Ultralight Header Source: https://docs.ultralig.ht/docs/using-the-cpp-api Include this header at the top of your C++ source files to import the core Ultralight API. ```cpp #include ``` -------------------------------- ### Configure View Rendering (CPU/GPU) Source: https://docs.ultralig.ht/docs/porting-from-12-to-13 Use the `is_accelerated` option in `ViewConfig` to specify whether a View should use the GPU renderer (accelerated) or the CPU renderer (unaccelerated). This is only applicable when managing the `Renderer` manually. ```cpp /// /// Whether to render using the GPU renderer (accelerated) or the CPU renderer (unaccelerated). /// /// This option is only valid if you're managing the Renderer yourself (eg, you've previously /// called Renderer::Create() instead of App::Create()). /// /// When true, the View will be rendered to an offscreen GPU texture using the GPU driver set in /// Platform::set_gpu_driver. You can fetch details for the texture via View::render_target. /// /// When false (the default), the View will be rendered to an offscreen pixel buffer using the /// multithreaded CPU renderer. This pixel buffer can optionally be provided by the user-- /// for more info see Platform::set_surface_factory and View::surface. /// bool is_accelerated = false; ``` -------------------------------- ### Include AppCore Header Source: https://docs.ultralig.ht/docs/using-the-cpp-api Include this header if you plan to use the optional AppCore API for cross-platform windowing and drawing. ```cpp #include ``` -------------------------------- ### Implement LoadListener::OnDOMReady Source: https://docs.ultralig.ht/docs/using-the-ondomready-event Inherit from LoadListener and override OnDOMReady to perform DOM manipulations or JavaScript initializations once the main frame's DOM is ready. Ignore events from child frames. ```c++ #include using namespace ultralight; class MyListener : public LoadListener { public: MyListener(View* view) { view->set_load_listener(this); } virtual ~MyListener() {} /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// virtual void OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) override { /// /// Ignore DOMReady events from child frames. /// if (!is_main_frame) return; /// /// The main document has loaded, we can initialize any DOM elements and / /// or set up our JavaScript bindings here. /// } }; ``` -------------------------------- ### HTML Structure for Button Click Source: https://docs.ultralig.ht/docs/calling-a-c-function-from-js This HTML sets up a button that triggers a JavaScript function 'OnButtonClick()' when clicked and a div to display results. ```html
``` -------------------------------- ### Custom GLPBOTextureSurface Implementation Source: https://docs.ultralig.ht/docs/using-a-custom-surface Inherit from Surface to create a custom implementation that paints directly into an OpenGL PBO. This allows for lower-latency uploads to a texture by mapping PBOs to system memory for drawing. ```cpp /// /// Custom Surface implementation that allows Ultralight to paint directly /// into an OpenGL PBO (pixel buffer object). /// /// PBOs in OpenGL allow us to get a pointer to a block of GPU-controlled /// memory for lower-latency uploads to a texture. /// /// For more info: /// class GLPBOTextureSurface : public Surface { public: GLPBOTextureSurface(uint32_t width, uint32_t height) { Resize(width, height); } virtual ~GLPBOTextureSurface() { /// /// Destroy our PBO and texture. /// if (pbo_id_) { glDeleteBuffers(1, &pbo_id_); pbo_id_ = 0; glDeleteTextures(1, &texture_id_); texture_id_ = 0; } } virtual uint32_t width() const override { return width_; } virtual uint32_t height() const override { return height_; } virtual uint32_t row_bytes() const override { return row_bytes_; } virtual size_t size() const override { return size_; } virtual void* LockPixels() override { /// /// Map our PBO to system memory so Ultralight can draw to it. /// glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_id_); void* result = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return result; } virtual void UnlockPixels() override { /// /// Unmap our PBO. /// glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_id_); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } virtual void Resize(uint32_t width, uint32_t height) override { if (pbo_id_ && width_ == width && height_ == height) return; /// /// Destroy any existing PBO and texture. /// if (pbo_id_) { glDeleteBuffers(1, &pbo_id_); pbo_id_ = 0; glDeleteTextures(1, &texture_id_); texture_id_ = 0; } width_ = width; height_ = height; row_bytes_ = width_ * 4; size_ = row_bytes_ * height_; /// /// Create our PBO (pixel buffer object), with a size of 'size_' /// glGenBuffers(1, &pbo_id_); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_id_); glBufferData(GL_PIXEL_UNPACK_BUFFER, size_, 0, GL_DYNAMIC_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); /// /// Create our Texture object. /// glGenTextures(1, &texture_id_); glBindTexture(GL_TEXTURE_2D, texture_id_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBindTexture(GL_TEXTURE_2D, 0); } virtual GLuint GetTextureAndSyncIfNeeded() { /// /// This is NOT called by Ultralight. /// /// This helper function is called when our application wants to draw this /// Surface to an OpenGL quad. (We return an OpenGL texture handle) /// /// We take this opportunity to upload the PBO to the texture if the /// pixels have changed since the last call (indicated by dirty_bounds() /// being non-empty) /// if (!dirty_bounds().IsEmpty()) { /// /// Update our Texture from our PBO (pixel buffer object) /// glBindTexture(GL_TEXTURE_2D, texture_id_); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_id_); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); /// /// Clear our Surface's dirty bounds to indicate we've handled any /// pending modifications to our pixels. /// ClearDirtyBounds(); } return texture_id_; } protected: GLuint texture_id_; GLuint pbo_id_ = 0; uint32_t width_; uint32_t height_; uint32_t row_bytes_; uint32_t size_; }; ``` -------------------------------- ### Linux: Linker Flags Source: https://docs.ultralig.ht/docs/linking-to-the-library Add these flags to your Makefile's `LDFLAGS` to link the Ultralight shared libraries. Include `-lAppCore` if using the AppCore API. ```makefile -lUltralight -lUltralightCore -lWebCore -lAppCore ``` -------------------------------- ### Edit HTML Content Source: https://docs.ultralig.ht/docs/writing-your-first-app Update the main heading in the application's HTML file to display a custom welcome message. ```html

Hello World!

``` ```html

Welcome to My App!

``` -------------------------------- ### Synthesize Key Up Event Source: https://docs.ultralig.ht/docs/integrating-with-games Create a KeyEvent with type KeyEvent::kType_KeyUp when a key is released. KeyCodes and GetKeyIdentifierFromVirtualKeyCode are required. ```cpp KeyEvent evt; evt.type = KeyEvent::kType_KeyUp; evt.virtual_key_code = KeyCodes::GK_RIGHT; evt.native_key_code = 0; evt.modifiers = 0; // You'll need to generate a key identifier from the virtual key code // when synthesizing events. This function is provided in KeyEvent.h GetKeyIdentifierFromVirtualKeyCode(evt.virtual_key_code, evt.key_identifier); view->FireKeyEvent(evt); ``` -------------------------------- ### Include Ultralight C API Header Source: https://docs.ultralig.ht/docs/using-the-c-api Include this header to use the core Ultralight C API in your C code. ```c #include ``` -------------------------------- ### Synthesize Raw Key Down Event Source: https://docs.ultralig.ht/docs/integrating-with-games Use KeyEvent::kType_RawKeyDown for physical key presses. Ensure KeyCodes and GetKeyIdentifierFromVirtualKeyCode are available. ```cpp KeyEvent evt; evt.type = KeyEvent::kType_RawKeyDown; evt.virtual_key_code = KeyCodes::GK_RIGHT; evt.native_key_code = 0; evt.modifiers = 0; // You'll need to generate a key identifier from the virtual key code // when synthesizing events. This function is provided in KeyEvent.h GetKeyIdentifierFromVirtualKeyCode(evt.virtual_key_code, evt.key_identifier); view->FireKeyEvent(evt); ``` -------------------------------- ### Pass Mouse Input to View Source: https://docs.ultralig.ht/docs/integrating-with-games Create a MouseEvent with appropriate type, coordinates, and button state, then pass it to View::FireMouseEvent(). Ensure coordinates are localized to the View and in logical units. ```c++ MouseEvent evt; evt.type = MouseEvent::kType_MouseMoved; evt.x = 100; evt.y = 100; evt.button = MouseEvent::kButton_None; view->FireMouseEvent(evt); ``` -------------------------------- ### Render One Frame Source: https://docs.ultralig.ht/docs/integrating-with-games Update the display by calling RefreshDisplay, then render all active Views to update their Surfaces. Check Surface::dirty_bounds() to determine if a View needs re-rendering and upload changed bitmaps to the GPU. ```c++ void RenderOneFrame() { /// /// Notify the renderer that the physical display has refreshed and /// any active animations should be updated. /// renderer->RefreshDisplay(0); /// /// Render all active Views (this updates the Surface for each View). /// renderer->Render(); /// /// Psuedo-code to loop through all active Views. /// for (auto view : view_list) { /// /// Get the Surface as a BitmapSurface (the default implementation). /// BitmapSurface* surface = (BitmapSurface*)(view->surface()); /// /// Check if our Surface is dirty (pixels have changed). /// if (!surface->dirty_bounds().IsEmpty()) { /// /// Psuedo-code to upload Surface's bitmap to GPU texture. /// CopyBitmapToTexture(surface->bitmap()); /// /// Clear the dirty bounds. /// surface->ClearDirtyBounds(); } } } ``` -------------------------------- ### Enable GPU-Acceleration on a View Source: https://docs.ultralig.ht/docs/using-a-custom-gpudriver Configure a View to use the GPU renderer by setting `ViewConfig::is_accelerated` to true. This causes the view to paint to a RenderTarget instead of a Surface. ```cplusplus RefPtr view; void CreateView() { /// /// Create our ViewConfig with GPU acceleration enabled. /// ViewConfig view_config; view_config.is_accelerated = true; /// /// Create an HTML view, 500 by 500 pixels large. /// view = renderer->CreateView(500, 500, view_config, nullptr); /// /// Load a raw string of HTML asynchronously into the View. /// view->LoadHTML("

Hello World!

"); } ``` -------------------------------- ### Synthesize Text Input Event Source: https://docs.ultralig.ht/docs/integrating-with-games Pass the actual text generated by a key press using KeyEvent::kType_Char. Set both evt.text and evt.unmodified_text. ```cpp KeyEvent evt; evt.type = KeyEvent::kType_Char; evt.text = "a"; evt.unmodified_text = "a"; // If not available, set to same as evt.text view->FireKeyEvent(evt); ``` -------------------------------- ### Ultralight Render Loop Integration Source: https://docs.ultralig.ht/docs/using-a-custom-gpudriver Integrate these calls into your application's main run loop to ensure proper rendering and updates. Renderer::Update() should be called as often as possible, while RefreshDisplay() and Render() should be called once per frame. ```cpp void UpdateLogic() { // Calling Update() allows the library to service resource callbacks, // JavaScript events, and other timers. renderer->Update(); } void RenderFrame() { renderer->RefreshDisplay(0); renderer->Render(); // Pseudo-code, consume any pending command-lists in your GPUDriver here. gpu_driver->DrawCommandListIfNeeded(); // Pseudo-code, draw any View textures to on-screen quads here. DrawViewQuadsToScreen(); } ``` -------------------------------- ### Implement Custom SurfaceFactory Source: https://docs.ultralig.ht/docs/using-a-custom-surface Define a SurfaceFactory for your custom Surface class. This factory is responsible for creating and destroying instances of your custom Surface. Ensure it's implemented before creating the Renderer or Views. ```cplusplus class GLTextureSurfaceFactory : public ultralight::SurfaceFactory { public: GLTextureSurfaceFactory() {} virtual ~GLTextureSurfaceFactory() {} virtual ultralight::Surface* CreateSurface(uint32_t width, uint32_t height) override { /// /// Called by Ultralight when it wants to create a Surface. /// return new GLPBOTextureSurface(width, height); } virtual void DestroySurface(ultralight::Surface* surface) override { // /// Called by Ultralight when it wants to destroy a Surface. /// delete static_cast(surface); } }; ``` -------------------------------- ### Include JavaScriptCore C API Header Source: https://docs.ultralig.ht/docs/using-the-c-api Include this header to access the full JavaScriptCore API for making native calls to and from the JavaScript VM. ```c #include ``` -------------------------------- ### Include AppCore C API Header Source: https://docs.ultralig.ht/docs/using-the-c-api Include this header to use the optional cross-platform windowing and drawing layer provided by AppCore. ```c #include ``` -------------------------------- ### Include JavaScriptCore Header Source: https://docs.ultralig.ht/docs/about-javascript-interop Include the necessary header file to use the JavaScriptCore API in your C++ code. ```c #include ``` -------------------------------- ### Execute JavaScript String with View::EvaluateScript() Source: https://docs.ultralig.ht/docs/evaluating-a-string-of-js Use View::EvaluateScript() to execute a string of JavaScript. This is the simplest method for running JS code directly. ```c++ /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { /// /// Execute a string of JavaScript /// view->EvaluateScript("document.body.innerHTML = 'Ultralight rocks!';"); } ``` -------------------------------- ### Update ViewListener::OnAddConsoleMessage() Signature (C++) Source: https://docs.ultralig.ht/docs/porting-from-13-to-14 Implement the ViewListener::OnAddConsoleMessage() method using the new signature that accepts a const ultralight::ConsoleMessage& parameter instead of individual message components. This change standardizes console message handling. ```cplusplus virtual void OnAddConsoleMessage(ultralight::View* caller, const ultralight::ConsoleMessage& message) { } ``` -------------------------------- ### Handle Results from View::EvaluateScript() Source: https://docs.ultralig.ht/docs/evaluating-a-string-of-js View::EvaluateScript() can return the result of a script execution as a String. Ensure the script produces a return value if you expect one. ```c++ /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { /// /// Evaluate a string of JavaScript, store result in 'result' /// String result = view->EvaluateScript("1 + 1"); /// /// result should now contain "2" /// } ``` -------------------------------- ### Define a Custom Logger Source: https://docs.ultralig.ht/docs/logging-library-errors Implement a custom Logger class by inheriting from `ultralight::Logger` and overriding the `LogMessage` method. This is necessary when using the low-level renderer. ```cplusplus #include using namespace ultralight; // Define our custom Logger class class MyLogger : public Logger { public: MyLogger() {} virtual ~MyLogger() {} /// /// Called when the library wants to print a message to the log. /// virtual void LogMessage(LogLevel log_level, const String& message) override { printf("%s\n", message.utf8().data()); } }; // In your initialization routine: void InitApp() { // Tell the library to use our custom Logger class Platform::instance().set_logger(new MyLogger()); // Create renderer, views, etc. } ``` -------------------------------- ### Copy Bitmap Pixels to Texture Source: https://docs.ultralig.ht/docs/integrating-with-games Lock the Bitmap to access its raw pixel data (BGRA, 8-bpp, premultiplied alpha), retrieve dimensions, and then upload to a GPU texture. Remember to unlock the bitmap afterwards. ```c++ void CopyBitmapToTexture(RefPtr bitmap) { /// /// Lock the Bitmap to retrieve the raw pixels. /// The format is BGRA, 8-bpp, premultiplied alpha. /// void* pixels = bitmap->LockPixels(); /// /// Get the bitmap dimensions. /// uint32_t width = bitmap->width(); uint32_t height = bitmap->height(); uint32_t stride = bitmap->row_bytes(); /// /// Psuedo-code to upload our pixels to a GPU texture. /// CopyPixelsToTexture(pixels, width, height, stride); /// /// Unlock the Bitmap when we are done. /// bitmap->UnlockPixels(); } ``` -------------------------------- ### Acquire JS Context for a View Source: https://docs.ultralig.ht/docs/getting-the-js-context-for-a-view Acquire the JS execution context for the current page within the OnDOMReady callback. The context is automatically released when the ScopedJSContext goes out of scope. ```cpp /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { /// /// Acquire the JS execution context for the current page. /// auto scoped_context = caller->LockJSContext(); /// /// Typecast to the underlying JSContextRef. /// JSContextRef ctx = (*scoped_context); /// Make calls to JavaScriptCore API here... } ``` -------------------------------- ### Call Renderer::RefreshDisplay() in Render Loop (C++) Source: https://docs.ultralig.ht/docs/porting-from-13-to-14 When managing rendering manually, call Renderer::RefreshDisplay(0) before Renderer::Render() to notify the engine of display refreshes and allow animations to update correctly. This is crucial for synchronizing animations with the physical display refresh rate. ```cplusplus void MyApplication::Render() { // **NEW**: // Notify the renderer that the default display has physically refreshed // so that the engine has a chance to update animations. renderer.RefreshDisplay(0); // Render all Views to their respective surfaces / render-targets. renderer.Render(); // Handle any updated surfaces / textures here. } ``` -------------------------------- ### Direct3D 11 Render Target Blend Description Source: https://docs.ultralig.ht/docs/using-a-custom-gpudriver This configuration is used for the D3D11 driver to achieve accurate color blending. Ensure your engine uses the same blending functions for consistent results. ```cpp D3D11_RENDER_TARGET_BLEND_DESC rt_blend_desc; ZeroMemory(&rt_blend_desc, sizeof(rt_blend_desc)); rt_blend_desc.BlendEnable = true; rt_blend_desc.SrcBlend = D3D11_BLEND_ONE; rt_blend_desc.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; rt_blend_desc.BlendOp = D3D11_BLEND_OP_ADD; rt_blend_desc.SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; rt_blend_desc.DestBlendAlpha = D3D11_BLEND_ONE; rt_blend_desc.BlendOpAlpha = D3D11_BLEND_OP_ADD; rt_blend_desc.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; ``` -------------------------------- ### Manage JS String Lifetime with JSRetainPtr Source: https://docs.ultralig.ht/docs/evaluating-a-string-of-js Use JSRetainPtr<> for automatic memory management of JavaScript strings created with JSStringCreate methods. This RAII wrapper ensures proper retain/release cycles. ```c++ #include /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { /// /// Acquire the JS execution context for the current page. /// auto scoped_context = caller->LockJSContext(); /// /// Typecast to the underlying JSContextRef. /// JSContextRef ctx = (*scoped_context); const char* script = "document.body.innerHTML = 'Ultralight rocks!';"; /// /// Create our string of JavaScript, automatically managed by JSRetainPtr /// JSRetainPtr str = adopt( JSStringCreateWithUTF8CString(script)); /// /// Execute it with JSEvaluateScript, ignoring other parameters for now /// JSEvaluateScript(ctx, str.get(), 0, 0, 0, 0); } ``` -------------------------------- ### Cast and Use Custom Surface Source: https://docs.ultralig.ht/docs/using-a-custom-surface After registering your custom SurfaceFactory, you can safely cast the Surface obtained from a View to your custom type. This allows access to custom methods, such as retrieving an OpenGL texture handle. ```cplusplus /// /// Get the Surface for a View. /// Surface* surface = view->surface(); /// /// Cast it to a GLPBOTextureSurface. /// GLPBOTextureSurface* texture_surface = (GLPBOTextureSurface*)surface; /// /// Get the underlying texture handle. /// GLuint texture_id = texture_surface->GetTextureAndSyncIfNeeded(); /// /// Use the texture here... /// ``` -------------------------------- ### Accessing BitmapSurface Pixels Source: https://docs.ultralig.ht/docs/using-a-custom-surface Retrieve the Surface from a View, cast it to BitmapSurface, and access the underlying Bitmap to use its pixel data. ```cplusplus /// /// Get the pixel-buffer Surface for a View. /// Surface* surface = view->surface(); /// /// Cast it to a BitmapSurface. /// BitmapSurface* bitmap_surface = (BitmapSurface*)surface; /// /// Get the underlying bitmap. /// RefPtr bitmap = bitmap_surface->bitmap(); /// /// Use the bitmap here... /// ``` -------------------------------- ### Update Renderer Source: https://docs.ultralig.ht/docs/integrating-with-games Call Renderer::Update() frequently to process background tasks, JavaScript callbacks, and dispatch event listeners. This is crucial for keeping the web content interactive. ```c++ void UpdateLogic() { /// /// Give the library a chance to handle any pending tasks and timers. /// /// renderer->Update(); } ``` -------------------------------- ### Set Window Title in C++ Source: https://docs.ultralig.ht/docs/writing-your-first-app Modify the window title of your Ultralight application by changing the string passed to SetTitle in the C++ source code. ```cplusplus /// /// Set the title of our window. /// window_->SetTitle("MyApp"); ``` ```cplusplus /// /// Set the title of our window. /// window_->SetTitle("My Awesome App!"); ``` -------------------------------- ### Dispatch Keyboard Event Conditionally Source: https://docs.ultralig.ht/docs/integrating-with-games Only dispatch keyboard events to the view if it has input focus, checked using View::HasInputFocus(). ```cpp if (view->HasInputFocus()) { /// /// The View has an input element with visible keyboard focus (blinking caret). /// Dispatch the keyboard event to the view and consume it. /// DispatchAndConsumeKeyboardEvent(view, evt); } ``` -------------------------------- ### Access Bitmap from Surface Source: https://docs.ultralig.ht/docs/integrating-with-games Cast the Surface to a BitmapSurface and retrieve the underlying Bitmap. This allows direct access to pixel data. ```c++ /// /// Get the pixel-buffer Surface for a View. /// Surface* surface = view->surface(); /// /// Cast it to a BitmapSurface. /// BitmapSurface* bitmap_surface = (BitmapSurface*)surface; /// /// Get the underlying bitmap. /// RefPtr bitmap = bitmap_surface->bitmap(); ``` -------------------------------- ### C Callback Function Signature Source: https://docs.ultralig.ht/docs/calling-a-c-function-from-js Defines the signature for C functions that can be used as callbacks for JavaScript functions. It includes context, function, 'this' object, arguments, and an exception pointer. ```c JSValueRef MyCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Handle JavaScript arguments in our C callback here... // Optionally return a value back to JavaScript return JSValueMakeNull(ctx); } ``` -------------------------------- ### Evaluate JavaScript Directly with JSEvaluateScript() Source: https://docs.ultralig.ht/docs/evaluating-a-string-of-js Use JSEvaluateScript() for lower-level control over script execution, including exception handling. This requires manual JavaScript string creation and management. ```c++ /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { /// /// Acquire the JS execution context for the current page. /// auto scoped_context = caller->LockJSContext(); /// /// Typecast to the underlying JSContextRef. /// JSContextRef ctx = (*scoped_context); const char* script = "document.body.innerHTML = 'Ultralight rocks!';"; /// /// Create our string of JavaScript /// JSStringRef str = JSStringCreateWithUTF8CString(script); /// /// Execute it with JSEvaluateScript, ignoring other parameters for now /// JSEvaluateScript(ctx, str, 0, 0, 0, 0); /// /// Release our string (we only Release what we Create) /// JSStringRelease(str); } ``` -------------------------------- ### Call JS Function by Evaluating Script String Source: https://docs.ultralig.ht/docs/calling-a-js-function-from-c Use EvaluateScript to call a JavaScript function by passing a string of JavaScript code. Ensure the DOM is ready before calling this method. ```cplusplus /// /// Use LoadListener::OnDOMReady to wait for the DOM to load. /// void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { if (!is_main_frame) return; /// /// Call ShowMessage() by evaluating a string of JavaScript /// caller->EvaluateScript("ShowMessage('Howdy!')"); } ``` -------------------------------- ### Binding C Callback to JavaScript Function Source: https://docs.ultralig.ht/docs/calling-a-c-function-from-js This C++ code binds a C callback function 'OnButtonClick' to a JavaScript global object property named 'OnButtonClick'. It executes JavaScript to update the DOM. ```c++ // This callback will be bound to 'OnButtonClick()' on the page. JSValueRef OnButtonClick(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { const char* str = "document.getElementById('result').innerText = 'Ultralight rocks!'"; // Create our string of JavaScript JSStringRef script = JSStringCreateWithUTF8CString(str); // Execute it with JSEvaluateScript, ignoring other parameters for now JSEvaluateScript(ctx, script, 0, 0, 0, 0); // Release our string (we only Release what we Create) JSStringRelease(script); return JSValueMakeNull(ctx); } // Use LoadListener::OnDOMReady to wait for the DOM to load. void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { // Ignore DOMReady events from child frames. if (!is_main_frame) return; // Acquire the JS execution context for the current page. auto scoped_context = caller->LockJSContext(); // Typecast to the underlying JSContextRef. JSContextRef ctx = (*scoped_context); // Create a JavaScript String containing the name of our callback. JSStringRef name = JSStringCreateWithUTF8CString("OnButtonClick"); // Create a garbage-collected JavaScript function that is bound to our // native C callback 'OnButtonClick()'. JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, name, OnButtonClick); // Get the global JavaScript object (aka 'window') JSObjectRef globalObj = JSContextGetGlobalObject(ctx); // Store our function in the page's global JavaScript object so that it // accessible from the page as 'OnButtonClick()'. JSObjectSetProperty(ctx, globalObj, name, func, 0, 0); // Release the JavaScript String we created earlier. JSStringRelease(name); } ``` -------------------------------- ### Set Compiler for Linux Builds Source: https://docs.ultralig.ht/docs/trying-the-samples On Linux, explicitly set CC and CXX environment variables to Clang if you encounter compilation issues with GCC. You may need to delete your build directory before re-running CMake. ```shell export CC=/usr/bin/clang export CXX=/usr/bin/clang++ ``` -------------------------------- ### Call JS Function Directly with JSObjectCallAsFunction Source: https://docs.ultralig.ht/docs/calling-a-js-function-from-c For improved performance and control, use JSObjectCallAsFunction to pass arguments directly to a JavaScript function object. This method also supports exception handling and 'this' object overriding. Ensure the JavaScript context is locked and the function is retrieved before calling. ```cplusplus #include // Use LoadListener::OnDOMReady to wait for the DOM to load. void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame, const String& url) { if (!is_main_frame) return; // Acquire the JS execution context for the current page. auto scoped_context = caller->LockJSContext(); // Typecast to the underlying JSContextRef. JSContextRef ctx = (*scoped_context); // Get the ShowMessage function by evaluating a script. We could have // also used JSContextGetGlobalObject() and JSObjectGetProperty() to // retrieve this from the global window object as well. // Create our string of JavaScript, automatically managed by JSRetainPtr JSRetainPtr str = adopt( JSStringCreateWithUTF8CString("ShowMessage")); // Evaluate the string "ShowMessage" JSValueRef func = JSEvaluateScript(ctx, str.get(), 0, 0, 0, 0); // Check if 'func' is actually an Object and not null if (JSValueIsObject(ctx, func)) { // Cast 'func' to an Object, will return null if typecast failed. JSObjectRef funcObj = JSValueToObject(ctx, func, 0); // Check if 'funcObj' is a Function and not null if (funcObj && JSObjectIsFunction(ctx, funcObj)) { // Create a JS string from null-terminated UTF8 C-string, store it // in a smart pointer to release it when it goes out of scope. JSRetainPtr msg = adopt(JSStringCreateWithUTF8CString("Howdy!")); // Create our list of arguments (we only have one) const JSValueRef args[] = { JSValueMakeString(ctx, msg.get()) }; // Count the number of arguments in the array. size_t num_args = sizeof(args) / sizeof(JSValueRef*); // Create a place to store an exception, if any JSValueRef exception = 0; // Call the ShowMessage() function with our list of arguments. JSValueRef result = JSObjectCallAsFunction(ctx, funcObj, 0, num_args, args, &exception); if (exception) { // Handle any exceptions thrown from function here. } if (result) { // Handle result (if any) here. } } } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.