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
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
146 lines
6.6 KiB
Rust
146 lines
6.6 KiB
Rust
// Prevents additional console window on Windows in release
|
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
|
|
/// WebKitGTK's DMA-BUF renderer (its default accelerated-compositing path
|
|
/// since 2.42) fails outright on some Mesa/driver/compositor combinations
|
|
/// under Wayland, killing the webview and leaving a blank window — see
|
|
/// triple-c#34, reported on CachyOS/Arch with Wayland.
|
|
///
|
|
/// **This is not the only cause of a blank window, and the error text alone
|
|
/// does not tell them apart.** An earlier version of this comment quoted
|
|
/// `Could not create default EGL display: EGL_BAD_PARAMETER. Aborting.` as
|
|
/// the error this fixes. The AppImage produces that same string for an
|
|
/// entirely unrelated reason: it bundled a `libwayland-client.so.0` that
|
|
/// shadowed the host's, and the host's `libEGL_mesa.so.0` has a hard
|
|
/// DT_NEEDED on that library, so the EGL driver failed to load before any
|
|
/// renderer choice was reachable. This flag was set, and correctly, and made no difference —
|
|
/// which cost a round of debugging that started from the comment rather than
|
|
/// from the evidence. See `scripts/unbundle-wayland-client.sh`.
|
|
///
|
|
/// Set unconditionally on Linux rather than gated on `WAYLAND_DISPLAY`: that
|
|
/// variable is exported into an XWayland client's environment too, so a
|
|
/// gate on it wouldn't even cleanly separate "Wayland" from "X11" — and
|
|
/// there is no reliable heuristic at all for the actual variable that
|
|
/// matters, which Mesa/driver/compositor combination is affected. This is
|
|
/// the blunt instrument, chosen deliberately because the fallback is a real
|
|
/// trade, not a free one: the terminal's `@xterm/addon-webgl` renderer
|
|
/// (`TerminalView.tsx`) is the one surface in this app actually asking for
|
|
/// GPU compositing, and it degrades to xterm's canvas renderer under this
|
|
/// setting — slower on very heavy output, but the addon's own construction
|
|
/// is already wrapped in a fallback (`WebGL not available` is a handled
|
|
/// case, not a crash), so this is a real but graceful downgrade, traded
|
|
/// against a startup abort that has no fallback at all.
|
|
///
|
|
/// Must be set before `triple_c_lib::run()` — GTK/WebKitGTK reads it at
|
|
/// their own init time, which happens inside the Tauri builder that
|
|
/// function calls into, not at binary load.
|
|
///
|
|
/// A user who has already set this themselves is left alone — with one
|
|
/// correction. The earlier version of this function left *any* pre-set value
|
|
/// alone, including `0`, on the assumption WebKitGTK reads the variable as a
|
|
/// boolean. WebKitGTK reads it as presence-only, so `WEBKIT_DISABLE_DMABUF_
|
|
/// RENDERER=0` disabled DMA-BUF exactly like `=1` did, and there was no value
|
|
/// at all a user could set to get the accelerated path back: the escape hatch
|
|
/// the comment described did not exist. `0`, `false` and empty are now treated
|
|
/// as an explicit opt-out and the variable is *removed*, which is the only
|
|
/// thing WebKitGTK reads as "enabled". The default is unchanged — unset still
|
|
/// means disabled on Linux, so nobody who was not deliberately overriding this
|
|
/// sees any difference.
|
|
///
|
|
/// That matters more than it looks, because the trade described above is not
|
|
/// the trade actually being made. `@xterm/addon-webgl` does not fall back to
|
|
/// the canvas renderer here: its constructor throws only when WebGL is
|
|
/// *absent*, and with DMA-BUF disabled WebGL is still present — served by
|
|
/// software rasterisation. So the addon loads happily and every terminal frame
|
|
/// is rendered on the CPU and copied, which is slower than the canvas renderer
|
|
/// this comment assumed it would degrade to, not faster. See
|
|
/// `terminal_gpu_rendering` in `AppSettings` for the switch that decides
|
|
/// whether the addon is loaded at all.
|
|
///
|
|
/// This env var also leaks to whatever the app spawns afterwards — notably
|
|
/// a cold-launched default browser via the `opener` plugin's `xdg-open`
|
|
/// call. Narrow in practice (an already-running browser just receives the
|
|
/// URL; most non-WebKitGTK browsers ignore the variable entirely), but
|
|
/// worth knowing before chasing the "links don't open" half of triple-c#34
|
|
/// as a separate, unrelated cause.
|
|
#[cfg(target_os = "linux")]
|
|
const DMABUF_VAR: &str = "WEBKIT_DISABLE_DMABUF_RENDERER";
|
|
|
|
/// What to do with `WEBKIT_DISABLE_DMABUF_RENDERER`, given whatever it is
|
|
/// already set to. Split from the mutation so it can be tested without
|
|
/// touching process-wide environment state from a parallel test runner.
|
|
#[cfg(target_os = "linux")]
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
enum DmabufAction {
|
|
/// Not set by the user — apply the workaround.
|
|
Disable,
|
|
/// Explicitly opted out. WebKitGTK reads presence, not value, so the only
|
|
/// way to express "enabled" is for the variable not to exist.
|
|
Remove,
|
|
/// Set to something meaning "disabled". Already what we want; leave it.
|
|
LeaveAlone,
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn dmabuf_action(current: Option<&str>) -> DmabufAction {
|
|
match current {
|
|
None => DmabufAction::Disable,
|
|
Some(value) => match value.trim().to_ascii_lowercase().as_str() {
|
|
"" | "0" | "false" | "no" => DmabufAction::Remove,
|
|
_ => DmabufAction::LeaveAlone,
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn apply_webkit_wayland_workaround() {
|
|
let current = std::env::var(DMABUF_VAR).ok();
|
|
match dmabuf_action(current.as_deref()) {
|
|
DmabufAction::Disable => std::env::set_var(DMABUF_VAR, "1"),
|
|
DmabufAction::Remove => std::env::remove_var(DMABUF_VAR),
|
|
DmabufAction::LeaveAlone => {}
|
|
}
|
|
}
|
|
|
|
#[cfg(all(test, target_os = "linux"))]
|
|
mod tests {
|
|
use super::{dmabuf_action, DmabufAction};
|
|
|
|
#[test]
|
|
fn unset_gets_the_workaround() {
|
|
assert_eq!(dmabuf_action(None), DmabufAction::Disable);
|
|
}
|
|
|
|
#[test]
|
|
fn falsey_values_opt_out_by_removing_the_variable() {
|
|
// The bug this replaces: these all previously read as "user set it,
|
|
// leave it alone", and WebKitGTK then disabled DMA-BUF anyway because
|
|
// it only checks presence. There was no way to ask for the GPU path.
|
|
for value in ["0", "false", "no", "", " 0 ", "FALSE", "No"] {
|
|
assert_eq!(
|
|
dmabuf_action(Some(value)),
|
|
DmabufAction::Remove,
|
|
"{value:?} should opt out"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn other_values_are_left_alone() {
|
|
for value in ["1", "true", "yes", "anything"] {
|
|
assert_eq!(
|
|
dmabuf_action(Some(value)),
|
|
DmabufAction::LeaveAlone,
|
|
"{value:?} should be left alone"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
#[cfg(target_os = "linux")]
|
|
apply_webkit_wayland_workaround();
|
|
|
|
triple_c_lib::run()
|
|
}
|