x11drv: clipboard

Ulrich Czekalla ulrich.czekalla at utoronto.ca
Mon Jun 5 10:55:14 CDT 2006


This patch improves the way we handle grabbing the clipboard with a window
belonging to another thread or process.

This is really more of a work-around to avoid the issue. We now use a
clipboard specific X window to own the selection. To handle this properly
we need to shift more functionality into the wineserver and explorer
process.


ChangeLog:
    Ulrich Czekalla <ulrich at codeweavers.com>
    Improve handling of the case where the clipboard is opened with a
    window from another thread or process

---

 dlls/user/driver.c           |   16 ---
 dlls/user/user_private.h     |    3 -
 dlls/user/win.c              |    3 -
 dlls/x11drv/clipboard.c      |  200 +++++++++++++++---------------------------
 dlls/x11drv/event.c          |    3 -
 dlls/x11drv/winex11.drv.spec |    1 
 dlls/x11drv/x11drv.h         |    3 -
 dlls/x11drv/x11drv_main.c    |    1 
 8 files changed, 80 insertions(+), 150 deletions(-)

a74f56d0fbfd3f09cda63a425abac0b429a6bcdd
diff --git a/dlls/user/driver.c b/dlls/user/driver.c
index 278dd1d..1f87205 100644
--- a/dlls/user/driver.c
+++ b/dlls/user/driver.c
@@ -102,7 +102,6 @@ static const USER_DRIVER *load_driver(vo
         GET_USER_FUNC(RegisterClipboardFormat);
         GET_USER_FUNC(GetClipboardFormatName);
         GET_USER_FUNC(EndClipboardUpdate);
-        GET_USER_FUNC(ResetSelectionOwner);
         GET_USER_FUNC(ChangeDisplaySettingsEx);
         GET_USER_FUNC(EnumDisplaySettingsEx);
         GET_USER_FUNC(CreateDesktopWindow);
@@ -239,7 +238,7 @@ static void nulldrv_SetScreenSaveActive(
 {
 }
 
-static void nulldrv_AcquireClipboard( HWND hwnd )
+static INT nulldrv_AcquireClipboard( HWND hwnd )
 {
 }
 
@@ -281,10 +280,6 @@ static UINT nulldrv_RegisterClipboardFor
     return 0;
 }
 
-static void nulldrv_ResetSelectionOwner( HWND hwnd, BOOL flag )
-{
-}
-
 static BOOL nulldrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner )
 {
     return FALSE;
@@ -438,7 +433,6 @@ static const USER_DRIVER null_driver =
     nulldrv_GetClipboardFormatName,
     nulldrv_IsClipboardFormatAvailable,
     nulldrv_RegisterClipboardFormat,
-    nulldrv_ResetSelectionOwner,
     nulldrv_SetClipboardData,
     /* display modes */
     nulldrv_ChangeDisplaySettingsEx,
@@ -563,7 +557,7 @@ static void loaderdrv_SetScreenSaveActiv
     load_driver()->pSetScreenSaveActive( on );
 }
 
-static void loaderdrv_AcquireClipboard( HWND hwnd )
+static INT loaderdrv_AcquireClipboard( HWND hwnd )
 {
     load_driver()->pAcquireClipboard( hwnd );
 }
@@ -608,11 +602,6 @@ static UINT loaderdrv_RegisterClipboardF
     return load_driver()->pRegisterClipboardFormat( name );
 }
 
-static void loaderdrv_ResetSelectionOwner( HWND hwnd, BOOL flag )
-{
-    load_driver()->pResetSelectionOwner( hwnd, flag );
-}
-
 static BOOL loaderdrv_SetClipboardData( UINT format, HANDLE16 h16, HANDLE h32, BOOL owner )
 {
     return load_driver()->pSetClipboardData( format, h16, h32, owner );
@@ -754,7 +743,6 @@ static const USER_DRIVER lazy_load_drive
     loaderdrv_GetClipboardFormatName,
     loaderdrv_IsClipboardFormatAvailable,
     loaderdrv_RegisterClipboardFormat,
-    loaderdrv_ResetSelectionOwner,
     loaderdrv_SetClipboardData,
     /* display modes */
     loaderdrv_ChangeDisplaySettingsEx,
diff --git a/dlls/user/user_private.h b/dlls/user/user_private.h
index a58ba5e..ffb2796 100644
--- a/dlls/user/user_private.h
+++ b/dlls/user/user_private.h
@@ -122,7 +122,7 @@ typedef struct tagUSER_DRIVER {
     BOOL   (*pGetScreenSaveActive)(void);
     void   (*pSetScreenSaveActive)(BOOL);
     /* clipboard functions */
-    void   (*pAcquireClipboard)(HWND);                     /* Acquire selection */
+    INT    (*pAcquireClipboard)(HWND);                     /* Acquire selection */
     BOOL   (*pCountClipboardFormats)(void);                /* Count available clipboard formats */
     void   (*pEmptyClipboard)(BOOL);                       /* Empty clipboard data */
     void   (*pEndClipboardUpdate)(void);                   /* End clipboard update */
@@ -131,7 +131,6 @@ typedef struct tagUSER_DRIVER {
     INT    (*pGetClipboardFormatName)(UINT, LPWSTR, UINT); /* Get a clipboard format name */
     BOOL   (*pIsClipboardFormatAvailable)(UINT);           /* Check if specified format is available */
     UINT   (*pRegisterClipboardFormat)(LPCWSTR);           /* Register a clipboard format */
-    void   (*pResetSelectionOwner)(HWND, BOOL);
     BOOL   (*pSetClipboardData)(UINT, HANDLE16, HANDLE, BOOL);   /* Set specified selection data */
     /* display modes */
     LONG   (*pChangeDisplaySettingsEx)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID);
diff --git a/dlls/user/win.c b/dlls/user/win.c
index 08c5677..3d5fecb 100644
--- a/dlls/user/win.c
+++ b/dlls/user/win.c
@@ -1292,7 +1292,6 @@ static void WIN_SendDestroyMsg( HWND hwn
         if (hwnd == info.hwndCaret) DestroyCaret();
         if (hwnd == info.hwndActive) WINPOS_ActivateOtherWindow( hwnd );
     }
-    USER_Driver->pResetSelectionOwner( hwnd, TRUE );
 
     /*
      * Send the WM_DESTROY to the window.
@@ -1358,8 +1357,6 @@ BOOL WINAPI DestroyWindow( HWND hwnd )
 
     if (!IsWindow(hwnd)) return TRUE;
 
-    USER_Driver->pResetSelectionOwner( hwnd, FALSE ); /* before the window is unmapped */
-
       /* Hide the window */
     if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
     {
diff --git a/dlls/x11drv/clipboard.c b/dlls/x11drv/clipboard.c
index f773b3e..cd063ff 100644
--- a/dlls/x11drv/clipboard.c
+++ b/dlls/x11drv/clipboard.c
@@ -334,9 +334,14 @@ static Window thread_selection_wnd(void)
 
     if (!w)
     {
+        XSetWindowAttributes attr;
+
+        attr.event_mask = (ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
+                       ButtonPressMask | ButtonReleaseMask | EnterWindowMask);
+
         wine_tsx11_lock();
         w = XCreateWindow(thread_display(), root_window, 0, 0, 1, 1, 0, screen_depth,
-                          InputOutput, CopyFromParent, 0, NULL);
+                          InputOutput, CopyFromParent, CWEventMask, &attr);
         wine_tsx11_unlock();
 
         if (w)
@@ -2304,70 +2309,67 @@ INT X11DRV_GetClipboardFormatName(UINT w
 /**************************************************************************
  *		AcquireClipboard (X11DRV.@)
  */
-void X11DRV_AcquireClipboard(HWND hWndClipWindow)
+int X11DRV_AcquireClipboard(HWND hWndClipWindow)
 {
+    DWORD procid;
+    Window owner;
     Display *display = thread_display();
 
+    TRACE(" %p\n", hWndClipWindow);
+
     /*
-     * Acquire X selection if we don't already own it.
-     * Note that we only acquire the selection if it hasn't been already
-     * acquired by us, and ignore the fact that another X window may be
-     * asserting ownership. The reason for this is we need *any* top level
-     * X window to hold selection ownership. The actual clipboard data requests
-     * are made via GetClipboardData from EVENT_SelectionRequest and this
-     * ensures that the real HWND owner services the request.
-     * If the owning X window gets destroyed the selection ownership is
-     * re-cycled to another top level X window in X11DRV_CLIPBOARD_ResetOwner.
-     *
+     * It's important that the selection get acquired from the thread
+     * that owns the clipboard window. The primary reason is that we know 
+     * it is running a message loop and therefore can process the 
+     * X selection events.
      */
-    if (!(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)))
+    if (hWndClipWindow &&
+        GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, &procid))
     {
-        Window owner;
-
-        if (!hWndClipWindow)
-            hWndClipWindow = GetActiveWindow();
-
-        hWndClipWindow = GetAncestor(hWndClipWindow, GA_ROOT);
-
-        if (GetCurrentThreadId() != GetWindowThreadProcessId(hWndClipWindow, NULL))
+        if (procid != GetCurrentProcessId())
+        {
+            WARN("Setting clipboard owner to other process is not supported\n");
+            hWndClipWindow = NULL;
+        }
+        else
         {
             TRACE("Thread %lx is acquiring selection with thread %lx's window %p\n",
                 GetCurrentThreadId(),
-                GetWindowThreadProcessId(hWndClipWindow, NULL),
-                hWndClipWindow);
-            if (!SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0))
-                ERR("Failed to acquire selection\n");
-            return;
+                GetWindowThreadProcessId(hWndClipWindow, NULL), hWndClipWindow);
+
+            return SendMessageW(hWndClipWindow, WM_X11DRV_ACQUIRE_SELECTION, 0, 0);
         }
+    }
 
-        owner = X11DRV_get_whole_window(hWndClipWindow);
+    owner = thread_selection_wnd();
 
-        wine_tsx11_lock();
-        /* Grab PRIMARY selection if not owned */
-        if (use_primary_selection && !(selectionAcquired & S_PRIMARY))
-            XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
-
-        /* Grab CLIPBOARD selection if not owned */
-        if (!(selectionAcquired & S_CLIPBOARD))
-            XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), owner, CurrentTime);
+    wine_tsx11_lock();
 
