- Implement interprocess clipboard communication.
- Support for the PRIMARY and CLIPBOARD selection atoms.
- Support for the TARGETS selection format.
- Expose native Windows clipboard formats through X selection targets.

diff --git a/windows/x11drv/clipboard.c b/windows/x11drv/clipboard.c
index 5ed4d24..6636a4e 100644
--- a/windows/x11drv/clipboard.c
+++ b/windows/x11drv/clipboard.c
@@ -1,8 +1,50 @@
 /*
- * X11 windows driver
+ * X11 clipboard windows driver
  *
  * Copyright 1994 Martin Ayotte
  *	     1996 Alex Korobka
+ *	     1999 Noel Borthwick
+ *
+ * NOTES:
+ *    This file contains the X specific implementation for the windows
+ *    Clipboard API.
+ *
+ *    Wine's internal clipboard is exposed to external apps via the X
+ *    selection mechanism.
+ *    Currently the driver asserts ownership via two selection atoms:
+ *    1. PRIMARY(XA_PRIMARY)
+ *    2. CLIPBOARD
+ *
+ *    In our implementation, the CLIPBOARD selection takes precedence over PRIMARY.
+ *    i.e. if a CLIPBOARD selection is available, it is used instead of PRIMARY.
+ *    When Wine taks ownership of the clipboard, it takes ownership of BOTH selections.
+ *    While giving up selection ownership, if the CLIPBOARD selection is lost,
+ *    it will lose both PRIMARY and CLIPBOARD and empty the clipboard.
+ *    However if only PRIMARY is lost, it will continue to hold the CLIPBOARD selection
+ *    (leaving the clipboard cache content unaffected).
+ *
+ *      Every format exposed via a windows clipboard format is also exposed through
+ *    a corresponding X selection target. A selection target atom is synthesized
+ *    whenever a new Windows clipboard format is registered via RegisterClipboardFormat,
+ *    or when a built in format is used for the first time.
+ *    Windows native format are exposed by prefixing the format name with "<WCF>"
+ *    This allows us to uniquely identify windows native formats exposed by other
+ *    running WINE apps.
+ *
+ *      In order to allow external applications to query WINE for supported formats,
+ *    we respond to the "TARGETS" selection target. (See EVENT_SelectionRequest
+ *    for implementation) We use the same mechanism to query external clients for
+ *    availability of a particular format, by cacheing the list of available targets
+ *    by using the clipboard cache's "delayed render" mechanism. If a selection client
+ *    does not support the "TARGETS" selection target, we actually attempt to retrieve
+ *    the format requested as a fallback mechanism.
+ *
+ *      Certain Windows native formats are automatically converted to X native formats
+ *    and vice versa. If a native format is available in the selection, it takes
+ *    precedence, in order to avoid unnecessary conversions.
+ *
+ * TODO:
+ *    - Support for converting between DIB and PIXMAP formats
  */
 
 #include "config.h"
@@ -10,6 +52,7 @@
 #ifndef X_DISPLAY_MISSING
 
 #include <X11/Xatom.h>
+#include <string.h>
 #include "ts_xlib.h"
 
 #include "wine/winuser16.h"
@@ -22,129 +65,435 @@
 
 DEFAULT_DEBUG_CHANNEL(clipboard)
 
-extern HWND hWndClipOwner;
-extern HWND hWndClipWindow;
-extern WINE_CLIPFORMAT ClipFormats[];
+/* Selection masks */
 
-static Bool   selectionAcquired = False;
-static Window selectionWindow = None;
-static Window selectionPrevWindow = None;
+#define S_NOSELECTION    0
+#define S_PRIMARY        1
+#define S_CLIPBOARD      2
+
+/* X selection context info */
+
+static char _CLIPBOARD[] = "CLIPBOARD";        /* CLIPBOARD atom name */
+static char FMT_PREFIX[] = "<WCF>";            /* Prefix for windows specific formats */
+static int    selectionAcquired = 0;           /* Contains the current selection masks */
+static Window selectionWindow = None;          /* The top level X window which owns the selection */
+static Window selectionPrevWindow = None;      /* The last X window that owned the selection */
+static Window PrimarySelectionOwner = None;    /* The window which owns the primary selection */
+static Window ClipboardSelectionOwner = None;  /* The window which owns the clipboard selection */
+static unsigned long cSelectionTargets = 0;    /* Number of target formats reported by TARGETS selection */
+static Atom selectionCacheSrc = XA_PRIMARY;    /* The selection source from which the clipboard cache was filled */
+
 
 /**************************************************************************
- * 		X11DRV_CLIPBOARD_CheckSelection	[Internal]
+ *		X11DRV_CLIPBOARD_MapPropertyToID
  *
- * Prevent X selection from being lost when a top level window is
- * destroyed.
+ *  Map an X selection property type atom name to a windows clipboard format ID
  */
