From d05b7beb5a17f4747dd2cb88b0b5a8ab57a16edf Mon Sep 17 00:00:00 2001
From: Noel Borthwick <noel@macadamian.com>
Date: Mon, 20 Sep 1999 15:42:47 +0000
Subject: [PATCH] - Add clipboard support for copying/pasting bitmaps or
 Pixmaps between Wine   and native Linux applications. - Respond to the
 MULTIPLE selection request target when Wine is the   selection owner. - Relax
 type checking for TARGETS selection.

---
 graphics/x11drv/bitmap.c   | 127 +++++++
 graphics/x11drv/dib.c      | 108 ++++++
 include/bitmap.h           |   4 +-
 include/ts_xlib.h          |   1 +
 include/x11drv.h           |   9 +
 objects/dib.c              |  84 +++++
 tsx11/X11_calls            |   1 +
 tsx11/ts_xlib.c            |  11 +
 windows/clipboard.c        |  33 +-
 windows/x11drv/clipboard.c | 218 ++++++++++--
 windows/x11drv/event.c     | 665 +++++++++++++++++++++++++++----------
 11 files changed, 1041 insertions(+), 220 deletions(-)

diff --git a/graphics/x11drv/bitmap.c b/graphics/x11drv/bitmap.c
index 51c31b3d808..8bb0fe3b2e6 100644
--- a/graphics/x11drv/bitmap.c
+++ b/graphics/x11drv/bitmap.c
@@ -2,6 +2,7 @@
  * X11DRV bitmap objects
  *
  * Copyright 1993 Alexandre Julliard
+ *           1999 Noel Borthwick
  */
 
 #include "config.h"
@@ -532,6 +533,132 @@ BOOL X11DRV_BITMAP_DeleteObject( HBITMAP hbitmap, BITMAPOBJ * bmp )
     return TRUE;
 }
 
+/**************************************************************************
+ *	        X11DRV_BITMAP_CreateBitmapHeaderFromPixmap
+ *
+ *  Allocates an HBITMAP which references the Pixmap passed in.
+ *  Note: This function makes the bitmap an owner of the Pixmap so subsequently
+ *  calling DeleteObject on this will free the Pixmap as well.
+ */
+HBITMAP X11DRV_BITMAP_CreateBitmapHeaderFromPixmap(Pixmap pixmap)
+{
+    HBITMAP hBmp = 0;
+    BITMAPOBJ *pBmp = NULL;
+    X11DRV_PHYSBITMAP *pPhysBmp = NULL;
+    Window root;
+    int x,y;               /* Unused */
+    unsigned border_width; /* Unused */
+    unsigned int depth, width, height;
+
+    /* Get the Pixmap dimensions and bit depth */
+    if ( 0 == TSXGetGeometry(display, pixmap, &root, &x, &y, &width, &height,
+                             &border_width, &depth) )
+        goto END;
+
+    TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
+          width, height, depth);
+    
+    /*
+     * Create an HBITMAP with the same dimensions and BPP as the pixmap,
+     * and make it a container for the pixmap passed.
+     */
+    hBmp = CreateBitmap( width, height, 1, depth, NULL );
+
+    /*�Allocate DDBitmap and physBitmap structures in BITMAPOBJ.
+     * The hBmp is just a filled in BITMAPOBJ header at this point.
+     */
+    pBmp = (BITMAPOBJ *)GDI_GetObjPtr( hBmp, BITMAP_MAGIC );
+    pPhysBmp = X11DRV_AllocBitmap( pBmp );
+    if( !pPhysBmp )
+    {
+        DeleteObject(hBmp);
+        hBmp = NULL;
+        goto END;
+    }
+    
+    /* Point to our Pixmap in the physical bitmap structure */
+    pPhysBmp->pixmap = pixmap;
+
+END:
+    TRACE("\tReturning HBITMAP %x\n", hBmp);
+    return hBmp;
+}
+
+
+/**************************************************************************
+ *	        X11DRV_BITMAP_CreateBitmapFromPixmap
+ *
+ *  Allocates an HBITMAP and copies the Pixmap data into it.
+ *  If bDeletePixmap is TRUE, the Pixmap passed in is deleted after the conversion.
+ */
+HBITMAP X11DRV_BITMAP_CreateBitmapFromPixmap(Pixmap pixmap, BOOL bDeletePixmap)
+{
+    HBITMAP hBmp = 0, hBmpCopy = 0;
+    BITMAPOBJ *pBmp = NULL;
+    unsigned int width, height;
+
+    /* Allocate an HBITMAP which references the Pixmap passed to us */
+    hBmp = X11DRV_BITMAP_CreateBitmapHeaderFromPixmap(pixmap);
+    if (!hBmp)
+    {
+        TRACE("\tCould not create bitmap header for Pixmap\n");
+        goto END;
+    }
+
+    /* Get the bitmap dimensions */
+    width = pBmp->bitmap.bmWidth;
+    height = pBmp->bitmap.bmHeight;
+                 
+    hBmpCopy = CopyImage(hBmp, IMAGE_BITMAP, width, height, LR_CREATEDIBSECTION);
+
+    /* We can now get rid of the HBITMAP wrapper we created earlier.
+     * Note: Simply calling DeleteObject will free the embedded Pixmap as well.
+     */
+    if (!bDeletePixmap)
+    {
+        /* Manually free the DDBitmap internals to prevent the Pixmap 
+         * from being deleted by DeleteObject.
+         */
+        pBmp = (BITMAPOBJ *)GDI_GetObjPtr( hBmp, BITMAP_MAGIC );
+        HeapFree( GetProcessHeap(), 0, pBmp->DDBitmap->physBitmap );
+        HeapFree( GetProcessHeap(), 0, pBmp->DDBitmap );
+        pBmp->DDBitmap = NULL;
+    }
+    DeleteObject(hBmp);  
+
+END:
+    TRACE("\tReturning HBITMAP %x\n", hBmpCopy);
+    return hBmpCopy;
+}
+
+
+/**************************************************************************
+ *	           X11DRV_BITMAP_CreatePixmapFromBitmap
+ *
+ *    Creates a Pixmap from a bitmap
+ */
+Pixmap X11DRV_BITMAP_CreatePixmapFromBitmap( HBITMAP hBmp, HDC hdc )
+{
+    HGLOBAL hPackedDIB = NULL;
+    Pixmap pixmap = NULL;
+
+    /*
+     * Create a packed DIB from the bitmap passed to us.
+     * A packed DIB contains a BITMAPINFO structure followed immediately by
+     * an optional color palette and the pixel data.
+     */
+    hPackedDIB = DIB_CreateDIBFromBitmap(hdc, hBmp);
+
+    /* Create a Pixmap from the packed DIB */
+    pixmap = X11DRV_DIB_CreatePixmapFromDIB( hPackedDIB, hdc );
+
+    /* Free the temporary packed DIB */
+    GlobalFree(hPackedDIB);
+
+    return pixmap;
+}
+
+
 /***********************************************************************
  *           X11DRV_BITMAP_Pixmap
  *
diff --git a/graphics/x11drv/dib.c b/graphics/x11drv/dib.c
index 8d2019099cf..dacb14da41a 100644
--- a/graphics/x11drv/dib.c
+++ b/graphics/x11drv/dib.c
@@ -3418,6 +3418,114 @@ void X11DRV_DIB_DeleteDIBSection(BITMAPOBJ *bmp)
 }
 
 
+/**************************************************************************
+ *	        X11DRV_DIB_CreateDIBFromPixmap
+ *
+ *  Allocates a packed DIB and copies the Pixmap data into it.
+ *  If bDeletePixmap is TRUE, the Pixmap passed in is deleted after the conversion.
+ */
+HGLOBAL X11DRV_DIB_CreateDIBFromPixmap(Pixmap pixmap, HDC hdc, BOOL bDeletePixmap)
+{
+    HBITMAP hBmp = 0;
+    BITMAPOBJ *pBmp = NULL;
+    HGLOBAL hPackedDIB = NULL;
+
+    /* Allocates an HBITMAP which references the Pixmap passed to us */
+    hBmp = X11DRV_BITMAP_CreateBitmapHeaderFromPixmap(pixmap);
+    if (!hBmp)
+    {
+        TRACE_(bitmap)("\tCould not create bitmap header for Pixmap\n");
+        goto END;
+    }
+
+    /*
+     * Create a packed DIB from the Pixmap wrapper bitmap created above.
+     * A packed DIB contains a BITMAPINFO structure followed immediately by
+     * an optional color palette and the pixel data.
+     */
+    hPackedDIB = DIB_CreateDIBFromBitmap(hdc, hBmp);
+    
+    /* Get a pointer to the BITMAPOBJ structure */
+    pBmp = (BITMAPOBJ *)GDI_GetObjPtr( hBmp, BITMAP_MAGIC );
+
+    /* We can now get rid of the HBITMAP wrapper we created earlier.
+     * Note: Simply calling DeleteObject will free the embedded Pixmap as well.
+     */
+    if (!bDeletePixmap)
+    {
+        /* Manually free the DDBitmap internals to prevent the Pixmap 
+         * from being deleted by DeleteObject.
+         */
+        HeapFree( GetProcessHeap(), 0, pBmp->DDBitmap->physBitmap );
+        HeapFree( GetProcessHeap(), 0, pBmp->DDBitmap );
+        pBmp->DDBitmap = NULL;
+    }
+    DeleteObject(hBmp);  
+    
+END:
+    TRACE_(bitmap)("\tReturning packed DIB %x\n", hPackedDIB);
+    return hPackedDIB;
+}
+
+
+/**************************************************************************
+ *	           X11DRV_DIB_CreatePixmapFromDIB
+ *
+ *    Creates a Pixmap from a packed DIB
+ */
+Pixmap X11DRV_DIB_CreatePixmapFromDIB( HGLOBAL hPackedDIB, HDC hdc )
+{
+    Pixmap pixmap = NULL;
+    HBITMAP hBmp = 0;
+    BITMAPOBJ *pBmp = NULL;
+    LPBYTE pPackedDIB = NULL;
+    LPBITMAPINFO pbmi = NULL;
+    LPBITMAPINFOHEADER pbmiHeader = NULL;
+    LPBYTE pbits = NULL;
+    
+    /* Get a pointer to the packed DIB's data  */
+    pPackedDIB = (LPBYTE)GlobalLock(hPackedDIB);
+    pbmiHeader = (LPBITMAPINFOHEADER)pPackedDIB;
+    pbmi = (LPBITMAPINFO)pPackedDIB;
+    pbits = (LPBYTE)(pPackedDIB
+                     + DIB_BitmapInfoSize( (LPBITMAPINFO)pbmiHeader, DIB_RGB_COLORS ));
+    
+    /* Create a DDB from the DIB */
+     
+    hBmp = CreateDIBitmap(hdc,
+                          pbmiHeader,
+                          CBM_INIT,
+                          (LPVOID)pbits,
+                          pbmi,
+                          DIB_RGB_COLORS);
+
+    GlobalUnlock(hPackedDIB);
+
+    TRACE_(bitmap)("CreateDIBitmap returned %x\n", hBmp);
+
+    /* Retrieve the internal Pixmap from the DDB */
+     
+    pBmp = (BITMAPOBJ *) GDI_GetObjPtr( hBmp, BITMAP_MAGIC );
+
+    if (pBmp->DDBitmap && pBmp->DDBitmap->physBitmap)
+    {
+        pixmap = ((X11DRV_PHYSBITMAP *)(pBmp->DDBitmap->physBitmap))->pixmap;
+        if (!pixmap)
+            TRACE_(bitmap)("NULL Pixmap in DDBitmap->physBitmap!\n");
+        
+        /* Manually free the BITMAPOBJ internals so that we can steal its pixmap */
+        HeapFree( GetProcessHeap(), 0, pBmp->DDBitmap->physBitmap );
+        HeapFree( GetProcessHeap(), 0, pBmp->DDBitmap );
+        pBmp->DDBitmap = NULL;   /* Its not a DDB anymore */
+    }
+
+    /* Delete the DDB we created earlier now that we have stolen its pixmap */
+    DeleteObject(hBmp);
+    
+    TRACE_(bitmap)("\tReturning Pixmap %ld\n", pixmap);
+    return pixmap;
+}
+
 #endif /* !defined(X_DISPLAY_MISSING) */
 
 
