http://bugs.winehq.org/show_bug.cgi?id=59647 Bug ID: 59647 Summary: Wayland clipboard: external clipboard changes are lost after 0.5–2 seconds due to delayed rendering invalidation Product: Wine Version: 11.6 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: major Priority: P2 Component: winewayland Assignee: wine-bugs@list.winehq.org Reporter: lldmakhbfcrmqyluns@nesopf.com Distribution: --- When a child process launched from a Wine application sets the system clipboard (via qdbus/Klipper, wl-copy, or xclip through XWayland), the clipboard content is cleared by Wine within 0.5–2 seconds. The first invocation usually works; subsequent ones fail progressively faster. How to reproduce Setup Total Commander running under Wine in a Wayland session. A custom button executes a command that converts a Windows path to a Linux path and copies it to the system clipboard: cmd Z:\bin\bash param -c "p=$(winepath -u \"$1\" | tr -d '\n'); qdbus6 org.kde.klipper /klipper setClipboardContents \"$p\"" -- "%P%N" This is a minimal reproducer — any external process setting the Wayland clipboard while a Wine app is focused triggers the same issue. Steps: Launch Total Commander under Wine in a Wayland session Select a file, invoke the command Paste into a native Linux app — works (1st call) Select another file, invoke the command again Paste — clipboard is empty Expected result: Clipboard should contain the Linux path after every invocation. Actual result: Clipboard is cleared by Wine within 0.5–2 seconds after the 2nd+ invocation. Diagnostic data Test script bash #!/bin/bash # ~/bin/tc-clip-debug.sh — called from TC via: # param=-c "$HOME/bin/tc-clip-debug.sh \"$1\"" -- "%P%N" exec >> /tmp/tc_clip.log 2>&1 echo "=== $(date) ===" path=$(winepath -u "$1" | tr -d '\n') echo "PATH: $path" qdbus6 org.kde.klipper /klipper setClipboardContents "$path" echo "SET exit: $?" for delay in 0.0 0.2 0.5 1.0 2.0; do [ "$delay" != "0.0" ] && sleep "$delay" got=$(qdbus6 org.kde.klipper /klipper getClipboardContents) echo "GET +${delay}s: [$got] match=$([ "$got" = "$path" ] && echo YES || echo NO)" done Test results (4 consecutive invocations) === Call 1 === SET exit: 0 GET +0.0s: [/home/tm/.wine/drive_c/1.txt] match=YES GET +0.2s: match=YES GET +0.5s: match=YES GET +1.0s: match=YES GET +2.0s: match=YES ← all OK === Call 2 === SET exit: 0 GET +0.0s: [/home/tm/.wine/drive_c/TCM/2.txt] match=YES GET +0.2s: match=YES GET +0.5s: match=YES GET +1.0s: match=YES GET +2.0s: [] match=NO ← cleared between 1.0–2.0s === Call 3 === SET exit: 0 GET +0.0s: [/home/tm/.wine/drive_c/3.txt] match=YES GET +0.2s: match=YES GET +0.5s: [] match=NO ← cleared between 0.2–0.5s === Call 4 === SET exit: 0 GET +0.0s: [/home/tm/.wine/drive_c/4.txt] match=YES GET +0.2s: match=YES GET +0.5s: match=YES GET +1.0s: [] match=NO ← cleared between 0.5–1.0s All qdbus6 calls return exit code 0 — Klipper accepts the data, but Wine overwrites it shortly after. Wine debug trace (WINEDEBUG=+clipboard,+waylanddrv) Each invocation of the command produces this sequence: 0158:trace:clipboard:handle_selection updating clipboard from wayland offer 0158:trace:clipboard:NtUserOpenClipboard 0x30052 0158:trace:clipboard:NtUserEmptyClipboard owner 0x30052 015c:trace:clipboard:destroy_clipboard 0158:trace:clipboard:handle_selection available clipboard format for text/plain;charset=utf-8 => 13 0158:trace:clipboard:NtUserSetClipboardData 000d L"#13" (nil) 0158:trace:clipboard:NtUserCloseClipboard Note: NtUserSetClipboardData ... (nil) — data is registered with delayed rendering, not fetched immediately. Analysis: The relevant code is in dlls/winewayland.drv/wayland_data_device.c, function handle_selection() (around line 510). When an external process sets the Wayland clipboard: Wine receives a data_offer from the compositor handle_selection() opens the Windows clipboard, calls EmptyClipboard(), registers available formats with delayed rendering (SetClipboardData with nil data), and stores the data_offer for later use by render_format() A subsequent event (Klipper re-offering after updating its history, window creation/destruction from the child process, etc.) triggers clipboard_update() via WM_CLIPBOARDUPDATE clipboard_update() creates a new wl_data_source and calls wl_data_device_set_selection(), claiming Wayland clipboard ownership for Wine This cancels the original source (Klipper) When the clipboard content is later requested, render_format() tries to read from the stored data_offer, but the original source has been cancelled — resulting in empty clipboard The first invocation works because the system is in a clean state and clipboard_update() is correctly skipped (clipboard owner == clipboard_hwnd). On subsequent invocations, accumulated state from previous clipboard exchanges creates conditions for clipboard_update() to fire, leading to the circular dependency described above. Workaround Setting the clipboard, waiting for Wine to clear it (~2 seconds), and setting it again works reliably — Wine only clears once per invocation: cmd Z:\bin\bash param -c "p=$(winepath -u \"$1\" | tr -d '\n'); if [ -n \"$WAYLAND_DISPLAY\" ]; then qdbus6 org.kde.klipper /klipper setClipboardContents \"$p\"; sleep 2; qdbus6 org.kde.klipper /klipper setClipboardContents \"$p\"; else echo -n \"$p\" | xclip -selection clipboard; fi" -- "%P%N" -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.