-static void X11DRV_CLIPBOARD_CheckSelection(WND* pWnd)
+UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName)
 {
-    TRACE("\tchecking %08x\n",
-        (unsigned) X11DRV_WND_GetXWindow(pWnd)
-    );
+    /*
+     * If the property name starts with FMT_PREFIX strip this off and
+     * get the ID for a custom Windows registered format with this name.
+     * We can also understand STRING, PIXMAP and BITMAP.
+     */
+    if ( NULL == itemFmtName )
+        return 0;
+    else if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) )
+        return RegisterClipboardFormatA(itemFmtName + strlen(FMT_PREFIX));
+    else if ( 0 == strcmp(itemFmtName, "STRING") )
+        return CF_OEMTEXT;
+    else if ( 0 == strcmp(itemFmtName, "PIXMAP") )
+        return CF_DIB;
+    else if ( 0 == strcmp(itemFmtName, "BITMAP") )
+        return CF_DIB;
 
-    if( selectionAcquired && selectionWindow != None &&
-        X11DRV_WND_GetXWindow(pWnd) == selectionWindow )
+    WARN("\tNo mapping to Windows clipboard format for property %s\n", itemFmtName);
+    return 0;
+}
+
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_MapFormatToProperty
+ *
+ *  Map a windows clipboard format ID to an X selection property atom
+ */
+Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT wFormat)
+{
+    Atom prop = None;
+    
+    switch (wFormat)
     {
-	selectionPrevWindow = selectionWindow;
-	selectionWindow = None;
+        case CF_OEMTEXT:
+        case CF_TEXT:
+            prop = XA_STRING;
+            break;
 
-	if( pWnd->next ) 
-	    selectionWindow = X11DRV_WND_GetXWindow(pWnd->next);
-	else if( pWnd->parent )
-             if( pWnd->parent->child != pWnd ) 
-                 selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child);
+        case CF_DIB:
+        case CF_BITMAP:
+        {
+            /*
+             * Request a PIXMAP, only if WINE is NOT the selection owner,
+             * AND the requested format is not in the cache.
+             */
+            if ( !X11DRV_CLIPBOARD_IsSelectionowner() && !CLIPBOARD_IsPresent(wFormat) )
+            {
+              prop = XA_PIXMAP;
+              break;
+            }
+            /* Fall thru to the default case in order to use the native format */
+        }
+        
+        default:
+        {
+            /*
+             * If an X atom is registered for this format, return that
+             * Otherwise register a new atom.
+             */
+            char str[256];
+            char *fmtName = CLIPBOARD_GetFormatName(wFormat);
+            strcpy(str, FMT_PREFIX);
 
-	TRACE("\tswitching selection from %08x to %08x\n", 
-                    (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
-
-	if( selectionWindow != None )
-	{
-	    TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
-	    if( TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow )
-		selectionWindow = None;
-	}
+            if (fmtName)
+            {
+                strncat(str, fmtName, sizeof(str) - strlen(FMT_PREFIX));
+                prop = TSXInternAtom(display, str, False);
+            }
+            break;
+        }
     }
+
+    if (prop == None)
+        TRACE("\tNo mapping to X property for Windows clipboard format %d(%s)\n",
+              wFormat, CLIPBOARD_GetFormatName(wFormat));
+    
+    return prop;
+}
+
+/**************************************************************************
+ *	        X11DRV_CLIPBOARD_IsNativeProperty
+ *
+ *  Checks if a property is a native property type
+ */
+BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop)
+{
+    char *itemFmtName = TSXGetAtomName(display, prop);
+    BOOL bRet = FALSE;
+    
+    if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) )
+        bRet = TRUE;
+    
+    TSXFree(itemFmtName);
+    return bRet;
+}
+
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_CacheDataFormats
+ *
+ * Caches the list of data formats available from the current selection.
+ * This queries the selection owner for the TARGETS property and saves all
+ * reported property types.
+ */
+int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
+{
+    HWND           hWnd = NULL;
+    HWND           hWndClipWindow = GetOpenClipboardWindow();
+    WND*           wnd = NULL;
+    XEvent         xe;
+    Atom           aTargets;
+    Atom           atype=AnyPropertyType;
+    int		   aformat;
+    unsigned long  remain;
+    Atom*	   targetList=NULL;
+    Window         w;
+    Window         ownerSelection = NULL;
+        
+    /*
+     * Empty the clipboard cache 
+     */
+    CLIPBOARD_EmptyCache(TRUE);
+
+    cSelectionTargets = 0;
+    selectionCacheSrc = SelectionName;
+    
+    hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
+
+    ownerSelection = TSXGetSelectionOwner(display, SelectionName);
+    if ( !hWnd || (ownerSelection == None) )
+        return cSelectionTargets;
+
+    /*
+     * Query the selection owner for the TARGETS property
+     */
+    wnd = WIN_FindWndPtr(hWnd);
+    w = X11DRV_WND_FindXWindow(wnd);
+    WIN_ReleaseWndPtr(wnd);
+    wnd = NULL;
+
+    aTargets = TSXInternAtom(display, "TARGETS", False);
+
+    TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
+          TSXGetAtomName(display, selectionCacheSrc), (unsigned)ownerSelection );
+          
+    EnterCriticalSection( &X11DRV_CritSection );
+    XConvertSelection(display, selectionCacheSrc, aTargets,
+                    TSXInternAtom(display, "SELECTION_DATA", False),
+                    w, CurrentTime);
+
+    /*
+     * Wait until SelectionNotify is received
+     */
+    while( TRUE )
+    {
+       if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
+           if( xe.xselection.selection == selectionCacheSrc )
+               break;
+    }
+    LeaveCriticalSection( &X11DRV_CritSection );
+
+    /* Verify that the selection returned a valid TARGETS property */
+    if ( (xe.xselection.target != aTargets)
+          || (xe.xselection.property == None) )
+    {
+        TRACE("\tCould not retrieve TARGETS\n");
+        return cSelectionTargets;
+    }
+
+    /* Read the TARGETS property contents */
+    if(TSXGetWindowProperty(display, xe.xselection.requestor, xe.xselection.property,
+                            0, 0x3FFF, True, XA_ATOM, &atype, &aformat,
+                            &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
+        TRACE("\tCouldn't read TARGETS property\n");
+    else
+    {
+       TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
+             TSXGetAtomName(display,atype),aformat,cSelectionTargets, remain);
+       /*
+        * The TARGETS property should have returned us a list of atoms
+        * corresponding to each selection target format supported.
+        */
+       if(atype == XA_ATOM && aformat == 32)
+       {
+          int i;
+          LPWINE_CLIPFORMAT lpFormat;
+          
+          /* Cache these formats in the clipboard cache */
+
+          for (i = 0; i < cSelectionTargets; i++)
+          {
+              char *itemFmtName = TSXGetAtomName(display, targetList[i]);
+              UINT wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
+
+              /*
+               * If the clipboard format maps to a Windows format, simply store
+               * the atom identifier and record its availablity status
+               * in the clipboard cache.
+               */
+              if (wFormat)
+              {
+                  lpFormat = CLIPBOARD_LookupFormat( wFormat );
+                  
+                  /* Don't replace if the property already cached is a native format */
+                  if (lpFormat->wDataPresent
+                      && X11DRV_CLIPBOARD_IsNativeProperty(lpFormat->drvData))
+                  {
+                      TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s (Skipped)\n",
+                            i, itemFmtName, wFormat, lpFormat->Name);
+                  }
+                  else
+                  {
+                      lpFormat->wDataPresent = 1;
+                      lpFormat->drvData = targetList[i];
+                      TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s\n",
+                            i, itemFmtName, wFormat, lpFormat->Name);
+                  }
+              }
+              
+              TSXFree(itemFmtName);
+          }
+       }
+
+       /* Free the list of targets */
+       TSXFree(targetList);
+    }
+    
+    return cSelectionTargets;
 }
 
 /**************************************************************************
  *		X11DRV_CLIPBOARD_ReadSelection
+ *  Reads the contents of the X selection property into the WINE clipboard cache
+ *  converting the selection into a format compatible with the windows clipboard
+ *  if possible.
+ *  This method is invoked only to read the contents of a the selection owned
+ *  by an external application. i.e. when we do not own the X selection.
  */
-static void X11DRV_CLIPBOARD_ReadSelection(Window w, Atom prop)
+static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, Atom reqFormat)
 {
-    HANDLE 	 hText = 0;
-    LPWINE_CLIPFORMAT lpFormat = ClipFormats; 
+    Atom	      atype=AnyPropertyType;
+    int		      aformat;
+    unsigned long     nitems,remain,itemSize;
+    long              lRequestLength;
+    unsigned char*    val=NULL;
+    LPWINE_CLIPFORMAT lpFormat;
+    BOOL              bRet = FALSE;
+    HWND              hWndClipWindow = GetOpenClipboardWindow();
+    
+    if(prop == None)
+        return bRet;
 
     TRACE("Reading X selection...\n");
 
-    if(prop != None)
+    TRACE("\tretrieving property %s into %s\n",
+          TSXGetAtomName(display,reqFormat), TSXGetAtomName(display,prop) );
+
+    /*
+     * Retrieve the property in the required X format.
+     * First request a zero length in order to figure out the request size.
+     */
+    if(TSXGetWindowProperty(display,w,prop,0,0,True,reqFormat,
+                            &atype, &aformat, &nitems, &itemSize, &val) != Success)
     {
-	Atom		atype=AnyPropertyType;
-	int		aformat;
-	unsigned long 	nitems,remain;
-	unsigned char*	val=NULL;
+        WARN("\tcouldn't get property size\n");
+        return bRet;
+    }
+    /* Free property if one was returned */
+    if ( val )
+    {
+       TSXFree(val);
+       val = NULL;
+    }
+    
+    TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
+    lRequestLength = (itemSize * aformat/8)/4  + 1;
+    
+    if(TSXGetWindowProperty(display,w,prop,0,lRequestLength,True,reqFormat,
+                            &atype, &aformat, &nitems, &remain, &val) != Success)
+    {
+        WARN("\tcouldn't read property\n");
+        return bRet;
+    }
 
-        TRACE("\tgot property %s\n",TSXGetAtomName(display,prop));
+    TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
+          atype ? TSXGetAtomName(display,atype) : NULL, aformat,nitems,remain,val);
+    
+    if (remain)
+    {
+        WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
+        return bRet;
+    }
+    
+    /*
+     * Translate the X property into the appropriate Windows clipboard
+     * format, if possible.
+     */
+    if ( (reqFormat == XA_STRING)
+         && (atype == XA_STRING) && (aformat == 8) ) /* treat Unix text as CF_OEMTEXT */
+    {
+      HANDLE16   hText = 0;
+      int 	   i,inlcount = 0;
+      char*      lpstr;
+ 
+      TRACE("\tselection is '%s'\n",val);
+ 
+      for(i=0; i <= nitems; i++)
+          if( val[i] == '\n' ) inlcount++;
+ 
+      if( nitems )
+      {
+        hText=GlobalAlloc16(GMEM_MOVEABLE, nitems + inlcount + 1);
+        if( (lpstr = (char*)GlobalLock16(hText)) )
+        {
+          for(i=0,inlcount=0; i <= nitems; i++)
+          {
+             if( val[i] == '\n' ) lpstr[inlcount++]='\r';
+             lpstr[inlcount++]=val[i];
+          }
+          GlobalUnlock16(hText);
+        }
+        else
+            hText = 0;
+      }
+      
+      if( hText )
+      {
+          /* delete previous CF_TEXT and CF_OEMTEXT data */
+          lpFormat = CLIPBOARD_LookupFormat(CF_TEXT);
+          if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
+              CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
+          
+          lpFormat = CLIPBOARD_LookupFormat(CF_OEMTEXT);
+          if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32)  
+              CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
+ 
+          /* Update the CF_OEMTEXT record */
+          lpFormat->wDataPresent = 1;
+          lpFormat->hData32 = 0;
+          lpFormat->hData16 = hText;
+ 
+          bRet = TRUE;
+      }
+    }
+    else if ( reqFormat == XA_PIXMAP ) /* treat PIXMAP as CF_DIB or CF_BITMAP */
+    {
+      if (wFormat == CF_BITMAP )
+          FIXME("PIXMAP to CF_BITMAP conversion not yet implemented!\n");
+      else if (wFormat == CF_DIB )
+          FIXME("PIXMAP to CF_DIB conversion not yet implemented!\n");
+    }
+ 
+    /* For other data types simply copy the X data without conversion */
+    else
+    {
+      HANDLE hClipData = 0;
+      void*  lpClipData;
+      int cBytes = nitems * aformat/8;
 
-        /* TODO: Properties longer than 64K */
+      if( cBytes )
+      {
+        /* Turn on the DDESHARE flag to enable shared 32 bit memory */
+        hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cBytes );
+        if( (lpClipData = GlobalLock(hClipData)) )
+        {
+            memcpy(lpClipData, val, cBytes);
+            GlobalUnlock(hClipData);
+        }
+        else
+            hClipData = 0;
+      }
+      
+      if( hClipData )
+      {
+          /* delete previous clipboard record if any */
+          lpFormat = CLIPBOARD_LookupFormat(wFormat);
+          if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
+              CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
+          
+          /* Update the clipboard record */
+          lpFormat->wDataPresent = 1;
+          lpFormat->hData32 = hClipData;
+          lpFormat->hData16 = 0;
 
-	if(TSXGetWindowProperty(display,w,prop,0,0x3FFF,True,XA_STRING,
-	    &atype, &aformat, &nitems, &remain, &val) != Success)
-	    WARN("\tcouldn't read property\n");
-	else
-	{
-           TRACE("\tType %s,Format %d,nitems %ld,value %s\n",
-		             TSXGetAtomName(display,atype),aformat,nitems,val);
-
-	   if(atype == XA_STRING && aformat == 8)
-	   {
-	      int 	i,inlcount = 0;
-	      char*	lpstr;
-
-	      TRACE("\tselection is '%s'\n",val);
-
-	      for(i=0; i <= nitems; i++)
-		  if( val[i] == '\n' ) inlcount++;
-
-	      if( nitems )
-	      {
-	        hText=GlobalAlloc(GMEM_MOVEABLE, nitems + inlcount + 1);
-	        if( (lpstr = (char*)GlobalLock(hText)) )
-	          for(i=0,inlcount=0; i <= nitems; i++)
-	          {
-	  	     if( val[i] == '\n' ) lpstr[inlcount++]='\r';
-		     lpstr[inlcount++]=val[i];
-		  }
-	        else hText = 0;
-	      }
-	   }
-	   TSXFree(val);
-	}
-   }
-
-   /* delete previous CF_TEXT and CF_OEMTEXT data */
-
-   if( hText )
-   {
-       lpFormat = &ClipFormats[CF_TEXT-1];
-       if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
-           CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
-       lpFormat = &ClipFormats[CF_OEMTEXT-1];
-       if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32)  
-           CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
-       lpFormat->wDataPresent = 1;
-       lpFormat->hData32 = hText;
-       lpFormat->hData16 = 0;
-   }
+          bRet = TRUE;
+      }
+    }
+ 
+    /* Free the retrieved property */
+    TSXFree(val);
+    
+    return bRet;
 }
 
 /**************************************************************************
  *		X11DRV_CLIPBOARD_ReleaseSelection
  *
- * Wine might have lost XA_PRIMARY selection because of
- * EmptyClipboard() or other client. 
+ * Release an XA_PRIMARY or XA_CLIPBOARD selection that we own, in response
+ * to a SelectionClear event.
+ * This can occur in response to another client grabbing the X selection.
+ * If the XA_CLIPBOARD selection is lost we relinquish XA_PRIMARY as well.
  */
-void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd)
+void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
 {
+    Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
+    
     /* w is the window that lost selection,
      * 
      * selectionPrevWindow is nonzero if CheckSelection() was called. 
@@ -159,17 +508,81 @@
 	{
 	    /* alright, we really lost it */
 
-	    selectionAcquired = False;
-	    selectionWindow = None; 
+            if ( selType == xaClipboard )  /* completely give up the selection */
+            {
+              TRACE("Lost CLIPBOARD selection\n");
+                
+              /* We are completely giving up the selection.
+               * Make sure we can open the windows clipboard first. */
+              
+              if ( !OpenClipboard(hwnd) )
+              {
+                  /*
+                   * We can't empty the clipboard if we cant open it so abandon.
+                   * Wine will think that it still owns the selection but this is
+                   * safer than losing the selection without properly emptying
+                   * the clipboard. Perhaps we should forcibly re-assert ownership
+                   * of the CLIPBOARD selection in this case...
+                   */
+                  ERR("\tClipboard is busy. Could not give up selection!\n");
+                  return;
+              }
 
-	    /* but we'll keep existing data for internal use */
+              selectionPrevWindow = selectionWindow;
+              selectionWindow = None;
+              PrimarySelectionOwner = ClipboardSelectionOwner = 0;
+              
+              /* Voluntarily give up the PRIMARY selection if we still own it */
+              if ( selectionAcquired & S_PRIMARY )
+              {
+                  XEvent xe;
+                  TRACE("Releasing XA_PRIMARY selection\n");
+                  
+                  TSXSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
+
+                  /* Wait until SelectionClear is processed */
+                  if( selectionPrevWindow )
+                      while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
+                                                      SelectionClear, &xe ) );
+              }
+
+              /* Empty the windows clipboard.
+               * We should pretend that we still own the selection BEFORE calling
+               * EmptyClipboard() since otherwise this has the side effect of
+               * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
+               * to be re-acquired by us!
+               */
+              selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
+              EmptyClipboard();
+              selectionAcquired = S_NOSELECTION;
+              
+              CloseClipboard();
+
+              /* Give up ownership of the windows clipboard */
+              CLIPBOARD_ReleaseOwner();
+            }
+            else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */
+            {
+                TRACE("Lost PRIMARY selection\n");
+                PrimarySelectionOwner = 0;
+                selectionAcquired &= ~S_PRIMARY;  /* clear S_PRIMARY mask */
+            }
+
+            cSelectionTargets = 0;
 	}
