From b41466b3b14ef6d3bbf8b634ccf061935846135c Mon Sep 17 00:00:00 2001
From: Ulrich Czekalla <ulrich@codeweavers.com>
Date: Thu, 6 May 2004 23:40:30 +0000
Subject: [PATCH] Allow applications, under certain conditions, to set data
 into the clipboard even if they are not the clipboard owners.

---
 dlls/x11drv/clipboard.c | 57 +++++++++++++++++++++++++++++++----------
 dlls/x11drv/x11drv.h    |  1 +
 include/user.h          |  4 +--
 windows/clipboard.c     | 41 ++++++++++++++++++++---------
 4 files changed, 75 insertions(+), 28 deletions(-)

diff --git a/dlls/x11drv/clipboard.c b/dlls/x11drv/clipboard.c
index e8afaf29057..5dbb9e10e9c 100644
--- a/dlls/x11drv/clipboard.c
+++ b/dlls/x11drv/clipboard.c
@@ -117,7 +117,7 @@ static Window PrimarySelectionOwner = None;    /* The window which owns the prim
 static Window ClipboardSelectionOwner = None;  /* The window which owns the clipboard selection */
 
 INT X11DRV_RegisterClipboardFormat(LPCSTR FormatName);
-void X11DRV_EmptyClipboard(void);
+void X11DRV_EmptyClipboard(BOOL keepunowned);
 void X11DRV_EndClipboardUpdate(void);
 HANDLE X11DRV_CLIPBOARD_ImportClipboardData(LPBYTE lpdata, UINT cBytes);
 HANDLE X11DRV_CLIPBOARD_ImportEnhMetaFile(LPBYTE lpdata, UINT cBytes);
@@ -719,7 +719,7 @@ static BOOL X11DRV_CLIPBOARD_UpdateCache(LPCLIPBOARDINFO lpcbinfo)
         }
         else if (wSeqNo < lpcbinfo->seqno)
         {
-            X11DRV_EmptyClipboard();
+            X11DRV_EmptyClipboard(TRUE);
 
             if (X11DRV_CLIPBOARD_QueryAvailableData(lpcbinfo) < 0)
             {
@@ -1985,7 +1985,7 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
                 selectionWindow = None;
                 PrimarySelectionOwner = ClipboardSelectionOwner = 0;
 
-                X11DRV_EmptyClipboard();
+                X11DRV_EmptyClipboard(FALSE);
 
                 /* Reset the selection flags now that we are done */
                 selectionAcquired = S_NOSELECTION;
@@ -2135,29 +2135,41 @@ void X11DRV_AcquireClipboard(HWND hWndClipWindow)
 
 /**************************************************************************
  *	X11DRV_EmptyClipboard
+ *
+ * Empty cached clipboard data. 
  */
-void X11DRV_EmptyClipboard(void)
+void X11DRV_EmptyClipboard(BOOL keepunowned)
 {
     if (ClipData)
     {
-        LPWINE_CLIPDATA lpData;
+        LPWINE_CLIPDATA lpData, lpStart;
         LPWINE_CLIPDATA lpNext = ClipData;
 
+        TRACE(" called with %d entries in cache.\n", ClipDataCount);
+
         do
         {
+            lpStart = ClipData;
             lpData = lpNext;
             lpNext = lpData->NextData;
+
+            if (!keepunowned || !(lpData->wFlags & CF_FLAG_UNOWNED))
+            {
             lpData->PrevData->NextData = lpData->NextData;
             lpData->NextData->PrevData = lpData->PrevData;
+
+                if (lpData == ClipData)
+                    ClipData = lpNext != lpData ? lpNext : NULL;
+
             X11DRV_CLIPBOARD_FreeData(lpData);
             HeapFree(GetProcessHeap(), 0, lpData);
-        } while (lpNext != lpData);
-    }
 
-    TRACE(" %d entries deleted from cache.\n", ClipDataCount);
+                ClipDataCount--;
+            }
+        } while (lpNext != lpStart);
+    }
 
-    ClipData = NULL;
-    ClipDataCount = 0;
+    TRACE(" %d entries remaining in cache.\n", ClipDataCount);
 }
 
 
@@ -2165,12 +2177,29 @@ void X11DRV_EmptyClipboard(void)
 /**************************************************************************
  *		X11DRV_SetClipboardData
  */
-BOOL X11DRV_SetClipboardData(UINT wFormat, HANDLE16 hData16, HANDLE hData32)
+BOOL X11DRV_SetClipboardData(UINT wFormat, HANDLE16 hData16, HANDLE hData32, BOOL owner)
+{
+    DWORD flags = 0;
+    BOOL bResult = TRUE;
+
+    /* If it's not owned, data can only be set if the format data is not already owned
+       and its rendering is not delayed */
+    if (!owner)
 {
-    BOOL bResult = FALSE;
+        CLIPBOARDINFO cbinfo;
+        LPWINE_CLIPDATA lpRender;
+
+        X11DRV_CLIPBOARD_UpdateCache(&cbinfo);
+
+        if ((!hData16 && !hData32) ||
+            ((lpRender = X11DRV_CLIPBOARD_LookupData(wFormat)) &&
+            !(lpRender->wFlags & CF_FLAG_UNOWNED)))
+            bResult = FALSE;
+        else
+            flags = CF_FLAG_UNOWNED;
+    }
 
-    if (X11DRV_CLIPBOARD_InsertClipboardData(wFormat, hData16, hData32, 0))
-        bResult = TRUE;
+    bResult &= X11DRV_CLIPBOARD_InsertClipboardData(wFormat, hData16, hData32, flags);
 
     return bResult;
 }