diff --git a/include/bitmap.h b/include/bitmap.h
index 61c024d66cc..3b25e4574d1 100644
--- a/include/bitmap.h
+++ b/include/bitmap.h
@@ -62,7 +62,7 @@ extern int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, DWORD *width,
 extern void DIB_UpdateDIBSection( DC *dc, BOOL toDIB );
 extern void DIB_DeleteDIBSection( BITMAPOBJ *bmp );
 extern void DIB_SelectDIBSection( DC *dc, BITMAPOBJ *bmp );
-extern void DIB_FixColorsToLoadflags(BITMAPINFO * bmi, UINT loadflags,
-  BYTE pix);
+extern void DIB_FixColorsToLoadflags(BITMAPINFO * bmi, UINT loadflags, BYTE pix);
+extern HGLOBAL DIB_CreateDIBFromBitmap(HDC hdc, HBITMAP hBmp);
 
 #endif  /* __WINE_BITMAP_H */
diff --git a/include/ts_xlib.h b/include/ts_xlib.h
index f1a87912a56..67094554e10 100644
--- a/include/ts_xlib.h
+++ b/include/ts_xlib.h
@@ -59,6 +59,7 @@ extern int  TSXConvertSelection(Display*, Atom, Atom, Atom, Window, Time);
 extern int  TSXCopyArea(Display*, Drawable, Drawable, GC, int, int, unsigned int, unsigned int, int, int);
 extern int  TSXCopyPlane(Display*, Drawable, Drawable, GC, int, int, unsigned int, unsigned int, int, int, unsigned long);
 extern int  TSXDefineCursor(Display*, Window, Cursor);
+extern int  TSXDeleteProperty(Display*, Window, Atom);
 extern int  TSXDestroyWindow(Display*, Window);
 extern int  TSXDisplayKeycodes(Display*, int*, int*);
 extern int  TSXDrawArc(Display*, Drawable, GC, int, int, unsigned int, unsigned int, int, int);
diff --git a/include/x11drv.h b/include/x11drv.h
index 3429ceea0a3..796549f3749 100644
--- a/include/x11drv.h
+++ b/include/x11drv.h
@@ -169,6 +169,11 @@ extern XImage *X11DRV_BITMAP_GetXImage( const struct tagBITMAPOBJ *bmp );
 extern int X11DRV_DIB_GetXImageWidthBytes( int width, int depth );
 extern BOOL X11DRV_DIB_Init(void);
 extern X11DRV_PHYSBITMAP *X11DRV_AllocBitmap( struct tagBITMAPOBJ *bmp );
+extern HBITMAP X11DRV_BITMAP_CreateBitmapHeaderFromPixmap(Pixmap pixmap);
+extern HGLOBAL X11DRV_DIB_CreateDIBFromPixmap(Pixmap pixmap, HDC hdc, BOOL bDeletePixmap);
+extern HBITMAP X11DRV_BITMAP_CreateBitmapFromPixmap(Pixmap pixmap, BOOL bDeletePixmap);
+extern Pixmap X11DRV_DIB_CreatePixmapFromDIB( HGLOBAL hPackedDIB, HDC hdc );
+extern Pixmap X11DRV_BITMAP_CreatePixmapFromBitmap( HBITMAP hBmp, HDC hdc );
 
 extern BOOL X11DRV_SetupGCForPatBlt( struct tagDC *dc, GC gc,
 				       BOOL fMapColors );
@@ -325,11 +330,15 @@ extern void X11DRV_USER_EndDebugging(void);
 
 extern struct tagCLIPBOARD_DRIVER X11DRV_CLIPBOARD_Driver;
 
+extern void X11DRV_CLIPBOARD_FreeResources( Atom property );
+extern BOOL X11DRV_CLIPBOARD_RegisterPixmapResource( Atom property, Pixmap pixmap );
+    
 extern void X11DRV_CLIPBOARD_Acquire(void);
 extern void X11DRV_CLIPBOARD_Release(void);
 extern void X11DRV_CLIPBOARD_SetData(UINT wFormat);
 extern BOOL X11DRV_CLIPBOARD_GetData(UINT wFormat);
 extern BOOL X11DRV_CLIPBOARD_IsFormatAvailable(UINT wFormat);
+extern BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop);
 extern BOOL X11DRV_CLIPBOARD_RegisterFormat( LPCSTR FormatName );
 extern BOOL X11DRV_CLIPBOARD_IsSelectionowner();
 extern UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName);
diff --git a/objects/dib.c b/objects/dib.c
index 56f3bdf14b5..d133f602b4c 100644
--- a/objects/dib.c
+++ b/objects/dib.c
@@ -978,3 +978,87 @@ void DIB_FixColorsToLoadflags(BITMAPINFO * bmi, UINT loadflags, BYTE pix)
       } 
     }
 }
+
+/***********************************************************************
+ *           DIB_CreateDIBFromBitmap
+ *  Allocates a packed DIB and copies the bitmap data into it.
+ */
+HGLOBAL DIB_CreateDIBFromBitmap(HDC hdc, HBITMAP hBmp)
+{
+    BITMAPOBJ *pBmp = NULL;
+    HGLOBAL hPackedDIB = 0;
+    LPBYTE pPackedDIB = NULL;
+    LPBITMAPINFOHEADER pbmiHeader = NULL;
+    unsigned int width, height, depth, cDataSize = 0, cPackedSize = 0,
+                 OffsetBits = 0, nLinesCopied = 0;
+
+    /* Get a pointer to the BITMAPOBJ structure */
+    pBmp = (BITMAPOBJ *)GDI_GetObjPtr( hBmp, BITMAP_MAGIC );
+
+    /* Get the bitmap dimensions */
+    width = pBmp->bitmap.bmWidth;
+    height = pBmp->bitmap.bmHeight;
+    depth = pBmp->bitmap.bmBitsPixel;
+                 
+    /*
+     * A packed DIB contains a BITMAPINFO structure followed immediately by
+     * an optional color palette and the pixel data.
+     */
+
+    /* Calculate the size of the packed DIB */
+    cDataSize = DIB_GetDIBImageBytes( width, height, depth );
+    cPackedSize = sizeof(BITMAPINFOHEADER)
+                  + ( (depth <= 8) ? (sizeof(RGBQUAD) * (1 << depth)) : 0 )
+                  + cDataSize;
+    /* Get the offset to the bits */
+    OffsetBits = cPackedSize - cDataSize;
+
+    /* Allocate the packed DIB */
+    TRACE("\tAllocating packed DIB of size %d\n", cPackedSize);
+    hPackedDIB = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE /*| GMEM_ZEROINIT*/,
+                             cPackedSize );
+    if ( !hPackedDIB )
+    {
+        WARN("Could not allocate packed DIB!\n");
+        goto END;
+    }
+    
+    /* A packed DIB starts with a BITMAPINFOHEADER */
+    pPackedDIB = (LPBYTE)GlobalLock(hPackedDIB);
+    pbmiHeader = (LPBITMAPINFOHEADER)pPackedDIB;
+
+    /* Init the BITMAPINFOHEADER */
+    pbmiHeader->biSize = sizeof(BITMAPINFOHEADER);
+    pbmiHeader->biWidth = width;
+    pbmiHeader->biHeight = height;
+    pbmiHeader->biPlanes = 1;
+    pbmiHeader->biBitCount = depth;
+    pbmiHeader->biCompression = BI_RGB;
+    pbmiHeader->biSizeImage = 0;
+    pbmiHeader->biXPelsPerMeter = pbmiHeader->biYPelsPerMeter = 0;
+    pbmiHeader->biClrUsed = 0;
+    pbmiHeader->biClrImportant = 0;
+
+    /* Retrieve the DIB bits from the bitmap and fill in the
+     * DIB color table if present */
+    
+    nLinesCopied = GetDIBits(hdc,                       /* Handle to device context */
+                             hBmp,                      /* Handle to bitmap */
+                             0,                         /* First scan line to set in dest bitmap */
+                             height,                    /* Number of scan lines to copy */
+                             pPackedDIB + OffsetBits,   /* [out] Address of array for bitmap bits */
+                             (LPBITMAPINFO) pbmiHeader, /* [out] Address of BITMAPINFO structure */
+                             0);                        /* RGB or palette index */
+    GlobalUnlock(hPackedDIB);
+
+    /* Cleanup if GetDIBits failed */
+    if (nLinesCopied != height)
+    {
+        TRACE("\tGetDIBits returned %d. Actual lines=%d\n", nLinesCopied, height);
+        GlobalFree(hPackedDIB);
+        hPackedDIB = 0;
+    }
+
+END:
+    return hPackedDIB;
+}
diff --git a/tsx11/X11_calls b/tsx11/X11_calls
index 4864e33d499..35a70824595 100644
--- a/tsx11/X11_calls
+++ b/tsx11/X11_calls
@@ -34,6 +34,7 @@ XCreateRegion
 XCreateWindow
 XDefineCursor
 XDeleteContext
+XDeleteProperty
 XDestroyImage
 XDestroyRegion
 XDestroyWindow
diff --git a/tsx11/ts_xlib.c b/tsx11/ts_xlib.c
index 1f34fb292e7..c3364830de2 100644
--- a/tsx11/ts_xlib.c
+++ b/tsx11/ts_xlib.c
@@ -488,6 +488,17 @@ int  TSXDefineCursor(Display* a0, Window a1, Cursor a2)
   return r;
 }
 
+int  TSXDeleteProperty(Display* a0, Window a1, Atom a2)
+{
+  int  r;
+  TRACE("Call XDeleteProperty\n");
+  EnterCriticalSection( &X11DRV_CritSection );
+  r = XDeleteProperty(a0, a1, a2);
+  LeaveCriticalSection( &X11DRV_CritSection );
+  TRACE("Ret XDeleteProperty\n");
+  return r;
+}
+
 int  TSXDestroyWindow(Display* a0, Window a1)
 {
   int  r;
diff --git a/windows/clipboard.c b/windows/clipboard.c
index 1662bd31823..18c7ddf94c9 100644
--- a/windows/clipboard.c
+++ b/windows/clipboard.c
@@ -796,27 +796,28 @@ HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat )
       if( lpRender->wFormatID == CF_METAFILEPICT )
 	size = sizeof( METAFILEPICT16 );
       else
-	size = GlobalSize(lpRender->hData32);
+          size = GlobalSize(lpRender->hData32);
+      
       lpRender->hData16 = GlobalAlloc16(GMEM_ZEROINIT, size);
       if( !lpRender->hData16 )
 	ERR("(%04X) -- not enough memory in 16b heap\n", wFormat);
       else
       {
-      if( lpRender->wFormatID == CF_METAFILEPICT )
-      {
-	FIXME("\timplement function CopyMetaFilePict32to16\n");
-	FIXME("\tin the appropriate file.\n");
-#ifdef SOMEONE_IMPLEMENTED_ME
-	CopyMetaFilePict32to16( GlobalLock16(lpRender->hData16), 
-			        GlobalLock(lpRender->hData32) );
-#endif
-      }
-      else
-      {
-	memcpy( GlobalLock16(lpRender->hData16), 
-		GlobalLock(lpRender->hData32), 
-		size );
-      }
+        if( lpRender->wFormatID == CF_METAFILEPICT )
+        {
+          FIXME("\timplement function CopyMetaFilePict32to16\n");
+          FIXME("\tin the appropriate file.\n");
+  #ifdef SOMEONE_IMPLEMENTED_ME
+          CopyMetaFilePict32to16( GlobalLock16(lpRender->hData16), 
+                                  GlobalLock(lpRender->hData32) );
+  #endif
+        }
+        else
+        {
+          memcpy( GlobalLock16(lpRender->hData16), 
+                  GlobalLock(lpRender->hData32), 
+                  size );
+        }
 	GlobalUnlock16(lpRender->hData16);
 	GlobalUnlock(lpRender->hData32);
       }
diff --git a/windows/x11drv/clipboard.c b/windows/x11drv/clipboard.c
index 6636a4e0acb..3db480b2226 100644
--- a/windows/x11drv/clipboard.c
+++ b/windows/x11drv/clipboard.c
@@ -43,8 +43,6 @@
  *    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"
@@ -62,6 +60,9 @@
 #include "win.h"
 #include "windef.h"
 #include "x11drv.h"
+#include "bitmap.h"
+#include "commctrl.h"
+#include "heap.h"
 
 DEFAULT_DEBUG_CHANNEL(clipboard)
 
@@ -83,9 +84,16 @@ static Window ClipboardSelectionOwner = None;  /* The window which owns the clip
 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 */
 
+/*
+ * Dynamic pointer arrays to manage destruction of Pixmap resources
+ */
+static HDPA PropDPA = NULL;
+static HDPA PixmapDPA = NULL;
+
+
 
 /**************************************************************************
- *		X11DRV_CLIPBOARD_MapPropertyToID
+ *		X11DRV_CLIPBOARD_MapPropertyToFormat
  *
  *  Map an X selection property type atom name to a windows clipboard format ID
  */
@@ -102,10 +110,21 @@ UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName)
         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;
+    else if ( 0 == strcmp(itemFmtName, "PIXMAP")
+                ||  0 == strcmp(itemFmtName, "BITMAP") )
+    {
+        /*
+         * Return CF_DIB as first preference, if WINE is the selection owner
+         * and if CF_DIB exists in the cache.
+         * If wine dowsn't own the selection we always return CF_DIB
+         */
+        if ( !X11DRV_CLIPBOARD_IsSelectionowner() )
+            return CF_DIB;
+        else if ( CLIPBOARD_IsPresent(CF_DIB) )
+            return CF_DIB;
+        else
+            return CF_BITMAP;
+    }
 
     WARN("\tNo mapping to Windows clipboard format for property %s\n", itemFmtName);
     return 0;
@@ -185,6 +204,7 @@ BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop)
     return bRet;
 }
 
+
 /**************************************************************************
  *		X11DRV_CLIPBOARD_CacheDataFormats
  *
@@ -259,7 +279,7 @@ int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
 
     /* Read the TARGETS property contents */
     if(TSXGetWindowProperty(display, xe.xselection.requestor, xe.xselection.property,
-                            0, 0x3FFF, True, XA_ATOM, &atype, &aformat,
+                            0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
                             &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
         TRACE("\tCouldn't read TARGETS property\n");
     else
@@ -270,7 +290,7 @@ int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
         * The TARGETS property should have returned us a list of atoms
         * corresponding to each selection target format supported.
         */
-       if(atype == XA_ATOM && aformat == 32)
+       if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
        {
           int i;
           LPWINE_CLIPFORMAT lpFormat;
@@ -291,9 +311,13 @@ int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
               {
                   lpFormat = CLIPBOARD_LookupFormat( wFormat );
                   
-                  /* Don't replace if the property already cached is a native format */
-                  if (lpFormat->wDataPresent
-                      && X11DRV_CLIPBOARD_IsNativeProperty(lpFormat->drvData))
+                  /* Don't replace if the property already cached is a native format,
+                   * or if a PIXMAP is being replaced by a BITMAP.
+                   */
+                  if (lpFormat->wDataPresent &&
+                        ( X11DRV_CLIPBOARD_IsNativeProperty(lpFormat->drvData)
+                          || (lpFormat->drvData == XA_PIXMAP && targetList[i] == XA_BITMAP) )
+                     )
                   {
                       TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s (Skipped)\n",
                             i, itemFmtName, wFormat, lpFormat->Name);
@@ -326,7 +350,7 @@ int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
  *  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 BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, Atom reqFormat)
+static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, Atom reqType)
 {
     Atom	      atype=AnyPropertyType;
     int		      aformat;
@@ -336,26 +360,27 @@ static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, At
     LPWINE_CLIPFORMAT lpFormat;
     BOOL              bRet = FALSE;
     HWND              hWndClipWindow = GetOpenClipboardWindow();
+
     
     if(prop == None)
         return bRet;
 
     TRACE("Reading X selection...\n");
 
-    TRACE("\tretrieving property %s into %s\n",
-          TSXGetAtomName(display,reqFormat), TSXGetAtomName(display,prop) );
+    TRACE("\tretrieving property %s from window %ld into %s\n",
+          TSXGetAtomName(display,reqType), (long)w, 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,
+    if(TSXGetWindowProperty(display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
                             &atype, &aformat, &nitems, &itemSize, &val) != Success)
     {
         WARN("\tcouldn't get property size\n");
         return bRet;
     }
-    /* Free property if one was returned */
+
+    /* Free zero length return data if any */
     if ( val )
     {
        TSXFree(val);
@@ -365,7 +390,10 @@ static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, At
     TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
     lRequestLength = (itemSize * aformat/8)/4  + 1;
     
-    if(TSXGetWindowProperty(display,w,prop,0,lRequestLength,True,reqFormat,
+    /*
+     * Retrieve the actual property in the required X format.
+     */
+    if(TSXGetWindowProperty(display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
                             &atype, &aformat, &nitems, &remain, &val) != Success)
     {
         WARN("\tcouldn't read property\n");
@@ -378,14 +406,14 @@ static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, At
     if (remain)
     {
         WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
-        return bRet;
+        goto END;
     }
     
     /*
      * Translate the X property into the appropriate Windows clipboard
      * format, if possible.
      */
-    if ( (reqFormat == XA_STRING)
+    if ( (reqType == XA_STRING)
          && (atype == XA_STRING) && (aformat == 8) ) /* treat Unix text as CF_OEMTEXT */
     {
       HANDLE16   hText = 0;
@@ -432,16 +460,56 @@ static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, At
           bRet = TRUE;
       }
     }
-    else if ( reqFormat == XA_PIXMAP ) /* treat PIXMAP as CF_DIB or CF_BITMAP */
+    else if ( reqType == XA_PIXMAP || reqType == XA_BITMAP ) /* 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");
+      /* Get the first pixmap handle passed to us */
+      Pixmap *pPixmap = (Pixmap *)val;
+      HANDLE hTargetImage = NULL;  /* Handle to store the converted bitmap or DIB */
+      
+      if (aformat != 32 || nitems < 1 || atype != XA_PIXMAP
+          || (wFormat != CF_BITMAP && wFormat != CF_DIB))
+      {
+          WARN("\tUnimplemented format conversion request\n");
+          goto END;
+      }
+          
+      if ( wFormat == CF_BITMAP )
+      {
+        /* For CF_BITMAP requests we must return an HBITMAP */
+        hTargetImage = X11DRV_BITMAP_CreateBitmapFromPixmap(*pPixmap, TRUE);
+      }
+      else if (wFormat == CF_DIB)
+      {
+        HWND hwnd = GetOpenClipboardWindow();
+        HDC hdc = GetDC(hwnd);
+        
+        /* For CF_DIB requests we must return an HGLOBAL storing a packed DIB */
+        hTargetImage = X11DRV_DIB_CreateDIBFromPixmap(*pPixmap, hdc, TRUE);
+        
+        ReleaseDC(hdc, hwnd);
+      }
+
+      if (!hTargetImage)
+      {
+          WARN("PIXMAP conversion failed!\n" );
+          goto END;
+      }
+      
+      /* Delete previous clipboard data */
+      lpFormat = CLIPBOARD_LookupFormat(wFormat);
+      if (lpFormat->wDataPresent && (lpFormat->hData16 || lpFormat->hData32))
+          CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
+      
+      /* Update the clipboard record */
+      lpFormat->wDataPresent = 1;
+      lpFormat->hData32 = hTargetImage;
+      lpFormat->hData16 = 0;
+
+      bRet = TRUE;
     }
  
-    /* For other data types simply copy the X data without conversion */
-    else
+    /* For native properties simply copy the X data without conversion */
+    else if (X11DRV_CLIPBOARD_IsNativeProperty(reqType)) /* <WCF>* */
     {
       HANDLE hClipData = 0;
       void*  lpClipData;
@@ -475,9 +543,20 @@ static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, At
           bRet = TRUE;
       }
     }
- 
-    /* Free the retrieved property */
-    TSXFree(val);
+    else
+    {
+        WARN("\tUnimplemented format conversion request\n");
+        goto END;
+    }
+
+END:
+    /* Delete the property on the window now that we are done
+     * This will send a PropertyNotify event to the selection owner. */
+    TSXDeleteProperty(display,w,prop);
+    
+    /* Free the retrieved property data */
+    if (val)
+       TSXFree(val);
     
     return bRet;
 }
@@ -629,6 +708,24 @@ void X11DRV_CLIPBOARD_Release()
         
 	LeaveCriticalSection(&X11DRV_CritSection);
     }
+
+    /* Get rid of any Pixmap resources we may still have */
+    if (PropDPA)
+        DPA_Destroy( PropDPA );
+    if (PixmapDPA)
+    {
+      int i;
+      Pixmap pixmap;
+      for( i = 0; ; i++ )
+      {
+        if ( (pixmap = ((Pixmap)DPA_GetPtr(PixmapDPA, i))) )
+          XFreePixmap(display, pixmap);
+        else
+          break;
+      }
+      DPA_Destroy( PixmapDPA );
+    }
+    PixmapDPA = PropDPA = NULL;
 }
 
 /**************************************************************************
@@ -675,6 +772,12 @@ void X11DRV_CLIPBOARD_Acquire()
 
         if (selectionAcquired)
         {
+            /* Create dynamic pointer arrays to manage Pixmap resources we may expose */
+            if (!PropDPA)
+                PropDPA = DPA_CreateEx( 2, SystemHeap );
+            if (!PixmapDPA)
+                PixmapDPA = DPA_CreateEx( 2, SystemHeap );
+            
 	    selectionWindow = owner;
 	    TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
         }
@@ -989,4 +1092,57 @@ END:
     }
 }
 
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_RegisterPixmapResource
+ * Registers a Pixmap resource which is to be associated with a property Atom.
+ * When the property is destroyed we also destroy the Pixmap through the
+ * PropertyNotify event.
+ */
+BOOL X11DRV_CLIPBOARD_RegisterPixmapResource( Atom property, Pixmap pixmap )
+{
+  if ( -1 == DPA_InsertPtr( PropDPA, 0, (void*)property ) )
+    return FALSE;
+    
+  if ( -1 == DPA_InsertPtr( PixmapDPA, 0, (void*)pixmap ) )
+    return FALSE;
+
+  return TRUE;
+}
+
+/**************************************************************************
+ *		X11DRV_CLIPBOARD_FreeResources
+ *
+ * Called from EVENT_PropertyNotify() to give us a chance to destroy
+ * any resources associated with this property.
+ */
+void X11DRV_CLIPBOARD_FreeResources( Atom property )
+{
+    /* Do a simple linear search to see if we have a Pixmap resource
+     * associated with this property and release it.
+     */
+    int i;
+    Pixmap pixmap;
+    Atom cacheProp = NULL;
+    for( i = 0; ; i++ )
+    {
+        if ( !(cacheProp = ((Atom)DPA_GetPtr(PropDPA, i))) )
+            break;
+        
+        if ( cacheProp == property )
+        {
+            /* Lookup the associated Pixmap and free it */
+            pixmap = (Pixmap)DPA_GetPtr(PixmapDPA, i);
+  
+            TRACE("Releasing pixmap %ld for Property %s\n",
+                  (long)pixmap, TSXGetAtomName(display, cacheProp));
+            
+            XFreePixmap(display, pixmap);
+
+            /* Free the entries from the table */
+            DPA_DeletePtr(PropDPA, i);
+            DPA_DeletePtr(PixmapDPA, i);
+        }
+    }
+}
+
 #endif /* !defined(X_DISPLAY_MISSING) */
