Created
July 28, 2026 02:37
-
-
Save yuters/cdc10ee6fdab4c567674dc6c4a1d2d97 to your computer and use it in GitHub Desktop.
Fix XWayland issues on Hyprland with the block/buzz AppImage (font rendering and animations)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # Launch Buzz on this machine (Arch, Hyprland, NVIDIA proprietary). | |
| # | |
| # Runs the real .AppImage, not an unpacked copy: the AppImage runtime is what sets | |
| # APPIMAGE=, and Tauri's updater refuses to self-update without it ("switch to AppImage | |
| # for automatic updates"). The updater rewrites that file in place, so its name must stay | |
| # version-less — never rename it to Buzz_<version>.AppImage. | |
| # | |
| # Everything below is a workaround for a specific, reproduced failure. None of it is | |
| # decorative; each line has a symptom attached. | |
| # | |
| set -euo pipefail | |
| # Resolve everything relative to this script, so the whole directory can be moved or | |
| # renamed without editing paths. realpath resolves the ~/.local/bin symlink that the | |
| # desktop entry actually invokes. | |
| readonly buzz_root="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" | |
| readonly buzz_appimage="${buzz_root}/Buzz.AppImage" | |
| if [[ ! -x "${buzz_appimage}" ]]; then | |
| printf 'Buzz AppImage is missing or not executable: %s\n' "${buzz_appimage}" >&2 | |
| exit 1 | |
| fi | |
| # Ask the dynamic linker where a library actually lives, rather than assuming /usr/lib | |
| # (wrong on Debian-style multiarch, and on any distro that moves libdir). | |
| resolve_lib() { | |
| local soname="$1" path | |
| path=$(ldconfig -p 2>/dev/null | awk -v s="$soname" '$1 == s { print $NF; exit }') | |
| if [[ -z "${path}" || ! -e "${path}" ]]; then | |
| printf 'required system library not found: %s\n' "${soname}" >&2 | |
| exit 1 | |
| fi | |
| printf '%s' "${path}" | |
| } | |
| # X11/XWayland, so Hyprland draws server-side decorations. On the Wayland backend GTK3 | |
| # draws its own titlebar and GTK_CSD=0 does not suppress it. AppRun's | |
| # linuxdeploy-plugin-gtk.sh hook sets the same value, so the two agree. | |
| export GDK_BACKEND="x11" | |
| # Hyprland runs no XSETTINGS manager, so on the X11 backend GDK ignores | |
| # ~/.config/gtk-3.0/settings.ini for gtk-xft-* keys and falls back to its built-in | |
| # hintstyle=hintmedium / rgba=rgb. hintmedium snaps glyph advances to whole pixels, which | |
| # is what produces ragged inter-letter spacing ("Mentio ned", "unle ss") in WebKit. | |
| # | |
| # XENVIRONMENT scopes the corrected values to this process only, so no other X client is | |
| # affected — unlike a global `xrdb -merge`. GDK reads the file once, when the X connection | |
| # opens, so writing it here (before exec) is early enough. It lives in XDG_RUNTIME_DIR so | |
| # it is regenerated every launch and cleaned up at logout; nothing to lose or maintain. | |
| # | |
| # Beware: X silently ignores an XENVIRONMENT pointing at a missing file, and the only | |
| # symptom is the ragged text above — which reads as a font bug and sends you chasing | |
| # fonts and CSS kerning rather than the backend. Hence the write is checked. | |
| readonly xft_resources="${XDG_RUNTIME_DIR:-/tmp}/buzz-xft-resources.ad" | |
| if ! cat > "${xft_resources}" <<'XFT' | |
| ! Generated by buzz-desktop-launcher — edits here are overwritten on next launch. | |
| Xft.antialias: 1 | |
| Xft.hinting: 1 | |
| Xft.hintstyle: hintslight | |
| Xft.rgba: none | |
| XFT | |
| then | |
| printf 'could not write Xft resources: %s\n' "${xft_resources}" >&2 | |
| exit 1 | |
| fi | |
| export XENVIRONMENT="${xft_resources}" | |
| # Keep WebKit's DMABUF renderer but move buffers through shared memory. GBM allocation | |
| # fails on this card ("Failed to create GBM buffer of size 3840x2160: Invalid argument"). | |
| # Measured on a blurred-gradient workload: FORCE_SHM ~20 paints/s (at the sampling | |
| # ceiling), DISABLE_DMABUF_RENDERER ~6, plain GBM never paints at all. | |
| # | |
| # Do NOT swap in WEBKIT_DMABUF_RENDERER_DISABLE_GBM=1. It benchmarks well and renders a | |
| # completely blank window. | |
| export WEBKIT_DMABUF_RENDERER_FORCE_SHM="1" | |
| # The bundled GStreamer plugins alone lack appsink/appsrc/autoaudiosink, which breaks | |
| # audio. GST_PLUGIN_PATH_1_0 is the one that actually carries the system dir through: | |
| # AppRun overwrites GST_PLUGIN_SYSTEM_PATH_1_0 with its own AppDir path and drops our | |
| # value. Set both anyway — the SYSTEM_PATH one is inert here but costs nothing, and | |
| # stops this looking like an oversight next time someone reads it. | |
| if system_gstreamer_plugins=$(pkg-config --variable=pluginsdir gstreamer-1.0 2>/dev/null) \ | |
| && [[ -d "${system_gstreamer_plugins}" ]]; then | |
| export GST_PLUGIN_SYSTEM_PATH_1_0="${system_gstreamer_plugins}" | |
| export GST_PLUGIN_PATH_1_0="${system_gstreamer_plugins}" | |
| fi | |
| # LD_PRELOAD carries two fixes. It is the only lever that still works, because | |
| # AppRun.wrapped unconditionally PREPENDS the AppDir lib dirs to LD_LIBRARY_PATH — so | |
| # setting LD_LIBRARY_PATH here would always lose. | |
| # | |
| # libsystemd Required to start at all. The bundle ships an older libsystemd than | |
| # the system libmount.so.1 (not bundled, loaded from the system libdir) | |
| # needs: libsystemd.so.0: version `LIBSYSTEMD_251' not found. | |
| # | |
| # libwebkit2gtk Fixes the stuttering splash animation. The bundled WebKit janks; | |
| # libjavascriptcore the system one is smooth. Both must be preloaded together — they | |
| # are a matched pair. | |
| # | |
| # Appends any caller-supplied LD_PRELOAD instead of clobbering it, so a one-off preload | |
| # can be injected for an experiment without editing this file. | |
| preload="" | |
| for soname in libsystemd.so.0 libwebkit2gtk-4.1.so.0 libjavascriptcoregtk-4.1.so.0; do | |
| preload="${preload}${preload:+:}$(resolve_lib "${soname}")" | |
| done | |
| export LD_PRELOAD="${preload}${LD_PRELOAD:+:$LD_PRELOAD}" | |
| exec "${buzz_appimage}" "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment