From d05b7beb5a17f4747dd2cb88b0b5a8ab57a16edf Mon Sep 17 00:00:00 2001 From: Noel Borthwick 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)) /* * */ { 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 * 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; - - /* - * Map the requested X selection property type atom name to a - * windows clipboard format ID. - */ - itemFmtName = TSXGetAtomName(display, event->target); - wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName); - TRACE_(event)("Request for %s\n", itemFmtName); - TSXFree(itemFmtName); + Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False); + Atom xaTargets = TSXInternAtom(display, "TARGETS", False); + Atom xaMultiple = TSXInternAtom(display, "MULTIPLE", False); /* * 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 + * 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. */ - if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) ) - || !(couldOpen = OpenClipboard(hWnd)) ) - goto END; - if ( (event->target != xaTargets) - && ( (0 == wFormat) || !CLIPBOARD_IsPresent(wFormat) ) ) - goto END; + if ( !bIsMultiple ) + { + if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) ) + || !(couldOpen = OpenClipboard(hWnd)) ) + goto END; + } - /* We can handle the request */ - + /* 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 = 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)) /* * */ + { + /* All 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