diff --git a/windows/x11drv/event.c b/windows/x11drv/event.c
index e55924b73be..d2281a3dcff 100644
--- a/windows/x11drv/event.c
+++ b/windows/x11drv/event.c
@@ -2,6 +2,7 @@
  * X11 event driver
  *
  * Copyright 1993 Alexandre Julliard
+ *	     1999 Noel Borthwick
  */
 
 #include "config.h"
@@ -79,6 +80,7 @@ static const char * const event_names[] =
   "ClientMessage", "MappingNotify"
 };
 
+
 static void CALLBACK EVENT_Flush( ULONG_PTR arg );
 static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg );
 static void EVENT_ProcessEvent( XEvent *event );
@@ -93,8 +95,9 @@ static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
 static void EVENT_Expose( HWND hWnd, XExposeEvent *event );
 static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event );
 static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event );
-static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event);
+static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple );
 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event);
+static void EVENT_PropertyNotify( XPropertyEvent *event );
 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event );
 static void EVENT_MapNotify( HWND pWnd, XMapEvent *event );
 static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event );
@@ -222,14 +225,14 @@ static void EVENT_ProcessEvent( XEvent *event )
     }
   }
 
-  if ( !hWnd && event->xany.window != X11DRV_GetXRootWindow() )
+  if ( !hWnd && event->xany.window != X11DRV_GetXRootWindow()
+             && event->type != PropertyNotify )
       ERR_(event)("Got event %s for unknown Window %08lx\n",
            event_names[event->type], event->xany.window );
   else
       TRACE_(event)("Got event %s for hwnd %04x\n",
 	     event_names[event->type], hWnd );
 
-  
   switch(event->type)
     {
     case KeyPress:
@@ -322,7 +325,7 @@ static void EVENT_ProcessEvent( XEvent *event )
 
     case SelectionRequest:
       if (!hWnd || bUserRepaintDisabled) return;
-      EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event );
+      EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
       break;
 
     case SelectionClear:
@@ -330,6 +333,10 @@ static void EVENT_ProcessEvent( XEvent *event )
       EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
       break;
       
+    case PropertyNotify:
+      EVENT_PropertyNotify( (XPropertyEvent *)event );
+      break;
+
     case ClientMessage:
       if (!hWnd || bUserRepaintDisabled) return;
       EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
@@ -833,191 +840,469 @@ static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event )
 }
 
 