+        /* but we'll keep existing data for internal use */
 	else if( w == selectionPrevWindow )
 	{
+            Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
+            
 	    w = TSXGetSelectionOwner(display, XA_PRIMARY);
 	    if( w == None )
 		TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
-	}
+
+	    w = TSXGetSelectionOwner(display, xaClipboard);
+	    if( w == None )
+		TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
+        }
     }
 
     selectionPrevWindow = None;
@@ -177,14 +590,18 @@
 
 /**************************************************************************
  *		X11DRV_CLIPBOARD_Empty
+ *  Voluntarily release all currently owned X selections
  */
-void X11DRV_CLIPBOARD_Empty()
+void X11DRV_CLIPBOARD_Release()
 {
     if( selectionAcquired )
     {
 	XEvent xe;
+        Window savePrevWindow = selectionWindow;
+        Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
+        BOOL bHasPrimarySelection = selectionAcquired & S_PRIMARY;
 
-	selectionAcquired   = False;
+	selectionAcquired   = S_NOSELECTION;
 	selectionPrevWindow = selectionWindow;
 	selectionWindow     = None;
       
@@ -192,135 +609,384 @@
 	     (unsigned)selectionPrevWindow);
       
 	EnterCriticalSection(&X11DRV_CritSection);
-	XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
 
+        TRACE("Releasing CLIPBOARD selection\n");
+        XSetSelectionOwner(display, xaClipboard, None, CurrentTime);
 	if( selectionPrevWindow )
 	    while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
-					    SelectionClear, &xe ) );
+                                            SelectionClear, &xe ) );
+
+        if ( bHasPrimarySelection )
+        {
+            TRACE("Releasing XA_PRIMARY selection\n");
+            selectionPrevWindow = savePrevWindow; /* May be cleared in X11DRV_CLIPBOARD_ReleaseSelection */
+            XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
+    
+            if( selectionPrevWindow )
+                while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
+                                                SelectionClear, &xe ) );
+        }
+        
 	LeaveCriticalSection(&X11DRV_CritSection);
     }
 }
 
 /**************************************************************************
+ *		X11DRV_CLIPBOARD_Acquire()
+ */
+void X11DRV_CLIPBOARD_Acquire()
+{
+    Window       owner;
+    HWND         hWndClipWindow = GetOpenClipboardWindow();
+
+    /*
+     * 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.
+     *
+     */
+
+    if ( !(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)) )
+    {
+        Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
+        WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() );
+        owner = X11DRV_WND_FindXWindow(tmpWnd );
+        WIN_ReleaseWndPtr(tmpWnd);
+
+        /* Grab PRIMARY selection if not owned */
+        if ( !(selectionAcquired & S_PRIMARY) )
+            TSXSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
+        
+        /* Grab CLIPBOARD selection if not owned */
+        if ( !(selectionAcquired & S_CLIPBOARD) )
+            TSXSetSelectionOwner(display, xaClipboard, owner, CurrentTime);
+
+        if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner )
+	    selectionAcquired |= S_PRIMARY;
+
+        if( TSXGetSelectionOwner(display,xaClipboard) == owner)
+	    selectionAcquired |= S_CLIPBOARD;
+
+        if (selectionAcquired)
+        {
+	    selectionWindow = owner;
+	    TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
+        }
+    }
+}
+
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_IsFormatAvailable
+ *
+ * Checks if the specified format is available in the current selection
+ * Only invoked when WINE is not the selection owner
+ */
+BOOL X11DRV_CLIPBOARD_IsFormatAvailable(UINT wFormat)
+{
+    Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
+    Window ownerPrimary = TSXGetSelectionOwner(display,XA_PRIMARY);
+    Window ownerClipboard = TSXGetSelectionOwner(display,xaClipboard);
+
+    /*
+     * If the selection has not been previously cached, or the selection has changed,
+     * try and cache the list of available selection targets from the current selection.
+     */
+    if ( !cSelectionTargets || (PrimarySelectionOwner != ownerPrimary)
+                         || (ClipboardSelectionOwner != ownerClipboard) )
+    {
+        /*
+         * First try cacheing the CLIPBOARD selection.
+         * If unavailable try PRIMARY.
+         */
+        if ( X11DRV_CLIPBOARD_CacheDataFormats(xaClipboard) == 0 )
+        {
+            X11DRV_CLIPBOARD_CacheDataFormats(XA_PRIMARY);
+        }
+
+        ClipboardSelectionOwner = ownerClipboard;
+        PrimarySelectionOwner = ownerPrimary;
+    }
+
+    /* Exit if there is no selection */
+    if ( !ownerClipboard && !ownerPrimary )
+        return FALSE;
+
+    if ( wFormat == CF_TEXT )
+        wFormat = CF_OEMTEXT;
+    
+    /* Check if the format is available in the clipboard cache */
+    if ( CLIPBOARD_IsPresent(wFormat) )
+        return TRUE;
+
+    /*
+     * Many X client apps (such as XTerminal) don't support being queried
+     * for the "TARGETS" target atom. To handle such clients we must actually
+     * try to convert the selection to the requested type.
+     */
+    if ( !cSelectionTargets )
+        return X11DRV_CLIPBOARD_GetData( wFormat );
+        
+    return FALSE;
+}
+
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_RegisterFormat
+ *
+ * Registers a custom X clipboard format
+ * Returns: TRUE - success,  FALSE - failure
+ */
+BOOL X11DRV_CLIPBOARD_RegisterFormat( LPCSTR FormatName )
+{
+    Atom prop = None;
+    char str[256];
+    
+    /*
+     * If an X atom is registered for this format, return that
+     * Otherwise register a new atom.
+     */
+    if (FormatName)
+    {
+        /* Add a WINE specific prefix to the format */
+        strcpy(str, FMT_PREFIX);
+        strncat(str, FormatName, sizeof(str) - strlen(FMT_PREFIX));
+        prop = TSXInternAtom(display, str, False);
+    }
+    
+    return (prop) ? TRUE : FALSE;
+}
+
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_IsSelectionowner
+ *
+ * Returns: TRUE - We(WINE) own the selection, FALSE - Selection not owned by us
+ */
+BOOL X11DRV_CLIPBOARD_IsSelectionowner()
+{
+    return selectionAcquired;
+}
+
+/**************************************************************************
  *		X11DRV_CLIPBOARD_SetData
+ *
+ * We don't need to do anything special here since the clipboard code
+ * maintains the cache. 
+ *
  */
 void X11DRV_CLIPBOARD_SetData(UINT wFormat)
 {
-    Window       owner;
-
-    /* Acquire X selection if text format */
-
-    if( !selectionAcquired && 
-	(wFormat == CF_TEXT || wFormat == CF_OEMTEXT) )
-    {
-        WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() );
-	owner = X11DRV_WND_FindXWindow(tmpWnd );
-
-	TSXSetSelectionOwner(display,XA_PRIMARY, owner, CurrentTime);
-	if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner )
-	{
-	    selectionAcquired = True;
-	    selectionWindow = owner;
-
-	    TRACE("Grabbed X selection, owner=(%08x)\n", 
-						(unsigned) owner);
-	}
-        WIN_ReleaseWndPtr(tmpWnd);
-    }
+    /* Make sure we have acquired the X selection */
+    X11DRV_CLIPBOARD_Acquire();
 }
 
 /**************************************************************************
  *		X11DRV_CLIPBOARD_GetData
  *
+ * This method is invoked only when we DO NOT own the X selection
+ *
  * NOTE: Clipboard driver doesn't get requests for CF_TEXT data, only
- *	 for CF_OEMTEXT.
+ * for CF_OEMTEXT.
+ * We always get the data from the selection client each time,
+ * since we have no way of determining if the data in our cache is stale.
  */
 BOOL X11DRV_CLIPBOARD_GetData(UINT wFormat)
 {
     BOOL bRet = selectionAcquired;
+    HWND hWndClipWindow = GetOpenClipboardWindow();
     HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
     WND* wnd = NULL;
+    LPWINE_CLIPFORMAT lpFormat;
 
-    if( wFormat != CF_OEMTEXT ) return FALSE;
-
-    if( !bRet && (wnd = WIN_FindWndPtr(hWnd)) )
+    if( !selectionAcquired && (wnd = WIN_FindWndPtr(hWnd)) )
     {
 	XEvent xe;
+        Atom propRequest;
 	Window w = X11DRV_WND_FindXWindow(wnd);
+        WIN_ReleaseWndPtr(wnd);
+        wnd = NULL;
 
-	TRACE("Requesting XA_STRING selection...\n");
+        /* Map the format ID requested to an X selection property.
+         * If the format is in the cache, use the atom associated
+         * with it.
+         */
+        
+        lpFormat = CLIPBOARD_LookupFormat( wFormat );
+        if (lpFormat && lpFormat->wDataPresent && lpFormat->drvData)
+            propRequest = (Atom)lpFormat->drvData;
+        else
+            propRequest = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat);
 
-	EnterCriticalSection( &X11DRV_CritSection );
-	XConvertSelection(display, XA_PRIMARY, XA_STRING,
-			XInternAtom(display, "PRIMARY_TEXT", False),
-			w, CurrentTime);
+        if (propRequest)
+        {
+            TRACE("Requesting %s selection from %s...\n",
+                  TSXGetAtomName(display, propRequest),
+                  TSXGetAtomName(display, selectionCacheSrc) );
     
-        /* wait until SelectionNotify is received */
+            EnterCriticalSection( &X11DRV_CritSection );
+            XConvertSelection(display, selectionCacheSrc, propRequest,
+                            TSXInternAtom(display, "SELECTION_DATA", False),
+                            w, CurrentTime);
+        
+            /* wait until SelectionNotify is received */
+    
+            while( TRUE )
+            {
+               if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
+                   if( xe.xselection.selection == selectionCacheSrc )
+                       break;
+            }
+            LeaveCriticalSection( &X11DRV_CritSection );
+        
+            /*
+             *  Read the contents of the X selection property into WINE's
+             *  clipboard cache converting the selection to be compatible if possible.
+             */
+            bRet = X11DRV_CLIPBOARD_ReadSelection( wFormat,
+                                                   xe.xselection.requestor,
+                                                   xe.xselection.property,
+                                                   xe.xselection.target);
+        }
+        else
+            bRet = FALSE;
 
-	while( TRUE )
-	{
-	   if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
-	       if( xe.xselection.selection == XA_PRIMARY )
-		   break;
-	}
-	LeaveCriticalSection( &X11DRV_CritSection );
-
-	if (xe.xselection.target != XA_STRING) 
-	    X11DRV_CLIPBOARD_ReadSelection( 0, None );
-	else 
-	    X11DRV_CLIPBOARD_ReadSelection( xe.xselection.requestor, 
-					    xe.xselection.property );
-
-	/* treat Unix text as CF_OEMTEXT */
-
-	bRet = (BOOL)ClipFormats[CF_OEMTEXT-1].wDataPresent;
-
-	TRACE("\tpresent CF_OEMTEXT = %i\n", bRet );
-	WIN_ReleaseWndPtr(wnd);
+        TRACE("\tpresent %s = %i\n", CLIPBOARD_GetFormatName(wFormat), bRet );
     }