-        if (use_primary_selection && XGetSelectionOwner(display,XA_PRIMARY) == owner)
-	    selectionAcquired |= S_PRIMARY;
+    selectionAcquired = 0;
+    selectionWindow = 0;
 
-        if (XGetSelectionOwner(display,x11drv_atom(CLIPBOARD)) == owner)
-	    selectionAcquired |= S_CLIPBOARD;
-        wine_tsx11_unlock();
+    /* Grab PRIMARY selection if not owned */
+    if (use_primary_selection)
+        XSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
 
-        if (selectionAcquired)
-        {
-	    selectionWindow = owner;
-	    TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
-        }
-    }
-    else
+    /* 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)
     {
-        ERR("Received request to acquire selection but process is already owner=(%08x)\n", (unsigned) selectionWindow);
+        selectionWindow = owner;
+        TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
     }
+
+    return 1;
 }
 
 
@@ -2612,94 +2614,40 @@ BOOL X11DRV_GetClipboardData(UINT wForma
 /**************************************************************************
  *		ResetSelectionOwner (X11DRV.@)
  *
- * Called from DestroyWindow() to prevent X selection from being lost when
- * a top level window is destroyed, by switching ownership to another top
- * level window.
- * Any top level window can own the selection. See X11DRV_CLIPBOARD_Acquire
- * for a more detailed description of this.
+ * Called when the thread owning the selection is destroyed and we need to
+ * preserve the selection ownership. We look for another top level window
+ * in this process and send it a message to acquire the selection.
  */
-void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar)
+void X11DRV_ResetSelectionOwner()
 {
-    Display *display = thread_display();
-    HWND hWndClipOwner = 0;
-    HWND tmp;
-    Window XWnd = X11DRV_get_whole_window(hwnd);
-    BOOL bLostSelection = FALSE;
-    Window selectionPrevWindow;
-
-    /* There is nothing to do if we don't own the selection,
-     * or if the X window which currently owns the selection is different
-     * from the one passed in.
-     */
-    if (!selectionAcquired || XWnd != selectionWindow
-         || selectionWindow == None )
-       return;
-
-    if ((bFooBar && XWnd) || (!bFooBar && !XWnd))
-       return;
-
-    hWndClipOwner = GetClipboardOwner();
-
-    TRACE("clipboard owner = %p, selection window = %08x\n",
-          hWndClipOwner, (unsigned)selectionWindow);
-
-    /* now try to salvage current selection from being destroyed by X */
-    TRACE("checking %08x\n", (unsigned) XWnd);
+    HWND hwnd;
+    DWORD procid;
 
-    selectionPrevWindow = selectionWindow;
-    selectionWindow = None;
+    TRACE("\n");
 
-    if (!(tmp = GetWindow(hwnd, GW_HWNDNEXT)))
-        tmp = GetWindow(hwnd, GW_HWNDFIRST);
+    if (!selectionAcquired  || thread_selection_wnd() != selectionWindow)
+        return;
 
-    if (tmp && tmp != hwnd) 
-        selectionWindow = X11DRV_get_whole_window(tmp);
+    selectionAcquired = S_NOSELECTION;
+    selectionWindow = 0;
 
-    if (selectionWindow != None)
+    hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
+    do
     {
-        /* We must pretend that we don't own the selection while making the switch
-         * since a SelectionClear event will be sent to the last owner.
-         * If there is no owner X11DRV_CLIPBOARD_ReleaseSelection will do nothing.
-         */
-        int saveSelectionState = selectionAcquired;
-        selectionAcquired = S_NOSELECTION;
-
-        TRACE("\tswitching selection from %08x to %08x\n",
-                    (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
-
-        wine_tsx11_lock();
-
-        /* Assume ownership for the PRIMARY and CLIPBOARD selection */
-        if (saveSelectionState & S_PRIMARY)
-            XSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
-
-        XSetSelectionOwner(display, x11drv_atom(CLIPBOARD), selectionWindow, CurrentTime);
-
-        /* Restore the selection masks */
-        selectionAcquired = saveSelectionState;
-
-        /* Lose the selection if something went wrong */
-        if (((saveSelectionState & S_PRIMARY) &&
-           (XGetSelectionOwner(display, XA_PRIMARY) != selectionWindow)) || 
-           (XGetSelectionOwner(display, x11drv_atom(CLIPBOARD)) != selectionWindow))
+        if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, &procid))
         {
-            bLostSelection = TRUE;
+            if (GetCurrentProcessId() == procid)
+            {
+                if (SendMessageW(hwnd, WM_X11DRV_ACQUIRE_SELECTION, 0, 0))
+                    return;
+            }
         }
-        wine_tsx11_unlock();
-    }
-    else
-    {
-        bLostSelection = TRUE;
-    }
+    } while ((hwnd = GetWindow(hwnd, GW_HWNDNEXT)) != NULL);
 