+/***********************************************************************
+ *           EVENT_SelectionRequest_TARGETS
+ *  Service a TARGETS selection request event
+ */
+static Atom EVENT_SelectionRequest_TARGETS( Window requestor, Atom target, Atom rprop )
+{
+    Atom xaTargets = TSXInternAtom(display, "TARGETS", False);
+    Atom* targets;
+    Atom prop;
+    UINT wFormat;
+    unsigned long cTargets;
+    BOOL bHavePixmap;
+    int xRc;
+
+    TRACE_(event)("Request for %s\n", TSXGetAtomName(display, target));
+    
+    /*
+     * Count the number of items we wish to expose as selection targets.
+     * We include the TARGETS item, and a PIXMAP if we have CF_DIB or CF_BITMAP
+     */
+    cTargets = CountClipboardFormats() + 1;
+    if ( CLIPBOARD_IsPresent(CF_DIB) ||  CLIPBOARD_IsPresent(CF_BITMAP) )
+       cTargets++;
+    
+    /* Allocate temp buffer */
+    targets = (Atom*)HEAP_xalloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
+
+    /* Create TARGETS property list (First item in list is TARGETS itself) */
+
+    for ( targets[0] = xaTargets, cTargets = 1, wFormat = 0, bHavePixmap = FALSE;
+          (wFormat = EnumClipboardFormats( wFormat )); )
+    {
+        if ( (prop = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat)) != None )
+        {
+            /* Scan through what we have so far to avoid duplicates */
+            int i;
+            BOOL bExists;
+            for (i = 0, bExists = FALSE; i < cTargets; i++)
+            {
+                if (targets[i] == prop)
+                {
+                    bExists = TRUE;
+                    break;
+                }
+            }
+            if (!bExists)
+            {
+                targets[cTargets++] = prop;
+            
+                /* Add PIXMAP prop for bitmaps additionally */
+                if ( (wFormat == CF_DIB || wFormat == CF_BITMAP )
+                     && !bHavePixmap )
+                {
+                    targets[cTargets++] = XA_PIXMAP;
+                    bHavePixmap = TRUE;
+                }
+            }
+        }
+    }
+
+#ifdef DEBUG_RUNTIME
+{
+    int i;
+    for ( i = 0; i < cTargets; i++)
+    {
+        if (targets[i])
+        {
+          char *itemFmtName = TSXGetAtomName(display, targets[i]);
+          TRACE_(event)("\tAtom# %d:  Type %s\n", i, itemFmtName);
+          TSXFree(itemFmtName);
+        }
+    }
+}
+#endif
+    
+    /* Update the X property */
+    TRACE_(event)("\tUpdating property %s...", TSXGetAtomName(display, rprop));
+
+    /* We may want to consider setting the type to xaTargets instead,
+     * in case some apps expect this instead of XA_ATOM */
+    xRc = TSXChangeProperty(display, requestor, rprop,
+                            XA_ATOM, 32, PropModeReplace,
+                            (unsigned char *)targets, cTargets);
+    TRACE_(event)("(Rc=%d)\n", xRc);
+    
+    HeapFree( GetProcessHeap(), 0, targets );
+
+    return rprop;
+}
+
+
+/***********************************************************************
+ *           EVENT_SelectionRequest_STRING
+ *  Service a STRING selection request event
+ */
+static Atom EVENT_SelectionRequest_STRING( Window requestor, Atom target, Atom rprop )
+{
+    HANDLE16 hText;
+    LPSTR  text;
+    int    size,i,j;
+    char* lpstr = 0;
+    char *itemFmtName;
+    int xRc;
+
+    /*
+     * Map the requested X selection property type atom name to a
+     * windows clipboard format ID.
+     */
+    itemFmtName = TSXGetAtomName(display, target);
+    TRACE_(event)("Request for %s (wFormat=%x %s)\n",
+                  itemFmtName, CF_TEXT, CLIPBOARD_GetFormatName(CF_TEXT));
+    TSXFree(itemFmtName);
+
+    if ( !CLIPBOARD_IsPresent(CF_TEXT) )
+    {
+       rprop = None;
+       goto END;
+    }
+
+    hText = GetClipboardData16(CF_TEXT);
+    text = GlobalLock16(hText);
+    size = GlobalSize16(hText);
+    
+    /* remove carriage returns */
+    
+    lpstr = (char*)HEAP_xalloc( GetProcessHeap(), 0, size-- );
+    for(i=0,j=0; i < size && text[i]; i++ )
+    {
+        if( text[i] == '\r' && 
+            (text[i+1] == '\n' || text[i+1] == '\0') ) continue;
+        lpstr[j++] = text[i];
+    }
+    lpstr[j]='\0';
+    
+    /* Update the X property */
+    TRACE_(event)("\tUpdating property %s...\n", TSXGetAtomName(display, rprop));
+    xRc = TSXChangeProperty(display, requestor, rprop,
+                            XA_STRING, 8, PropModeReplace,
+                            lpstr, j);
+    TRACE_(event)("(Rc=%d)\n", xRc);
+
+    GlobalUnlock16(hText);
+    HeapFree( GetProcessHeap(), 0, lpstr );
+
+END:
+    return rprop;
+}
+
+/***********************************************************************
+ *           EVENT_SelectionRequest_PIXMAP
+ *  Service a PIXMAP selection request event
+ */
+static Atom EVENT_SelectionRequest_PIXMAP( Window requestor, Atom target, Atom rprop )
+{
+    HANDLE hClipData = 0;
+    Pixmap pixmap = NULL;
+    UINT   wFormat;
+    char * itemFmtName;
+    int xRc;
+#if(0)
+    XSetWindowAttributes win_attr;
+    XWindowAttributes win_attr_src;
+#endif
+    
+    /*
+     * Map the requested X selection property type atom name to a
+     * windows clipboard format ID.
+     */
+    itemFmtName = TSXGetAtomName(display, target);
+    wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
+    TRACE_(event)("Request for %s (wFormat=%x %s)\n",
+                  itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
+    TSXFree(itemFmtName);
+    
+    hClipData = GetClipboardData(wFormat);
+    if ( !hClipData )
+    {
+        TRACE_(event)("Could not retrieve a Pixmap compatible format from clipboard!\n");
+        rprop = None; /* Fail the request */
+        goto END;
+    }
+
+    if (wFormat == CF_DIB)
+    {
+        HWND hwnd = GetOpenClipboardWindow();
+        HDC hdc = GetDC(hwnd);
+        
+        /* For convert from packed DIB to Pixmap */
+        pixmap = X11DRV_DIB_CreatePixmapFromDIB(hClipData, hdc);
+        
+        ReleaseDC(hdc, hwnd);
+    }
+    else if (wFormat == CF_BITMAP)
+    {
+        HWND hwnd = GetOpenClipboardWindow();
+        HDC hdc = GetDC(hwnd);
+        
+        pixmap = X11DRV_BITMAP_CreatePixmapFromBitmap(hClipData, hdc);
+
+        ReleaseDC(hdc, hwnd);
+    }
+    else
+    {
+        FIXME_(event)("%s to PIXMAP conversion not yet implemented!\n",
+                      CLIPBOARD_GetFormatName(wFormat));
+        rprop = None;
+        goto END;
+    }
+
+    TRACE_(event)("\tUpdating property %s on Window %ld with %s %ld...\n",
+                  TSXGetAtomName(display, rprop), (long)requestor,
+                  TSXGetAtomName(display, target),
+                  pixmap);
+
+    /* Store the Pixmap handle in the property */
+    xRc = TSXChangeProperty(display, requestor, rprop, target, 
+                            32, PropModeReplace,
+                            (unsigned char *)&pixmap, 1);
+    TRACE_(event)("(Rc=%d)\n", xRc);
+
+    /* Enable the code below if you want to handle destroying Pixmap resources
+     * in response to property notify events. Clients like XPaint don't
+     * appear to be duplicating Pixmaps so they don't like us deleting,
+     * the resource in response to the property being deleted.
+     */
+#if(0)
+    /* Express interest in property notify events so that we can delete the
+     * pixmap when the client deletes the property atom.
+     */
+    xRc = TSXGetWindowAttributes(display, requestor, &win_attr_src);
+    TRACE_(event)("Turning on PropertyChangeEvent notifications from window %ld\n",
+                 (long)requestor);
+    win_attr.event_mask = win_attr_src.your_event_mask | PropertyChangeMask;
+    TSXChangeWindowAttributes(display, requestor, CWEventMask, &win_attr);
+
+    /* Register the Pixmap we created with the request property Atom.
+     * When this property is destroyed we also destroy the Pixmap in
+     * response to the PropertyNotify event.
+     */
+    X11DRV_CLIPBOARD_RegisterPixmapResource( rprop, pixmap );
+#endif
+    
+END:
+    return rprop;
+}
+
+
+/***********************************************************************
+ *           EVENT_SelectionRequest_WCF
+ *  Service a Wine Clipboard Format selection request event.
+ *  For <WCF>* data types we simply copy the data to X without conversion.
+ */
+static Atom EVENT_SelectionRequest_WCF( Window requestor, Atom target, Atom rprop )
+{
+    HANDLE hClipData = 0;
+    void*  lpClipData;
+    UINT   wFormat;
+    char * itemFmtName;
+    int cBytes;
+    int xRc;
+    
+    /*
+     * Map the requested X selection property type atom name to a
+     * windows clipboard format ID.
+     */
+    itemFmtName = TSXGetAtomName(display, target);
+    wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
+    TRACE_(event)("Request for %s (wFormat=%x %s)\n",
+                  itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
+    TSXFree(itemFmtName);
+    
+    hClipData = GetClipboardData16(wFormat);
+    
+    if( hClipData && (lpClipData = GlobalLock16(hClipData)) )
+    {
+        cBytes = GlobalSize16(hClipData);
+        
+        TRACE_(event)("\tUpdating property %s, %d bytes...\n",
+                      TSXGetAtomName(display, rprop), cBytes);
+        
+        xRc = TSXChangeProperty(display, requestor, rprop,
+                                target, 8, PropModeReplace,
+                                (unsigned char *)lpClipData, cBytes);
+        TRACE_(event)("(Rc=%d)\n", xRc);
+        
+        GlobalUnlock16(hClipData);
+    }
+    else
+    {
+        TRACE_(event)("\tCould not retrieve native format!\n");
+        rprop = None; /* Fail the request */
+    }
+    
+    return rprop;
+}
+
+
+/***********************************************************************
+ *           EVENT_SelectionRequest_MULTIPLE
+ *  Service a MULTIPLE selection request event
+ *  rprop contains a list of (target,property) atom pairs.
+ *  The first atom names a target and the second names a property.
+ *  The effect is as if we have received a sequence of SelectionRequest events
+ *  (one for each atom pair) except that:
+ *  1. We reply with a SelectionNotify only when all the requested conversions
+ *  have been performed.
+ *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
+ *  we replace the atom in the property by None.
+ */
+static Atom EVENT_SelectionRequest_MULTIPLE( HWND hWnd, XSelectionRequestEvent *pevent )
+{
+    Atom           rprop;
+    Atom           atype=AnyPropertyType;
+    int		   aformat;
+    unsigned long  remain;
+    Atom*	   targetPropList=NULL;
+    unsigned long  cTargetPropList = 0;
+/*  Atom           xAtomPair = TSXInternAtom(display, "ATOM_PAIR", False); */
+    
+   /* If the specified property is None the requestor is an obsolete client.
+    * We support these by using the specified target atom as the reply property.
+    */
+    rprop = pevent->property;
+    if( rprop == None ) 
+        rprop = pevent->target;
+    if (!rprop)
+        goto END;
+
+    /* Read the MULTIPLE property contents. This should contain a list of
+     * (target,property) atom pairs.
+     */
+    if(TSXGetWindowProperty(display, pevent->requestor, rprop,
+                            0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
+                            &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
+        TRACE_(event)("\tCouldn't read MULTIPLE property\n");
+    else
+    {
+       TRACE_(event)("\tType %s,Format %d,nItems %ld, Remain %ld\n",
+                     TSXGetAtomName(display,atype),aformat,cTargetPropList,remain);
+
+       /*
+        * Make sure we got what we expect.
+        * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
+        * in a MULTIPLE selection request should be of type ATOM_PAIR.
+        * However some X apps(such as XPaint) are not compliant with this and return
+        * a user defined atom in atype when XGetWindowProperty is called.
+        * The data *is* an atom pair but is not denoted as such.
+        */
+       if(aformat == 32 /* atype == xAtomPair */ )
+       {
+          int i;
+          
+          /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
+           * for each (target,property) pair */
+
+          for (i = 0; i < cTargetPropList; i+=2)
+          {
+              char *targetName = TSXGetAtomName(display, targetPropList[i]);
+              char *propName = TSXGetAtomName(display, targetPropList[i+1]);
+              XSelectionRequestEvent event;
+
+              TRACE_(event)("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
+              TSXFree(targetName);
+              TSXFree(propName);
+              
+              /* We must have a non "None" property to service a MULTIPLE target atom */
+              if ( !targetPropList[i+1] )
+              {
+                  TRACE_(event)("\tMULTIPLE(%d): Skipping target with empty property!", i);
+                  continue;
+              }
+              
+              /* Set up an XSelectionRequestEvent for this (target,property) pair */
+              memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
+              event.target = targetPropList[i];
+              event.property = targetPropList[i+1];
+                  
+              /* Fire a SelectionRequest, informing the handler that we are processing
+               * a MULTIPLE selection request event.
+               */
+              EVENT_SelectionRequest( hWnd, &event, TRUE );
+          }
+       }
+
+       /* Free the list of targets/properties */
+       TSXFree(targetPropList);
+    }
+
+END:
+    return rprop;
+}
+
+
 /***********************************************************************
  *           EVENT_SelectionRequest
+ *  Process an event selection request event.
+ *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
+ *  recursively while servicing a "MULTIPLE" selection target.
+ *
  *  Note: We only receive this event when WINE owns the X selection
  */
-static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event )
+static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple )
 {
   XSelectionEvent result;
   Atom 	          rprop = None;
   Window          request = event->requestor;
-  UINT            wFormat;
-  char *          itemFmtName;
   BOOL            couldOpen = FALSE;
-  Atom            xaClipboard = XInternAtom(display, "CLIPBOARD", False);
-  Atom            xaTargets = XInternAtom(display, "TARGETS", False);
-  int             xRc;
+  Atom            xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
+  Atom            xaTargets = TSXInternAtom(display, "TARGETS", False);
+  Atom            xaMultiple = TSXInternAtom(display, "MULTIPLE", False);
 
   /*
-   * Map the requested X selection property type atom name to a
-   * windows clipboard format ID.
+   * We can only handle the selection request if :
+   * The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard.
+   * Don't do these checks or open the clipboard while recursively processing MULTIPLE,
+   * since this has been already done.
    */
-  itemFmtName = TSXGetAtomName(display, event->target);
-  wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
-  TRACE_(event)("Request for %s\n", itemFmtName);
-  TSXFree(itemFmtName);
+  if ( !bIsMultiple )
+  {
+    if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) )
+        || !(couldOpen = OpenClipboard(hWnd)) )
+       goto END;
+  }
 
-  /*
-   * We can only handle the selection request if :
-   * The selection is PRIMARY or CLIPBOARD,
-   * AND we can successfully open the clipboard.
-   * AND we have a request for a TARGETS selection target,
-   *     OR the requested format is available in the clipboard
+  /* If the specified property is None the requestor is an obsolete client.
+   * We support these by using the specified target atom as the reply property.
    */
-  if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) )
-      || !(couldOpen = OpenClipboard(hWnd)) )
-     goto END;
-  if ( (event->target != xaTargets)
-       && ( (0 == wFormat) || !CLIPBOARD_IsPresent(wFormat) ) )
-     goto END;
-
-  /* We can handle the request */
-       
   rprop = event->property;
