### 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.