diff --git a/dlls/x11drv/x11drv.h b/dlls/x11drv/x11drv.h
index fcc26c38eca..098c8c6488a 100644
--- a/dlls/x11drv/x11drv.h
+++ b/dlls/x11drv/x11drv.h
@@ -474,6 +474,7 @@ typedef struct tagWINE_CLIPFORMAT {
 } WINE_CLIPFORMAT, *LPWINE_CLIPFORMAT;
 
 #define CF_FLAG_BUILTINFMT   1 /* Built-in windows format */
+#define CF_FLAG_UNOWNED      2 /* cached data is not owned */
 #define CF_FLAG_SYNTHESIZED  8 /* Implicitly converted data */
 
 extern void X11DRV_InitClipboard(void);
diff --git a/include/user.h b/include/user.h
index cf48024ba7c..5f2a4202201 100644
--- a/include/user.h
+++ b/include/user.h
@@ -94,7 +94,7 @@ typedef struct tagUSER_DRIVER {
     /* clipboard functions */
     void   (*pAcquireClipboard)(HWND);                     /* Acquire selection */
     BOOL   (*pCountClipboardFormats)(void);                /* Count available clipboard formats */
-    void   (*pEmptyClipboard)(void);                       /* Empty clipboard data */
+    void   (*pEmptyClipboard)(BOOL);                       /* Empty clipboard data */
     BOOL   (*pEndClipboardUpdate)(void);                   /* End clipboard update */
     BOOL   (*pEnumClipboardFormats)(UINT);                 /* Enumerate clipboard formats */
     BOOL   (*pGetClipboardData)(UINT, HANDLE16*, HANDLE*); /* Get specified selection data */
@@ -102,7 +102,7 @@ typedef struct tagUSER_DRIVER {
     BOOL   (*pIsClipboardFormatAvailable)(UINT);           /* Check if specified format is available */
     INT    (*pRegisterClipboardFormat)(LPCSTR);            /* Register a clipboard format */
     void   (*pResetSelectionOwner)(HWND, BOOL);
-    BOOL   (*pSetClipboardData)(UINT, HANDLE16, HANDLE);   /* Set specified selection data */
+    BOOL   (*pSetClipboardData)(UINT, HANDLE16, HANDLE, BOOL);   /* Set specified selection data */
     /* display modes */
     LONG   (*pChangeDisplaySettingsExW)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID);
     BOOL   (*pEnumDisplaySettingsExW)(LPCWSTR,DWORD,LPDEVMODEW,DWORD);
diff --git a/windows/clipboard.c b/windows/clipboard.c
index fda151e1b31..85117cf050a 100644
--- a/windows/clipboard.c
+++ b/windows/clipboard.c
@@ -427,7 +427,7 @@ BOOL WINAPI EmptyClipboard(void)
 
     /* Empty the local cache */
     if (USER_Driver.pEmptyClipboard) 
-        USER_Driver.pEmptyClipboard();
+        USER_Driver.pEmptyClipboard(FALSE);
  
     bCBHasChanged = TRUE;
 
@@ -557,14 +557,22 @@ HANDLE16 WINAPI SetClipboardData16(UINT16 wFormat, HANDLE16 hData)
 
     TRACE("(%04X, %04x) !\n", wFormat, hData);
 
-    if (!CLIPBOARD_GetClipboardInfo(&cbinfo) ||
-        (~cbinfo.flags & CB_OPEN) ||
-        (~cbinfo.flags & CB_OWNER))
+    if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || !(cbinfo.flags & CB_OPEN))
     {
-        WARN("Clipboard not opened by calling task!\n");
+        WARN("Clipboard not opened by calling task. Operation failed.\n");
+        return 0;
+    }
+
+    /* If it's not owned, data can only be set if the format doesn't exists
+       and its rendering is not delayed */
+    if (!(cbinfo.flags & CB_OWNER) && !hData)
+    {
+        WARN("Clipboard not owned by calling task. Operation failed.\n");
+        return 0;
     }
-    else if (USER_Driver.pSetClipboardData &&
-        USER_Driver.pSetClipboardData(wFormat, hData, 0))
+
+    if (USER_Driver.pSetClipboardData &&
+        USER_Driver.pSetClipboardData(wFormat, hData, 0, cbinfo.flags & CB_OWNER))
     {
         hResult = hData;
         bCBHasChanged = TRUE;
@@ -584,13 +592,22 @@ HANDLE WINAPI SetClipboardData(UINT wFormat, HANDLE hData)
 
     TRACE("(%04X, %p) !\n", wFormat, hData);
 
-    if (!CLIPBOARD_GetClipboardInfo(&cbinfo) ||
-        (~cbinfo.flags & CB_OWNER))
+    if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || !(cbinfo.flags & CB_OPEN))
     {
-        WARN("Clipboard not owned by calling task!\n");
+        WARN("Clipboard not opened by calling task. Operation failed.\n");
+        return 0;
     }
-    else if (USER_Driver.pSetClipboardData &&
-        USER_Driver.pSetClipboardData(wFormat, 0, hData))
+
+    /* If it's not owned, data can only be set if the format isn't
+       available and its rendering is not delayed */
+    if (!(cbinfo.flags & CB_OWNER) && !hData)
+    {
+        WARN("Clipboard not owned by calling task. Operation failed.\n");
+        return 0;
+    }
+
+    if (USER_Driver.pSetClipboardData &&
+        USER_Driver.pSetClipboardData(wFormat, 0, hData, cbinfo.flags & CB_OWNER))
     {
         hResult = hData;
         bCBHasChanged = TRUE;
-- 
2.24.1