- 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.
diff --git a/windows/x11drv/clipboard.c b/windows/x11drv/clipboard.c
index 6636a4e..3db480b 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 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 @@
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 @@
return bRet;
}
+
/**************************************************************************
* X11DRV_CLIPBOARD_CacheDataFormats
*
@@ -259,7 +279,7 @@
/* 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 @@
* 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 @@
{
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+/**************************************************************************
+ * 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) */