Skip to content

Instantly share code, notes, and snippets.

@LCamel
Last active February 9, 2026 16:32
Show Gist options
  • Select an option

  • Save LCamel/c073f13bd7994afe8e646c2592570b24 to your computer and use it in GitHub Desktop.

Select an option

Save LCamel/c073f13bd7994afe8e646c2592570b24 to your computer and use it in GitHub Desktop.
xpra
cp -rL /usr/share/xpra $HOME/xpra_copy
sed -i 's/getboolparam("swap_keys", Utilities\.isMacOS())/getboolparam("swap_keys", false)/' \
"$HOME/xpra_copy/www/index.html"
xpra start --bind-tcp=0.0.0.0:8080 --html=$HOME/xpra_copy/www --start=emacs-lucid
#!/bin/bash
set -euo pipefail
WWW_SRC="/usr/share/xpra/www"
XPRA_OVR="$HOME/.local/share/xpra"
WWW_DST="$XPRA_OVR/www"
# Prepare overlay: symlink everything, copy only the files we patch
rm -rf "$XPRA_OVR"
mkdir -p "$WWW_DST/js"
# Top-level: symlink all except files we need to patch
for item in "$WWW_SRC"/*; do
name=$(basename "$item")
case "$name" in
index.html|connect.html|js) ;; # handle separately
*) ln -s "$item" "$WWW_DST/$name" ;;
esac
done
# js/: symlink all except Client.js (and its compressed variants)
for item in "$WWW_SRC"/js/*; do
name=$(basename "$item")
case "$name" in
Client.js|Client.js.*) ;; # skip compressed variants too
*) ln -s "$item" "$WWW_DST/js/$name" ;;
esac
done
# Copy the 3 files we need to patch
cp "$WWW_SRC/index.html" "$WWW_DST/index.html"
cp "$WWW_SRC/connect.html" "$WWW_DST/connect.html"
cp "$WWW_SRC/js/Client.js" "$WWW_DST/js/Client.js"
# Patch swap_keys defaults to false (even on Mac)
sed -i 's/getboolparam("swap_keys", Utilities\.isMacOS())/getboolparam("swap_keys", false)/' \
"$WWW_DST/index.html"
sed -i '/Utilities\.isMacOS()/{N;/swap_keys/{N;d;}}' \
"$WWW_DST/connect.html"
sed -i 's/this\.swap_keys = Utilities\.isMacOS()/this.swap_keys = false/' \
"$WWW_DST/js/Client.js"
# http-headers is resolved as www_dir/../http-headers (server_core.py:599)
ln -s /usr/share/xpra/http-headers "$XPRA_OVR/http-headers"
# Use XPRA_RESOURCES_DIR so --html=on finds our overlay first
export XPRA_RESOURCES_DIR="$XPRA_OVR"
xpra start \
--bind-tcp=0.0.0.0:8080 \
--html=on \
--start=emacs-lucid
❯ 幫我分析一下 為什麼 xpra.sh 本來用簡單的 copy 不行, 但是現在用 copy + symlink 就可以?
● Read 1 file (ctrl+o to expand)
● 這是一個很好的問題。讓我分析腳本的結構來解釋。
核心問題:預壓縮檔案 (pre-compressed variants)
xpra 的 web server 會針對每個檔案提供預壓縮版本(.gz, .br)。當瀏覽器發送 Accept-Encoding: gzip, br 時,server 優先送出壓縮版。
如果「簡單 copy」是指 cp -r 整個目錄再 sed
cp -r /usr/share/xpra/www → overlay/www/
sed -i ... overlay/www/index.html
sed -i ... overlay/www/js/Client.js
問題在於:
- sed 只改了 Client.js(未壓縮版)
- Client.js.gz 和 Client.js.br 仍然是原版內容
- 瀏覽器支援 gzip → server 送出 Client.js.gz → 你的 patch 完全沒生效
為什麼 symlink + copy 能解決
關鍵在第 24-28 行:
case "$name" in
Client.js|Client.js.*) ;; # ← 刻意跳過壓縮版!
*) ln -s "$item" "$WWW_DST/js/$name" ;;
esac
這個做法的效果是:
┌─────────────────┬───────────────────────┬─────────────────────┐
│ 檔案 │ overlay 裡的狀態 │ 結果 │
├─────────────────┼───────────────────────┼─────────────────────┤
│ Client.js │ 實體複製 + sed 修改過 │ 有 patch │
├─────────────────┼───────────────────────┼─────────────────────┤
│ Client.js.gz │ 不存在 │ server 找不到壓縮版 │
├─────────────────┼───────────────────────┼─────────────────────┤
│ Client.js.br │ 不存在 │ server 找不到壓縮版 │
├─────────────────┼───────────────────────┼─────────────────────┤
│ Utilities.js │ symlink → 原版 │ 原樣提供 │
├─────────────────┼───────────────────────┼─────────────────────┤
│ Utilities.js.gz │ symlink → 原版 │ 原樣提供 │
└─────────────────┴───────────────────────┴─────────────────────┘
因為壓縮版 不存在於 overlay 目錄,xpra 的 web server 被迫 fallback 到未壓縮的 Client.js — 也就是你改過的版本。
第二個關鍵:http-headers
第 43-44 行也很重要:
# http-headers is resolved as www_dir/../http-headers (server_core.py:599)
ln -s /usr/share/xpra/http-headers "$XPRA_OVR/http-headers"
xpra 的 server_core.py 會去 www/ 的上層目錄找 http-headers。如果只是單純 copy www/ 到自訂路徑,上層不會有 http-headers,可能導致
server 啟動失敗或回應缺少必要的 HTTP headers。
總結
「簡單 copy」失敗的根本原因是 壓縮版檔案覆蓋了你的 patch。Symlink + copy 的策略是:
1. Symlink — 大部分不需改的檔案直接 symlink,零成本引用原版(含壓縮版)
2. Copy — 只複製要改的 3 個檔案
3. 排除壓縮版 — 刻意不 symlink 被 patch 檔案的 .gz/.br,強迫 server 送出未壓縮的修改版
4. 補上 http-headers — symlink 確保 server 能找到必要的 HTTP header 設定
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment