Let the host's libwayland-client win in the AppImage #50

Merged
jknapp merged 1 commits from fix/appimage-wayland-client into main 2026-09-03 00:22:03 +00:00
Owner

The AppImage comes up blank on CachyOS with:

Could not create default EGL display: EGL_BAD_PARAMETER. Aborting...

and only starts when run as LD_PRELOAD=/usr/lib/libwayland-client.so ./Triple-C_0.4.19_amd64.AppImage.

It is not the DMA-BUF bug, despite the identical error text

main.rs already sets WEBKIT_DISABLE_DMABUF_RENDERER=1 unconditionally on Linux (#38), and its comment quoted this exact string as the error it fixes. That claim is wrong, and it cost a round of debugging that started from the comment instead of the evidence.

The flag is present and working — verified by pulling the released 0.4.19 artifact and finding the variable compiled into usr/bin/triple-c. The crash happens anyway.

What it actually is

linuxdeploy-plugin-gtk bundles libwayland-client.so.0 as a GTK dependency, and AppRun.wrapped puts the bundled directory ahead of the host's on the loader path. The host's Mesa then resolves against our copy instead of the one it was built against.

libEGL_mesa.so.0 — the driver libglvnd's libEGL.so.1 dlopens — carries a hard DT_NEEDED on libwayland-client.so.0. When those symbols will not resolve the driver never loads, glvnd is left with no driver, and eglGetDisplay reports no display. That is why GDK_BACKEND=x11 does not dodge it, and why the symptom is a bad-parameter error rather than a link failure.

Bisected on the reporter's machine, against the released artifact:

removed from the AppDir result
libwayland-client.so.0 no EGL abort
libwayland-egl.so.1 still fails
libepoxy.so.0 still fails

The bundled copy (Ubuntu 22.04 runner, wayland 1.20) is missing eleven symbols their wayland 1.26 exports, wl_proxy_get_display, wl_proxy_get_queue, wl_display_create_queue_with_name and wl_fixes_interface among them.

Why not just bundle a newer wayland

The version floor is set by the host's Mesa, which we neither ship nor can ship — Mesa has to match the running kernel's DRM. Their Mesa updates independently of our releases, so any version we pin is one wayland release away from being too old again. This is a host-coupled library in the same class as libGL, libEGL and libdrm: the only correct version is the host's. It simply is not on linuxdeploy's excludelist.

Demoted, not deleted

The bundled copy moves off the loader path into usr/lib/wayland-fallback, and an AppRun hook adds that directory back only when the host has no libwayland-client of its own. So a host without one still starts, and nothing is lost.

The ordering is safe because AppRun.wrapped appends the inherited LD_LIBRARY_PATH after its own AppDir entries — anything the hook exports lands last, making it a fallback and never an override. AppRun sources hooks by name rather than globbing the directory, so it is patched to source this one.

X11-only hosts are unaffected

libwayland-client is a package dependency of Mesa and GTK, so it is present even on a machine with no display server at all — confirmed on a headless container with neither DISPLAY nor WAYLAND_DISPLAY set. And this AppImage already runs as an X11 client everywhere, since linuxdeploy's own hook forces GDK_BACKEND=x11.

Verification

Against the real 0.4.19 artifact pulled from the release, not a synthetic AppDir:

  • it repacks, and the repacked image extracts cleanly
  • usr/bin/triple-c and AppRun survive intact
  • both hook branches exercised — a host with the library leaves LD_LIBRARY_PATH untouched; a debian:12-slim container with no wayland at all engages the fallback

The script's post-repack assertions are the regression test: they fail the build if the library is still on the loader path, the fallback copy or hook is missing, AppRun does not source it, or the binary did not survive. Rust suite unchanged at 541 passing.

What this PR cannot prove is the end result on the reporter's machine — that needs CI to build a new AppImage first.

🤖 Generated with Claude Code

https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm

The AppImage comes up blank on CachyOS with: ``` Could not create default EGL display: EGL_BAD_PARAMETER. Aborting... ``` and only starts when run as `LD_PRELOAD=/usr/lib/libwayland-client.so ./Triple-C_0.4.19_amd64.AppImage`. ## It is not the DMA-BUF bug, despite the identical error text `main.rs` already sets `WEBKIT_DISABLE_DMABUF_RENDERER=1` unconditionally on Linux (#38), and its comment quoted *this exact string* as the error it fixes. That claim is wrong, and it cost a round of debugging that started from the comment instead of the evidence. The flag is present and working — verified by pulling the released 0.4.19 artifact and finding the variable compiled into `usr/bin/triple-c`. The crash happens anyway. ## What it actually is `linuxdeploy-plugin-gtk` bundles `libwayland-client.so.0` as a GTK dependency, and `AppRun.wrapped` puts the bundled directory **ahead of the host's** on the loader path. The host's Mesa then resolves against our copy instead of the one it was built against. `libEGL_mesa.so.0` — the driver libglvnd's `libEGL.so.1` dlopens — carries a hard `DT_NEEDED` on `libwayland-client.so.0`. When those symbols will not resolve the driver never loads, glvnd is left with no driver, and `eglGetDisplay` reports no display. That is why `GDK_BACKEND=x11` does not dodge it, and why the symptom is a bad-parameter error rather than a link failure. **Bisected on the reporter's machine, against the released artifact:** | removed from the AppDir | result | |---|---| | `libwayland-client.so.0` | **no EGL abort** | | `libwayland-egl.so.1` | still fails | | `libepoxy.so.0` | still fails | The bundled copy (Ubuntu 22.04 runner, wayland 1.20) is missing eleven symbols their wayland 1.26 exports, `wl_proxy_get_display`, `wl_proxy_get_queue`, `wl_display_create_queue_with_name` and `wl_fixes_interface` among them. ## Why not just bundle a newer wayland The version floor is set by the **host's** Mesa, which we neither ship nor can ship — Mesa has to match the running kernel's DRM. Their Mesa updates independently of our releases, so any version we pin is one wayland release away from being too old again. This is a host-coupled library in the same class as libGL, libEGL and libdrm: the only correct version is the host's. It simply is not on linuxdeploy's excludelist. ## Demoted, not deleted The bundled copy moves off the loader path into `usr/lib/wayland-fallback`, and an AppRun hook adds that directory back **only when the host has no `libwayland-client` of its own**. So a host without one still starts, and nothing is lost. The ordering is safe because `AppRun.wrapped` appends the inherited `LD_LIBRARY_PATH` *after* its own AppDir entries — anything the hook exports lands last, making it a fallback and never an override. AppRun sources hooks by name rather than globbing the directory, so it is patched to source this one. ## X11-only hosts are unaffected `libwayland-client` is a package dependency of Mesa and GTK, so it is present even on a machine with **no display server at all** — confirmed on a headless container with neither `DISPLAY` nor `WAYLAND_DISPLAY` set. And this AppImage already runs as an X11 client everywhere, since linuxdeploy's own hook forces `GDK_BACKEND=x11`. ## Verification Against the **real 0.4.19 artifact** pulled from the release, not a synthetic AppDir: - it repacks, and the repacked image extracts cleanly - `usr/bin/triple-c` and `AppRun` survive intact - **both hook branches exercised** — a host with the library leaves `LD_LIBRARY_PATH` untouched; a `debian:12-slim` container with no wayland at all engages the fallback The script's post-repack assertions are the regression test: they fail the build if the library is still on the loader path, the fallback copy or hook is missing, AppRun does not source it, or the binary did not survive. Rust suite unchanged at 541 passing. **What this PR cannot prove** is the end result on the reporter's machine — that needs CI to build a new AppImage first. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm
jknapp added 1 commit 2026-09-03 00:05:17 +00:00
Let the host's libwayland-client win in the AppImage
Secret Scan / scan (push) Successful in 3s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m52s
Build App (Preview) / build-linux (pull_request) Successful in 5m20s
Build App (Preview) / build-windows (pull_request) Successful in 5m21s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
a3bdf6f4da
The AppImage came up blank on CachyOS with `Could not create default EGL
display: EGL_BAD_PARAMETER. Aborting...`, and the DMA-BUF workaround already
in `main.rs` did not help — verified by finding the flag compiled into the
shipped 0.4.19 binary, where it runs unconditionally on Linux.

It is a different fault with the same error text. linuxdeploy bundles
`libwayland-client.so.0` as a GTK dependency and `AppRun.wrapped` puts the
bundled directory ahead of the host's, so the host's Mesa resolves against our
copy. `libEGL_mesa.so.0` — the driver libglvnd's `libEGL.so.1` dlopens — has a
hard DT_NEEDED on that library, so when its symbols will not resolve the
driver never loads, glvnd is left with none, and `eglGetDisplay` reports no
display. That is why `GDK_BACKEND=x11` does not dodge it, and why the symptom
is a bad-parameter error rather than a link failure.

Bisected on the reporter's machine against the released artifact — removing
`libwayland-client.so.0` from the AppDir cleared the abort, while removing
`libwayland-egl` or `libepoxy` did not. The bundled copy (Ubuntu 22.04,
wayland 1.20) is missing eleven symbols their wayland 1.26 exports, including
`wl_proxy_get_display`, `wl_proxy_get_queue`,
`wl_display_create_queue_with_name` and `wl_fixes_interface`.

Bundling a newer wayland would defer this, not fix it: the floor is set by the
host's Mesa, which updates independently of our releases, so any version we
pick is one release away from being too old again. This is a host-coupled
library like libGL and libdrm — the only correct version is the host's.

So the copy is demoted rather than deleted. It moves off the loader path into
`usr/lib/wayland-fallback`, and a hook adds that directory back only when the
host has no libwayland-client of its own — so a host without one still starts.
The ordering is safe because `AppRun.wrapped` appends the inherited
LD_LIBRARY_PATH after its own entries, making the hook a fallback and never an
override. AppRun sources hooks by name rather than globbing, so it is patched
to source this one.

X11-only hosts are unaffected: libwayland-client is a package dependency of
Mesa and GTK, so it is present even on a machine with no display server at all
— confirmed on a headless container with neither DISPLAY nor WAYLAND_DISPLAY
set. And the AppImage already runs as an X11 client everywhere, since
linuxdeploy's own hook forces GDK_BACKEND=x11.

Verified against the real 0.4.19 artifact rather than a synthetic AppDir: it
repacks, the binary and AppRun survive, and both hook branches were exercised
— host-has-it leaves LD_LIBRARY_PATH untouched, and a debian:12-slim container
with no wayland at all engages the fallback.

The comment in `main.rs` quoted this exact error as one the DMA-BUF flag
fixes. That claim sent this investigation down the wrong path first, so it is
corrected rather than left to do it again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm
jknapp merged commit a0b9f1e19b into main 2026-09-03 00:22:03 +00:00
jknapp deleted branch fix/appimage-wayland-client 2026-09-03 00:22:03 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: CyberCoveLLC/Triple-C#50