Building an application with BlinkGTK

A minimal guide to building an application that embeds BlinkGTK.

First: you do not build BlinkGTK itself from source

BlinkGTK embeds Chromium, so building it requires a Chromium source tree (over 100 GB) and
anywhere from several to well over ten hours of build time. Use the distributed packages.
This page describes how to build your application against those packages.

The distribution is split into three packages:

Package Purpose
runtime The shared library and the Chromium runtime. Needed to run
devel Headers and the .pc file. Needed to build
gir GObject Introspection typelib. Needed to use BlinkGTK from Python or GJS

1. Install

# Fedora
sudo dnf install ./blinkgtk-bin-1.2.0-build2.fc44.x86_64.rpm \
                 ./blinkgtk-bin-devel-1.2.0-build2.fc44.x86_64.rpm

# Debian / Ubuntu
sudo dpkg -i ./libblinkgtk-0.1-0_1.2.0-build2_amd64.deb \
             ./libblinkgtk-0.1-dev_1.2.0-build2_amd64.deb

If you use the tarball, extract it anywhere and tell pkg-config where it is:

tar xzf blinkgtk-1.2.0-build2-linux-x86_64.tar.gz -C /opt/blinkgtk
export PKG_CONFIG_PATH=/opt/blinkgtk/usr/lib64/pkgconfig:$PKG_CONFIG_PATH

This is the easiest thing to get wrong, and it produces the most confusing failure.

cc -o myapp myapp.c $(pkg-config --cflags --libs blinkgtk-0.1)

Do not hand-write -lblinkgtk on its own. BlinkGTK requires Chromium's
PartitionAlloc allocator shim, and that shim must be a direct dependency
(DT_NEEDED) of the executable so that it is loaded early. The Libs line of
blinkgtk-0.1.pc already includes it.

If the shim is missing, the program builds, links, and starts — but page loads never
complete
(navigation does not commit, and the window stays blank). The cause is hard to
spot from the symptom alone.

To check:

readelf -d ./myapp | grep allocator_shim
# One or more lines: good. Nothing: you are not going through pkg-config
#include <blink_gtk/blink_gtk.h>
#include <gtk/gtk.h>

The public API is collected in this one header. Do not include internal headers directly;
they change between versions.

4. A minimal application

Lifecycle of a BlinkGTK application: point at the resources, blink_gtk_init, create the window and the WebView, load and run the main loop, then shut down.

The call order matters. In particular, call blink_gtk_init() before GTK.

#include <blink_gtk/blink_gtk.h>
#include <gtk/gtk.h>

static void on_activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *win = gtk_application_window_new(app);
    gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);

    GtkWidget *view = blink_web_view_new();
    gtk_window_set_child(GTK_WINDOW(win), view);
    gtk_widget_set_visible(win, TRUE);

    blink_web_view_load_uri(BLINK_WEB_VIEW(view), "https://example.com/");
}

int main(int argc, char **argv) {
    blink_gtk_init(&argc, &argv);
    GtkApplication *app = gtk_application_new("com.example.MyApp",
                                              G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
    /* BlinkGTK needs Chromium's browser main loop to run.
     * g_application_run() does not run it, so the page never loads
     * (the navigation never commits).
     */
    GError *error = NULL;
    if (!g_application_register(G_APPLICATION(app), NULL, &error)) {
        g_printerr("Failed to register the application: %s\n", error ? error->message : "unknown error");
        g_clear_error(&error);
        g_object_unref(app);
        return 1;
    }
    g_application_activate(G_APPLICATION(app));
    int status = blink_gtk_run_main_loop();
    g_object_unref(app);
    return status;
}

Note: blink_web_view_new() returns a GtkWidget *, but the BlinkGTK API takes a
BlinkWebView *. Cast with BLINK_WEB_VIEW().

5. Example Makefile

CC      ?= cc
PKGS     = blinkgtk-0.1
CFLAGS  += -O2 -Wall $(shell pkg-config --cflags $(PKGS))
LDLIBS  += $(shell pkg-config --libs $(PKGS))

myapp: myapp.c
    $(CC) $(CFLAGS) -o $@ $< $(LDLIBS)

6. What is needed at run time

Wayland is required

BlinkGTK is Wayland only; it does not run on X11. Run it in a Wayland session, or use
Weston's headless backend for automated verification:

weston --backend=headless-backend.so --width=1024 --height=768 &
export WAYLAND_DISPLAY=wayland-1

Where Chromium's resource files must be

Chromium needs its ICU data (icudtl.dat), V8 snapshots (snapshot_blob.bin and
v8_context_snapshot.bin), and resource packs (content_shell.pak). These are looked up
relative to the directory containing the executable, not the current working directory.

With an RPM or DEB installation everything is already in the right place, so there is
normally nothing to do. If you extracted the tarball, or moved your executable elsewhere,
you will see:

ERROR:base/i18n/icu_util.cc: Invalid file descriptor to ICU data received.
FATAL:base/i18n/icu_util.cc: Check failed: result.

Put the executable in the same directory as the resources, or set the library search path
to match:

export LD_LIBRARY_PATH=/opt/blinkgtk/usr/lib64/blinkgtk-0.1:\
/opt/blinkgtk/usr/lib64/blinkgtk-0.1/chromium

7. What to check first when something goes wrong

Symptom Check this first
Link errors (undefined reference) Does pkg-config --libs blinkgtk-0.1 print anything? Is the devel package installed?
Starts, but the page stays blank readelf -d ./myapp | grep allocator_shim — is the shim a direct dependency? (section 2)
Invalid file descriptor to ICU data Where the executable sits relative to the resources (section 6)
No window appears Are you in a Wayland session? echo $WAYLAND_DISPLAY
Python cannot import it Is the gir package installed?

8. About using GTK4 itself

BlinkGTK is a library that provides one GTK4 widget. How to use GTK4 itself — windows,
layout, buttons, menus, event handling — is covered by the GTK4 documentation, not here.

BlinkWebView derives from GtkWidget, so it behaves like any other GTK4 widget: put it in a
container, set size requests, connect signals.

The main loop is the one exception. Use blink_gtk_run_main_loop() rather than GTK's
g_application_run() or g_main_loop_run() (see section 4).

Using it from Python, Rust, and other languages

BlinkGTK supports GObject Introspection, so it can be used from PyGObject and gtk-rs. Refer to the
documentation of each binding for how to use them.