My colleague Kate recently demonstrated on her blog how simple it is to write a WPE Platform-based launcher, and did so by building it side-by-side with MiniBrowser, inside the WebKit tree.
This entry takes one step back, and demonstrates the same concepts assuming you are not building WPE WebKit yourself, but rather getting it from your distribution. Many of the steps below would apply if you were using a Yocto/OpenEmbedded-based image, but that can be the focus of another post.
Getting WPE WebKit
Get WPE lists a number of options to get WPE from your preferred distribution. At the moment of writing, Fedora, Debian and ArchLinux are your best choices to get a recent version of WPE:
- 2.52 on Fedora
- 2.50 on Debian Forky, 2.52 on Debian Sid
- 2.50 on ArchLinux
However, since WPE Platform hasn’t officially been released, we need to use Fedora, where my colleague Philippe maintains a Copr repository with it enabled.
sudo dnf copr enable -y philn/wpewebkit
sudo dnf install wpewebkit-devel
Alternatively, you can use a container. Here is a Containerfile based on Fedora 42:
FROM fedora:42
RUN dnf install -y \
dnf-plugins-core \
&& dnf copr enable -y philn/wpewebkit \
&& dnf install -y \
gcc-c++ \
cmake \
pkg-config \
wpewebkit-devel
WORKDIR /src
Build and run it with:
podman build -t wpe-dev .
podman run -it -e WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
-e XDG_RUNTIME_DIR=/run/user/$(id -u) \
-v $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY:/run/user/$(id -u)/$WAYLAND_DISPLAY \
-v /dev/dri:/dev/dri \
wpe-dev bash
The build system
Kate’s post builds the launcher as part of the WebKit tree using WebKit’s own CMake infrastructure. For a standalone project, we need a self-contained CMakeLists.txt that finds WPE WebKit through pkg-config:
cmake_minimum_required(VERSION 3.16)
project(wpe_sample CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(PkgConfig REQUIRED)
# The Wayland WPE Platform already depends on wpe-platform-2.0
pkg_check_modules(WebKitDeps REQUIRED
IMPORTED_TARGET
wpe-webkit-2.0
wpe-platform-wayland-2.0
)
add_executable(wpe_sample main.cpp)
target_link_libraries(wpe_sample
PRIVATE
PkgConfig::WebKitDeps
)
The launcher
Here is a minimal launcher — the smallest amount of code needed to display a web page with WPE WebKit:
#include <wpe/webkit.h>
int main(int argc, const char *argv[]) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, false);
g_autoptr(WebKitWebView) view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
nullptr));
webkit_web_view_load_uri(view,
(argc > 1) ? argv[1] : "https://wpewebkit.org");
g_main_loop_run(loop);
return EXIT_SUCCESS;
}
This snippet relies heavily on default behaviours: it will create a default WPE view, with default top levels, with the default display selection behaviour (Wayland), default context, settings…
Again, Kate’s post does a more realistic job at showing how the various pieces are created and connected together.
Building and running
cmake -B build
cmake --build build
./build/wpe_sample https://wpewebkit.org/

Display backends
WPE WebKit can render to different display backends depending on your environment, which you can select through environment variables:
# Wayland (e.g. desktop, Weston).
WPE_DISPLAY=wpe-display-wayland WAYLAND_DISPLAY=wayland-1 ./build/wpe_sample https://wpewebkit.org/
# DRM/KMS (e.g. embedded, no compositor)
WPE_DISPLAY=wpe-display-drm ./build/wpe_sample https://wpewebkit.org/
# Headless (e.g. testing, CI)
WPE_DISPLAY=wpe-display-headless ./build/wpe_sample https://wpewebkit.org/
You can take a look at wpe_display_get_default() in WPEPlatform/wpe/WPEDisplay.cpp to understand how the automatic selection takes place in the absence of an explicit WPE_DISPLAY request.
(In our example, we are only listing Wayland as a CMake dependency. If libwpewebkit was compiled without DRM or headless support, the environment variable approach would not work.)
Next steps
This is all for now. The next entry in the series will cover classic kiosk features: preventing navigation to unwanted sites, controlling whether new windows can be opened, and intercepting requests through policy decisions.
For a more complete example that includes a custom HTML context menu and JavaScript injection, see Kate’s post.