Linuxqq 粘贴板同步问题

Last
更新
  • 该 Shell 脚本因改来改去 Bug 频出,因此近日笔者细细研究了一番这其中的古怪机制,详情见 这篇博文

前言

  • 不知从哪个版本开始,Linuxqq 在 Wayland 下的粘贴板出现了一个奇怪的问题:可以将 qq 中的文本复制到系统粘贴板(cliphist),但是无法将系统粘贴板上的内容粘贴进 qq 中。

  • 由于问题出现当时我正好将 Linuxqq 换成了 linuxqq-nt-bwarp,于是理所当然的认为这是因为 bwarp 沙盒化引起的,乍看之下也挺合理。

  • 直到今天在群里看到群主(应要求,不透露 id)发了一份用来同步 xclipcliphist 粘贴板内容的脚本,这才恍然大悟——沟槽的 tx,虽然 linuxqq 基于的 Electron 已经默认使用 Wayland 协议,但其复制仍然选择将内容输出到 xclip


查看

可以使用 xclip -sel clip -o 来查看 xlip 粘贴板中的内容。

脚本

  • 脚本代码如下:

    clipboard_sync.sh
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    #!/bin/bash

    # Lock file to prevent multiple instances
    LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp}"
    LOCK_FILE="$LOCK_DIR/clipboard_sync.lock"

    exec 200>"$LOCK_FILE"
    flock -n 200 || {
    echo "Error: Another instance of the script is already running." >&2
    exit 1
    }

    # Temp file path
    TEMP_IMG="$LOCK_DIR/clip_sync_buffer"

    # The interval in seconds for checking the clipboard
    INTERVAL=0.5

    # Color definitions for logging
    COLOR_RESET="\033[0m"
    COLOR_GREEN="\033[32m"
    COLOR_BLUE="\033[34m"
    COLOR_YELLOW="\033[33m"
    COLOR_CYAN="\033[36m"

    # General log function
    log() {
    local timestamp=$(date '+%H:%M:%S')
    echo -e "${COLOR_CYAN}[$timestamp]${COLOR_RESET} $1"
    }

    # Log function specific to sync events
    log_sync() {
    local direction=$1
    local type=$2
    local format=$3

    case "$direction" in
    "x11->wl")
    echo -e "${COLOR_CYAN}[$(date '+%H:%M:%S')]${COLOR_RESET} ${COLOR_GREEN}${COLOR_RESET} X11 → Wayland | ${COLOR_YELLOW}${type}${COLOR_RESET}${format}"
    ;;
    "wl->x11")
    echo -e "${COLOR_CYAN}[$(date '+%H:%M:%S')]${COLOR_RESET} ${COLOR_BLUE}${COLOR_RESET} Wayland → X11 | ${COLOR_YELLOW}${type}${COLOR_RESET}${format}"
    ;;
    esac
    }

    # Function to check for required command dependencies
    check_dependencies() {
    local missing_deps=()
    for cmd in xclip wl-paste wl-copy sha256sum; do
    if ! command -v "$cmd" &>/dev/null; then
    missing_deps+=("$cmd")
    fi
    done

    if [[ ${#missing_deps[@]} -gt 0 ]]; then
    echo "Error: Missing required dependencies: ${missing_deps[*]}" >&2
    exit 1
    fi
    }

    # Debounce variables
    last_text=""
    last_x11_img_hash="" # Hash of the last image synced from X11 to Wayland
    last_wl_img_hash="" # Hash of the last image synced from Wayland to X11

    # Main sync loop
    clipboard_sync() {
    while true; do
    img_synced=false # Flag to track if an image was synced in this iteration

    # -------- Image Sync: X11 → Wayland --------
    x11_targets=$(xclip -selection clipboard -t TARGETS -o 2>/dev/null || true)
    x11_img_type=""
    if echo "$x11_targets" | grep -q "image/png"; then
    x11_img_type="image/png"
    elif echo "$x11_targets" | grep -q "image/jpeg"; then
    x11_img_type="image/jpeg"
    elif echo "$x11_targets" | grep -q "image/gif"; then
    x11_img_type="image/gif"
    fi

    if [[ -n "$x11_img_type" ]]; then
    if xclip -selection clipboard -t "$x11_img_type" -o > "TEMP_IMG" 2>/dev/null; then

    # image output file exists
    if [[ -s "$TEMP_IMG" ]]; then
    x11_img_hash=$(xclip -selection clipboard -t "$x11_img_type" -o 2>/dev/null | sha256sum | awk '{print $1}')

    if [[ -n "$x11_img_hash" && "$x11_img_hash" != "$last_x11_img_hash" ]]; then

    # use temp file as buffer instead of using pipeline directly to avoid image cut off
    wl-copy -t "$x11_img_type" < "$TEMP_IMG"

    last_x11_img_hash="$x11_img_hash"
    last_wl_img_hash="$x11_img_hash"
    img_synced=true
    log_sync "x11->wl" "Image" " ($x11_img_type)"
    fi
    fi

    fi
    fi

    # -------- Image Sync: Wayland → X11 --------
    # Only check Wayland → X11 if no image sync has occurred in this iteration
    if [[ "$img_synced" == false ]]; then
    wl_types=$(wl-paste --list-types 2>/dev/null || true)
    wl_img_type=""
    if echo "$wl_types" | grep -q "image/png"; then
    wl_img_type="image/png"
    elif echo "$wl_types" | grep -q "image/jpeg"; then
    wl_img_type="image/jpeg"
    elif echo "$wl_types" | grep -q "image/gif"; then
    wl_img_type="image/gif"
    fi

    if [[ -n "$wl_img_type" ]]; then
    # Pipe binary data to calculate hash for debouncing
    # wl_img_hash=$(wl-paste -t "$wl_img_type" 2>/dev/null | tee >(sha256sum | awk '{print $1}' > /tmp/wl_hash_$$) | cat > /dev/null; cat /tmp/wl_hash_$$ 2>/dev/null; rm -f /tmp/wl_hash_$$ 2>/dev/null)

    if wl-paste -t "$wl_img_type" > "$TEMP_IMG" 2>/dev/null; then
    if [[ -s "$TEMP_IMG" ]]; then
    wl_img_hash=$(sha256sum "$TEMP_IMG" | awk '{print $1}')

    # If the Wayland image is different from the last synced one, sync it to X11
    if [[ -n "$wl_img_hash" && "$wl_img_hash" != "$last_wl_img_hash" ]]; then
    xclip -selection clipboard -t "wl_img_type" -i < "$TEMP_IMG"

    last_wl_img_hash="$wl_img_hash"
    last_x11_img_hash="$wl_img_hash"
    img_synced=true # Mark image as synced to skip text sync in this iteration
    log_sync "wl->x11" "Image" " ($wl_img_type)"
    fi
    fi
    fi

    fi
    fi

    # -------- Text Sync --------
    # Only perform text sync if no image was synced in this iteration
    if [[ "$img_synced" == false ]]; then
    text_synced=false # Flag to track if text was synced in this iteration

    x11_targets=$(xclip -selection clipboard -t TARGETS -o 2>/dev/null || true)

    # Only read from X11 clipboard if it likely contains text to avoid warnings from binary data
    if echo "x11_targets" | grep -qE "image/png|image/jpeg|image/bmp|image/tiff"; then
    x11_text=""
    else
    x11_text=$(xclip -selection clipboard -t UTF8_STRING -o 2>/dev/null || true)
    fi

    current_text=$(wl-paste --type text/plain 2>/dev/null || true)

    if [[ -n "$x11_text" && "$x11_text" != "$last_text" && "$x11_text" != "$current_text" ]]; then
    if [[ ! "$x11_text" =~ \x00 ]]; then
    # use `printf` for special characters
    printf "%s" "$x11_text" | wl-copy --type text/plain
    last_text="$x11_text"
    text_synced=true
    log_sync "x11->wl" "Text" " \"$preview\""
    fi
    fi

    # Only check Wayland → X11 if no text sync has occurred from the other direction
    if [[ "$text_synced" == false && -n "$current_text" && "$current_text" != "$last_text" && "$x11_text" != "$current_text" ]]; then
    # use `printf` for special characters
    printf "%s" "$current_text" | xclip -selection clipboard -t UTF8_STRING -i
    last_text="$current_text"
    log_sync "wl->x11" "Text" " \"$preview\""
    fi
    fi

    sleep $INTERVAL
    done
    }

    # Set up cleanup on exit (releases the lock file)
    trap 'exit' INT TERM EXIT

    # Check for dependencies
    check_dependencies

    log "Clipboard sync service started"
    log "Monitoring clipboard sync between Wayland ↔ X11..."

    log "Service started on lock: $LOCK_FILE"

    # Start the sync service
    clipboard_sync

  • 这个脚本的思路很简单:每间隔 $INTERVAL 的时间,就同步一次两个剪贴板中的内容。同时,利用哈希算法来确保图片的同步过程中不会出现无限循环。

  • 同时,笔者加入了使用 flock 的片段来确保不会出现该脚本在同一时间被运行多次的情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # Lock file to prevent multiple instances
    LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp}"
    LOCK_FILE="$LOCK_DIR/clipboard_sync.lock"

    exec 200>"$LOCK_FILE"
    flock -n 200 || {
    echo "Error: Another instance of the script is already running." >&2
    exit 1
    }

    # ...


    trap 'exit' INT TERM EXIT

后台运行

  • 笔者使用的是 Hyprland,因此只需在配置文件中加入:

    1
    2
    # Start clipboard sync service (for linuxqq)
    exec = ~/path/to/clipboard_sync.sh
  • 即可。

1

  • Title: Linuxqq 粘贴板同步问题
  • Author: Last
  • Created at : 2025-10-15 17:07:43
  • Link: https://blog.imlast.top/2025/10/15/linuxqq-clipboard-issue/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Linuxqq 粘贴板同步问题