From b92913cbf96be2973d83c89cb71ba33859109e78 Mon Sep 17 00:00:00 2001 From: Amy Findlay Date: Sat, 22 Nov 2025 19:57:55 +0100 Subject: [PATCH] Fixed bug with Clipboard on Wayland --- gui/utils/clipboard.py | 73 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/gui/utils/clipboard.py b/gui/utils/clipboard.py index 2ca2345e4..7aa1aaf15 100644 --- a/gui/utils/clipboard.py +++ b/gui/utils/clipboard.py @@ -1,22 +1,69 @@ # noinspection PyPackageRequirements import wx +from logbook import Logger + +logger = Logger(__name__) def toClipboard(text): - clip = wx.TheClipboard - clip.Open() - data = wx.TextDataObject(text) - clip.SetData(data) - clip.Close() + """ + Copy text to clipboard. Explicitly uses CLIPBOARD selection, not PRIMARY. + + On X11 systems, wxPython can confuse between PRIMARY and CLIPBOARD selections, + causing "already open" errors. This function ensures we always use CLIPBOARD. + + See: https://discuss.wxpython.org/t/wx-theclipboard-pasting-different-content-on-every-second-paste/35361 + """ + clipboard = wx.Clipboard() + try: + # Explicitly use CLIPBOARD selection, not PRIMARY selection + # This prevents X11 confusion between the two clipboard types + clipboard.UsePrimarySelection(False) + + if clipboard.Open(): + try: + data = wx.TextDataObject(text) + clipboard.SetData(data) + clipboard.Flush() # Ensure clipboard manager gets the data + return True + finally: + clipboard.Close() + else: + logger.debug("Failed to open clipboard for writing") + return False + except Exception as e: + logger.warning("Error writing to clipboard: {}", e) + return False def fromClipboard(): - clip = wx.TheClipboard - clip.Open() - data = wx.TextDataObject("") - if clip.GetData(data): - clip.Close() - return data.GetText() - else: - clip.Close() + """ + Read text from clipboard. Explicitly uses CLIPBOARD selection, not PRIMARY. + + On X11 systems, wxPython can confuse between PRIMARY and CLIPBOARD selections, + causing "already open" errors. This function ensures we always use CLIPBOARD. + + See: https://discuss.wxpython.org/t/wx-theclipboard-pasting-different-content-on-every-second-paste/35361 + """ + clipboard = wx.Clipboard() + try: + # Explicitly use CLIPBOARD selection, not PRIMARY selection + # This prevents X11 confusion between the two clipboard types + clipboard.UsePrimarySelection(False) + + if clipboard.Open(): + try: + data = wx.TextDataObject() + if clipboard.GetData(data): + return data.GetText() + else: + logger.debug("Clipboard open but no CLIPBOARD data available") + return None + finally: + clipboard.Close() + else: + logger.debug("Failed to open clipboard for reading") + return None + except Exception as e: + logger.warning("Error reading from clipboard: {}", e) return None