winex11.drv: Move clipboard handling to a separate thread

Yuri Khan yurivkhan at gmail.com
Fri Jun 26 00:04:12 CDT 2009


Resending due to no reaction due to no description/explanation on my part.

== Background ==

In Windows, an application can place data on the clipboard and that
data is immediately available to anyone willing to paste, with no
additional support from the application. (Windows also supports
delayed rendering of clipboard data, but this is not directly
relevant.)

In X, a window must register itself as a clipboard owner and respond
to SelectionRequest messages. It should also process SelectionClear
messages to know when another window has become the clipboard owner.

In current wine, when a win32 application acquires clipboard
ownership, an auxiliary window is created and registered as the
clipboard owner. The internal function named process_events in
winex11.drv then handles SelectionRequest and SelectionClear messages.
process_events gets called from within all user32.dll functions that
deal with window messages.

== The problem ==

If the win32 application in question does not call any functions that
cause process_events to be called, the X selection messages never get
delivered to the clipboard owner auxiliary window. The applications
requesting data to paste then do not receive any response. Copy from
win32 -- paste to X is broken.

Also, the win32 application never gets notified that it no longer owns
the clipboard. As a result, it thinks that the clipboard has the same
content it copied. Copy X -- paste win32 is broken too.

A win32 application might not process window messages for a number of reasons:
 * It might be busy processing something on its UI thread. Not very
nice but happens sometimes.
 * It might be a console application driven by a ReadConsoleInput loop.
 * It might even be a console application driven by an stdio input loop.

The message loop in wineconsole is not relevant here, because it runs
in a process different from that to which the clipboard auxiliary
window belongs.

Bug #15122 is an instance of this problem. The affected application,
FAR Manager, is a file manager widely popular among Windows users in
the former USSR.

== Proposed solution ==

It is clear that wine cannot rely on the application calling any
message-related functions, or any Win32 API functions at all. Wine
somehow has to make clipboard handling happen independently.

This patch achieves this by creating an auxiliary thread that will run
in the same process that puts data on the clipboard and handle X
selection messages.

== Revision history ==

A preliminary version of this patch was reviewed by Dmitry Timoshkov
(comments #29-#30 to bug #15122). I have subsequently fixed a handle
leak and applied Dmitry's suggestions.

The patch is rebased to current git as of now, 2009-06-26T12:00+0700.
-------------- next part --------------
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index 3806080..b5ef3f2 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -2504,6 +2504,48 @@ INT CDECL X11DRV_GetClipboardFormatName(UINT wFormat, LPWSTR retStr, INT maxlen)
     return strlenW(retStr);
 }
 
+DWORD WINAPI selection_thread_proc(LPVOID lphwnd)
+{
+    Window owner;
+    Display *display;
+    HANDLE handles[1];
+
+    owner = thread_selection_wnd();
+    display = thread_display();
+
+    wine_tsx11_lock();
+
+    selectionAcquired = 0;
+    selectionWindow = 0;
+
+    /* Grab PRIMARY selection if not owned */
+    if (use_primary_selection)
+        XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
+
+    /* Grab CLIPBOARD selection if not owned */
+    XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
+
+    if (use_primary_selection && XGetSelectionOwner(display, XA_PRIMARY) == owner)
+        selectionAcquired |= S_PRIMARY;
+
+    if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
+        selectionAcquired |= S_CLIPBOARD;
+
+    wine_tsx11_unlock();
+
+    if (selectionAcquired)
+    {
+        selectionWindow = owner;
+        TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
+    }
+
+    while (selectionAcquired)
+    {
+        MsgWaitForMultipleObjectsEx(0, handles, INFINITE, 0, 0);
+    }
+
+    return 0;
+}
 
 /**************************************************************************
  *		AcquireClipboard (X11DRV.@)
@@ -2511,8 +2553,7 @@ INT CDECL X11DRV_GetClipboardFormatName(UINT wFormat, LPWSTR retStr, INT maxlen)
 int CDECL X11DRV_AcquireClipboard(HWND hWndClipWindow)
 {
     DWORD procid;
-    Window owner;
-    Display *display;
+    HANDLE selectionThread;
 
     TRACE(" %p\n", hWndClipWindow);
 
@@ -2540,35 +2581,16 @@ int CDECL X11DRV_AcquireClipboard(HWND hWndClipWindow)
         }
     }
 
-    owner = thread_selection_wnd();
-    display = thread_display();
-
-    wine_tsx11_lock();
-
-    selectionAcquired = 0;
-    selectionWindow = 0;
-
-    /* Grab PRIMARY selection if not owned */
-    if (use_primary_selection)
-        XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
+    selectionThread = CreateThread(NULL, 0, &selection_thread_proc, &hWndClipWindow, 0, NULL);
 
-    /* Grab CLIPBOARD selection if not owned */
-    XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
-
-    if (use_primary_selection && XGetSelectionOwner(display, XA_PRIMARY) == owner)
-        selectionAcquired |= S_PRIMARY;
-
-    if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
-        selectionAcquired |= S_CLIPBOARD;
-
-    wine_tsx11_unlock();
-
-    if (selectionAcquired)
+    if (!selectionThread)
     {
-        selectionWindow = owner;
-        TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
+        WARN("Could not start clipboard thread\n");
+        return 0;
     }
 
+    CloseHandle(selectionThread);
+
     return 1;
 }
 
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index c746546..8933671 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -239,6 +239,9 @@ static Bool filter_event( Display *display, XEvent *event, char *arg )
     case PropertyNotify:
     case ClientMessage:
         return (mask & QS_POSTMESSAGE) != 0;
+    case SelectionClear:
+    case SelectionRequest:
+        return 1;
     default:
         return (mask & QS_SENDMESSAGE) != 0;
     }


More information about the wine-patches mailing list