-  
   if( rprop == None ) 
       rprop = event->target;
   
   if(event->target == xaTargets)  /*  Return a list of all supported targets */
   {
-      Atom* targets;
-      Atom prop;
-      UINT wFormat;
-      unsigned long cTargets;
-      BOOL bHavePixmap;
-
-      /*
-       * Count the number of items we wish to expose as selection targets.
-       * We include the TARGETS item, and a PIXMAP if we have CF_DIB or CF_BITMAP
-       */
-      cTargets = CountClipboardFormats() + 1;
-      if ( CLIPBOARD_IsPresent(CF_DIB) ||  CLIPBOARD_IsPresent(CF_BITMAP) )
-         cTargets++;
-      
-      /* Allocate temp buffer */
-      targets = (Atom*)HEAP_xalloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
-
-      /* Create TARGETS property list (First item in list is TARGETS itself) */
-
-      for ( targets[0] = xaTargets, cTargets = 1, wFormat = 0, bHavePixmap = FALSE;
-            (wFormat = EnumClipboardFormats( wFormat )); )
-      {
-          if ( (prop = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat)) != None )
-          {
-              /* Scan through what we have so far to avoid duplicates */
-              int i;
-              BOOL bExists;
-              for (i = 0, bExists = FALSE; i < cTargets; i++)
-              {
-                  if (targets[i] == prop)
-                  {
-                      bExists = TRUE;
-                      break;
-                  }
-              }
-              if (!bExists)
-              {
-                  targets[cTargets++] = prop;
-              
-                  /* Add PIXMAP prop for bitmaps additionally */
-                  if ( (wFormat == CF_DIB || wFormat == CF_BITMAP )
-                       && !bHavePixmap )
-                  {
-                      targets[cTargets++] = XA_PIXMAP;
-                      bHavePixmap = TRUE;
-                  }
-              }
-          }
-      }
-
-#ifdef DEBUG_RUNTIME
-{
-      int i;
-      for ( i = 0; i < cTargets; i++)
-      {
-          if (targets[i])
-          {
-            char *itemFmtName = TSXGetAtomName(display, targets[i]);
-            TRACE_(event)("\tAtom# %d:  Type %s\n", i, itemFmtName);
-            TSXFree(itemFmtName);
-          }
-      }
-}
-#endif
-      
-      /* Update the X property */
-      TRACE_(event)("\tUpdating property %s...", TSXGetAtomName(display, rprop));
-      xRc = TSXChangeProperty(display, request, rprop,
-                              XA_ATOM, 32, PropModeReplace,
-                              (unsigned char *)targets, cTargets);
-      TRACE_(event)("(Rc=%d)\n", xRc);
-      
-      HeapFree( GetProcessHeap(), 0, targets );
+      /* TARGETS selection request */
+      rprop = EVENT_SelectionRequest_TARGETS( request, event->target, rprop );
+  }
+  else if(event->target == xaMultiple)  /*  rprop contains a list of (target, property) atom pairs */
+  {
+      /* MULTIPLE selection request */
+      rprop = EVENT_SelectionRequest_MULTIPLE(  hWnd, event );
   }
   else if(event->target == XA_STRING)  /* treat CF_TEXT as Unix text */
   {
-      HANDLE16 hText;
-      LPSTR  text;
-      int    size,i,j;
-      
-      char* lpstr = 0;
-      
-      hText = GetClipboardData16(CF_TEXT);
-      text = GlobalLock16(hText);
-      size = GlobalSize16(hText);
-      
-      /* remove carriage returns */
-      
-      lpstr = (char*)HEAP_xalloc( GetProcessHeap(), 0, size-- );
-      for(i=0,j=0; i < size && text[i]; i++ )
-      {
-          if( text[i] == '\r' && 
-              (text[i+1] == '\n' || text[i+1] == '\0') ) continue;
-          lpstr[j++] = text[i];
-      }
-      lpstr[j]='\0';
-      
-      /* Update the X property */
-      TRACE_(event)("\tUpdating property %s...\n", TSXGetAtomName(display, rprop));
-      xRc = TSXChangeProperty(display, request, rprop, 
-                              XA_STRING, 8, PropModeReplace,
-                              lpstr, j);
-      TRACE_(event)("(Rc=%d)\n", xRc);
-
-      GlobalUnlock16(hText);
-      HeapFree( GetProcessHeap(), 0, lpstr );
+      /* XA_STRING selection request */
+      rprop = EVENT_SelectionRequest_STRING( request, event->target, rprop );
   }
   else if(event->target == XA_PIXMAP)  /*  Convert DIB's to Pixmaps */
   {
-      FIXME_(event)("DIB to PIXMAP conversion not yet implemented!\n");
-      rprop = None;
+      /* XA_PIXMAP selection request */
+      rprop = EVENT_SelectionRequest_PIXMAP( request, event->target, rprop );
   }