+    
     return bRet;
 }
 
 /**************************************************************************
  *		X11DRV_CLIPBOARD_ResetOwner
  *
- * Called from DestroyWindow().
+ * 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.
  */
 void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
 {
-    LPWINE_CLIPFORMAT lpFormat = ClipFormats;
+    HWND hWndClipOwner = 0;
+    Window XWnd = X11DRV_WND_GetXWindow(pWnd);
+    Atom xaClipboard;
+    BOOL bLostSelection = FALSE;
 
-    if(bFooBar && X11DRV_WND_GetXWindow(pWnd))
-      return;
+    /* There is nothing to do if we don't own the selection,
+     * or if the X window which currently owns the selecion is different
+     * from the one passed in.
+     */
+    if ( !selectionAcquired || XWnd != selectionWindow
+         || selectionWindow == None )
+       return;
 
-    if(!bFooBar && !X11DRV_WND_GetXWindow(pWnd))
-      return;
+    if ( (bFooBar && XWnd) || (!bFooBar && !XWnd) )
+       return;
 
-    TRACE("clipboard owner = %04x, selection = %08x\n", 
-				hWndClipOwner, (unsigned)selectionWindow);
+    hWndClipOwner = GetClipboardOwner();
+    xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
+    
+    TRACE("clipboard owner = %04x, selection window = %08x\n",
+          hWndClipOwner, (unsigned)selectionWindow);
 
-    if( pWnd->hwndSelf == hWndClipOwner)
+#if(0)
+    /* Check if all formats are already in the clipboard cache */
+    if( !CLIPBOARD_IsCacheRendered() )
     {
 	SendMessage16(hWndClipOwner,WM_RENDERALLFORMATS,0,0L);
 
 	/* check if all formats were rendered */
-
-	while(lpFormat)
-	{
-	    if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
-	    {
-		TRACE("\tdata missing for clipboard format %i\n", 
-				   lpFormat->wFormatID); 
-		lpFormat->wDataPresent = 0;
-	    }
-	    lpFormat = lpFormat->NextFormat;
-	}
-	hWndClipOwner = 0;
+        if ( !CLIPBOARD_IsCacheRendered() )
+        {
+	    ERR("\tCould not render all formats\n");
+            CLIPBOARD_ReleaseOwner();
+        }
     }
-
+#endif
+    
     /* now try to salvage current selection from being destroyed by X */
 
-    if( X11DRV_WND_GetXWindow(pWnd) ) X11DRV_CLIPBOARD_CheckSelection(pWnd);
+    TRACE("\tchecking %08x\n", (unsigned) XWnd);
+
+    selectionPrevWindow = selectionWindow;
+    selectionWindow = None;
+
+    if( pWnd->next ) 
+        selectionWindow = X11DRV_WND_GetXWindow(pWnd->next);
+    else if( pWnd->parent )
+         if( pWnd->parent->child != pWnd ) 
+             selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child);
+
+    if( selectionWindow != None )
+    {
+        /* 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 = False;
+
+        TRACE("\tswitching selection from %08x to %08x\n", 
+                    (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
+    
+        /* Assume ownership for the PRIMARY and CLIPBOARD selection */
+        if ( saveSelectionState & S_PRIMARY )
+            TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
+        
+        TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
+        
+        /* Lose the selection if something went wrong */
+        if ( ( (saveSelectionState & S_PRIMARY) &&
+               (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) )
+             || (TSXGetSelectionOwner(display, xaClipboard) != selectionWindow) )
+        {
+            bLostSelection = TRUE;
+            goto END;
+        }
+        else
+        {
+            /* Update selection state */
+            selectionAcquired = saveSelectionState;
+            if (saveSelectionState & S_PRIMARY)
+               PrimarySelectionOwner = selectionWindow;
+            
+            ClipboardSelectionOwner = selectionWindow;
+        }
+    }
+    else
+    {
+        bLostSelection = TRUE;
+        goto END;
+    }
+
+END:
+    if (bLostSelection)
+    {
+       /* Empty the windows clipboard.
+        * We should pretend that we still own the selection BEFORE calling
+        * EmptyClipboard() since otherwise this has the side effect of
+        * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
+        * to be re-acquired by us!
+        */
+
+       TRACE("\tLost the selection! Emptying the clipboard...\n");
+    
+       OpenClipboard(NULL);
+       selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
+       EmptyClipboard();
+       selectionAcquired = S_NOSELECTION;
+       
+       CloseClipboard();
+ 
+       /* Give up ownership of the windows clipboard */
+       CLIPBOARD_ReleaseOwner();
+       ClipboardSelectionOwner = PrimarySelectionOwner = 0;
+       selectionWindow = 0;
+    }
 }
 
 #endif /* !defined(X_DISPLAY_MISSING) */