-    if (bLostSelection)
-    {
-        TRACE("Lost the selection!\n");
+    WARN("Failed to find another thread to take selection ownership. Clipboard data will be lost.\n");
 
-        X11DRV_CLIPBOARD_ReleaseOwnership();
-        selectionAcquired = S_NOSELECTION;
-        selectionWindow = 0;
-    }
+    X11DRV_CLIPBOARD_ReleaseOwnership();
+    X11DRV_EmptyClipboard(FALSE);
 }
 
 
@@ -3091,7 +3039,6 @@ END:
  */
 void X11DRV_SelectionRequest( HWND hWnd, XEvent *event )
 {
-    if (!hWnd) return;
     X11DRV_HandleSelectionRequest( hWnd, &event->xselectionrequest, FALSE );
 }
 
@@ -3102,7 +3049,6 @@ void X11DRV_SelectionRequest( HWND hWnd,
 void X11DRV_SelectionClear( HWND hWnd, XEvent *xev )
 {
     XSelectionClearEvent *event = &xev->xselectionclear;
-    if (!hWnd) return;
     if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD))
         X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd, event->time );
 }
diff --git a/dlls/x11drv/event.c b/dlls/x11drv/event.c
index b1df567..ae72f16 100644
--- a/dlls/x11drv/event.c
+++ b/dlls/x11drv/event.c
@@ -937,8 +937,7 @@ LRESULT X11DRV_WindowMessage( HWND hwnd,
     switch(msg)
     {
     case WM_X11DRV_ACQUIRE_SELECTION:
-        X11DRV_AcquireClipboard( hwnd );
-        return 0;
+        return X11DRV_AcquireClipboard( hwnd );
     case WM_X11DRV_DELETE_WINDOW:
         return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
     default:
diff --git a/dlls/x11drv/winex11.drv.spec b/dlls/x11drv/winex11.drv.spec
index 9c33ce4..687aabd 100644
--- a/dlls/x11drv/winex11.drv.spec
+++ b/dlls/x11drv/winex11.drv.spec
@@ -98,7 +98,6 @@
 @ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) X11DRV_MsgWaitForMultipleObjectsEx
 @ cdecl RegisterClipboardFormat(wstr) X11DRV_RegisterClipboardFormat
 @ cdecl ReleaseDC(long long long) X11DRV_ReleaseDC
-@ cdecl ResetSelectionOwner(long long) X11DRV_ResetSelectionOwner
 @ cdecl ScrollDC(long long long ptr ptr long ptr) X11DRV_ScrollDC
 @ cdecl SetClipboardData(long long long long) X11DRV_SetClipboardData
 @ cdecl SetFocus(long) X11DRV_SetFocus
diff --git a/dlls/x11drv/x11drv.h b/dlls/x11drv/x11drv.h
index f1a9e8d..ab55b58 100644
--- a/dlls/x11drv/x11drv.h
+++ b/dlls/x11drv/x11drv.h
@@ -662,7 +662,8 @@ extern void invalidate_dce( HWND hwnd, c
 extern XContext winContext;
 
 extern void X11DRV_InitClipboard(void);
-extern void X11DRV_AcquireClipboard(HWND hWndClipWindow);
+extern int X11DRV_AcquireClipboard(HWND hWndClipWindow);
+extern void X11DRV_ResetSelectionOwner();
 extern void X11DRV_SetFocus( HWND hwnd );
 extern Cursor X11DRV_GetCursor( Display *display, struct tagCURSORICONINFO *ptr );
 extern void X11DRV_InitKeyboard(void);
diff --git a/dlls/x11drv/x11drv_main.c b/dlls/x11drv/x11drv_main.c
index 7b0abce..b6fa275 100644
--- a/dlls/x11drv/x11drv_main.c
+++ b/dlls/x11drv/x11drv_main.c
@@ -457,6 +457,7 @@ static void thread_detach(void)
 
     if (data)
     {
+        X11DRV_ResetSelectionOwner();
         CloseHandle( data->display_fd );
         wine_tsx11_lock();
         XCloseDisplay( data->display );
-- 
1.2.4



More information about the wine-patches mailing list