-  else  /* For other data types (WCF_*) simply copy the data to X without conversion */
+  else if(event->target == XA_BITMAP)  /*  Convert DIB's to 1-bit Pixmaps */
   {
-      HANDLE hClipData = 0;
-      void*  lpClipData;
-      int cBytes;
-      
-      hClipData = GetClipboardData16(wFormat);
-      
-      if( hClipData && (lpClipData = GlobalLock16(hClipData)) )
-      {
-          cBytes = GlobalSize16(hClipData);
-          
-          TRACE_(event)("\tUpdating property %s, %d bytes...\n",
-                        TSXGetAtomName(display, rprop), cBytes);
-          
-          xRc = TSXChangeProperty(display, request, rprop, 
-                                  event->target, 8, PropModeReplace,
-                                  (unsigned char *)lpClipData, cBytes);
-          TRACE_(event)("(Rc=%d)\n", xRc);
-          
-          GlobalUnlock16(hClipData);
-      }
-      else
-          rprop = None; /* Fail the request */
+      /* XA_BITMAP selection request - TODO: create a monochrome Pixmap */
+      rprop = EVENT_SelectionRequest_PIXMAP( request, XA_PIXMAP, rprop );
+  }
+  else if(X11DRV_CLIPBOARD_IsNativeProperty(event->target)) /* <WCF>* */
+  {
+      /* All <WCF> selection requests */
+      rprop = EVENT_SelectionRequest_WCF( request, event->target, rprop );
   }
+  else
+      rprop = None;  /* Don't support this format */
 
 END:
   /* close clipboard only if we opened before */
@@ -1026,16 +1311,21 @@ END:
   if( rprop == None) 
       TRACE_(event)("\tRequest ignored\n");
 
-  /* reply to sender */
-  
-  result.type = SelectionNotify;
-  result.display = display;
-  result.requestor = request;
-  result.selection = event->selection;
-  result.property = rprop;
-  result.target = event->target;
-  result.time = event->time;
-  TSXSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
+  /* reply to sender 
+   * SelectionNotify should be sent only at the end of a MULTIPLE request
+   */
+  if ( !bIsMultiple )
+  {
+    result.type = SelectionNotify;
+    result.display = display;
+    result.requestor = request;
+    result.selection = event->selection;
+    result.property = rprop;
+    result.target = event->target;
+    result.time = event->time;
+    TRACE_(event)("Sending SelectionNotify event...\n");
+    TSXSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
+  }
 }
 
 /***********************************************************************
@@ -1043,12 +1333,45 @@ END:
  */
 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event )
 {
-  Atom xaClipboard = XInternAtom(display, "CLIPBOARD", False);
+  Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
     
   if (event->selection == XA_PRIMARY || event->selection == xaClipboard)
       X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd );
 }
 
+/***********************************************************************
+ *           EVENT_PropertyNotify
+ *   We use this to release resources like Pixmaps when a selection
+ *   client no longer needs them.
+ */
+static void EVENT_PropertyNotify( XPropertyEvent *event )
+{
+  /* Check if we have any resources to free */
+  TRACE_(event)("Received PropertyNotify event: ");
+
+  switch(event->state)
+  {
+    case PropertyDelete:
+    {
+      TRACE_(event)("\tPropertyDelete for atom %s on window %ld\n",
+                    TSXGetAtomName(event->display, event->atom), (long)event->window);
+      
+      if (X11DRV_CLIPBOARD_IsSelectionowner())
+          X11DRV_CLIPBOARD_FreeResources( event->atom );
+      break;
+    }
+
+    case PropertyNewValue:
+    {
+      TRACE_(event)("\tPropertyNewValue for atom %s on window %ld\n\n",
+                    TSXGetAtomName(event->display, event->atom), (long)event->window);
+      break;
+    }
+    
+    default:
+      break;
+  }
+}
 
 /**********************************************************************
  *           EVENT_DropFromOffix
-- 
GitLab