Created
November 13, 2025 16:08
-
-
Save mal1k-me/65eb954d204ea0d302a854ac64d56199 to your computer and use it in GitHub Desktop.
Patch `.desktop` Files to Disable Client Side Decorations of GTK 3, GTK 4 and Libadwaita Applications in KDE Plasma on Arch Linux
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 | |
| # NOTE: Install gtk-nocsd-git from AUR first | |
| set -euo pipefail | |
| readonly WRAPPER_PATH="/usr/local/bin/gtk-nocsd-wrapper" | |
| readonly DEST_DIR="${HOME}/.local/share/applications" | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| log() { | |
| printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*" | |
| } | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| create_wrapper() { | |
| if [[ -f "${WRAPPER_PATH}" ]]; then | |
| log "Wrapper already exists: ${WRAPPER_PATH}" | |
| return 0 | |
| fi | |
| log "Creating wrapper at ${WRAPPER_PATH}" | |
| # Create wrapper content | |
| sudo tee "${WRAPPER_PATH}" >/dev/null <<'EOF' | |
| #!/usr/bin/env bash | |
| # Wrapper: load libgtk-nocsd only on KDE Plasma sessions | |
| if [[ "${XDG_CURRENT_DESKTOP:-}" =~ KDE ]]; then | |
| exec env LD_PRELOAD="/usr/lib/libgtk-nocsd.so" "$@" | |
| else | |
| exec "$@" | |
| fi | |
| EOF | |
| sudo chmod +x "${WRAPPER_PATH}" | |
| } | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| is_gtk_pkg() { | |
| pacman -Qi "$1" 2>/dev/null | grep -Eq 'Depends On.*(gtk3|gtk4|libadwaita)' | |
| } | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| merge_desktop_file() { | |
| local src="$1" | |
| local dest="$2" | |
| # If destination doesn't exist, copy directly and patch Exec | |
| if [[ ! -f "${dest}" ]]; then | |
| cp -f "${src}" "${dest}" | |
| patch_exec_line "${dest}" | |
| log "Added new: $(basename "${dest}")" | |
| return 0 | |
| fi | |
| # Otherwise, merge differences excluding Exec= line | |
| local tmp | |
| tmp="$(mktemp)" | |
| awk ' | |
| BEGIN { exec_line = "" } | |
| /^Exec=/ { | |
| exec_line = $0 | |
| next | |
| } | |
| { print } | |
| END { | |
| if (exec_line != "") | |
| print exec_line | |
| } | |
| ' "${dest}" >"${tmp}.user" | |
| awk ' | |
| /^Exec=/ { next } | |
| { print } | |
| ' "${src}" >"${tmp}.sys" | |
| # If files differ aside from Exec=, update user copy | |
| if ! diff -q "${tmp}.user" "${tmp}.sys" >/dev/null 2>&1; then | |
| log "Updating non-Exec changes: $(basename "${dest}")" | |
| # Replace all non-Exec lines from src into dest, keep Exec line intact | |
| awk ' | |
| NR==FNR { | |
| if ($0 ~ /^Exec=/) { exec_line = $0; next } | |
| user[NR]=$0 | |
| next | |
| } | |
| { | |
| if ($0 ~ /^Exec=/) { print exec_line; next } | |
| } | |
| ' "${dest}" "${src}" >"${tmp}" | |
| mv "${tmp}" "${dest}" | |
| fi | |
| patch_exec_line "${dest}" | |
| rm -f "${tmp}.user" "${tmp}.sys" | |
| } | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| patch_exec_line() { | |
| local file="$1" | |
| # Extract current Exec line | |
| local current | |
| current="$(grep '^Exec=' "${file}" || true)" | |
| # Skip if already using wrapper | |
| if grep -q '^Exec=gtk-nocsd-wrapper' <<<"${current}"; then | |
| log "Exec already patched: $(basename "${file}")" | |
| return 0 | |
| fi | |
| local tmp | |
| tmp="$(mktemp)" | |
| awk ' | |
| /^Exec=/ { | |
| line=$0 | |
| # Preserve any env prefix; just insert wrapper at the start | |
| if (line ~ /^Exec=env /) { | |
| sub(/^Exec=env /, "Exec=gtk-nocsd-wrapper env ", line) | |
| } else { | |
| sub(/^Exec=/, "Exec=gtk-nocsd-wrapper ", line) | |
| } | |
| print line | |
| next | |
| } | |
| { print } | |
| ' "${file}" >"${tmp}" | |
| mv "${tmp}" "${file}" | |
| log "Patched Exec: $(basename "${file}")" | |
| } | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| process_packages() { | |
| mkdir -p "${DEST_DIR}" | |
| log "Scanning installed GTK packages…" | |
| local pkg | |
| for pkg in $(pacman -Qq); do | |
| if is_gtk_pkg "${pkg}"; then | |
| pacman -Ql "${pkg}" 2>/dev/null | awk '/\.desktop$/ {print $2}' | while read -r desktop; do | |
| [[ -f "${desktop}" ]] || continue | |
| local dest | |
| dest="${DEST_DIR}/$(basename "${desktop}")" | |
| merge_desktop_file "${desktop}" "${dest}" | |
| done | |
| fi | |
| done | |
| } | |
| # ──────────────────────────────────────────────────────────────────────────────── | |
| main() { | |
| create_wrapper | |
| process_packages | |
| log "Done. Patched .desktop files are in ${DEST_DIR}" | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment