From 2970067b146fb59e5a7b8a3b1a855029ee6cc3a5 Mon Sep 17 00:00:00 2001 From: Noel Borthwick Date: Fri, 3 Sep 1999 15:17:57 +0000 Subject: [PATCH] - Implement interprocess clipboard communication. - Support for the PRIMARY and CLIPBOARD selection atoms. - Support for the TARGETS selection format. - Expose native Windows clipboard formats through X selection targets. --- include/clipboard.h | 21 +- include/ttydrv.h | 6 +- include/x11drv.h | 10 +- ole/clipboard.c | 21 +- windows/clipboard.c | 748 +++++++++++++++++--------- windows/ttydrv/clipboard.c | 40 +- windows/ttydrv/init.c | 6 +- windows/user.c | 10 +- windows/x11drv/clipboard.c | 1016 +++++++++++++++++++++++++++++------- windows/x11drv/event.c | 222 ++++++-- windows/x11drv/init.c | 6 +- 11 files changed, 1627 insertions(+), 479 deletions(-) diff --git a/include/clipboard.h b/include/clipboard.h index 7aebde970b4..f6439fb0093 100644 --- a/include/clipboard.h +++ b/include/clipboard.h @@ -10,26 +10,35 @@ typedef struct tagWINE_CLIPFORMAT { WORD wRefCount; WORD wDataPresent; LPSTR Name; + HANDLE16 hData16; HANDLE hDataSrc32; HANDLE hData32; - DWORD BufSize; + ULONG drvData; struct tagWINE_CLIPFORMAT *PrevFormat; struct tagWINE_CLIPFORMAT *NextFormat; - HANDLE16 hData16; } WINE_CLIPFORMAT, *LPWINE_CLIPFORMAT; typedef struct tagCLIPBOARD_DRIVER { - void (*pEmpty)(void); - void (*pSetData)(UINT); - BOOL (*pGetData)(UINT); + void (*pAcquire)(void); /* Acquire selection */ + void (*pRelease)(void); /* Release selection */ + void (*pSetData)(UINT); /* Set specified selection data */ + BOOL (*pGetData)(UINT); /* Get specified selection data */ + BOOL (*pIsFormatAvailable)(UINT); /* Check if specified format is available */ + BOOL (*pRegisterFormat)(LPCSTR); /* Register a clipboard format */ + BOOL (*pIsSelectionOwner)(void); /* Check if we own the selection */ void (*pResetOwner)(struct tagWND *, BOOL); } CLIPBOARD_DRIVER; extern CLIPBOARD_DRIVER *CLIPBOARD_Driver; -extern void CLIPBOARD_ResetLock(HQUEUE16 hqRef, HQUEUE16 hqNew); +extern LPWINE_CLIPFORMAT CLIPBOARD_LookupFormat( WORD wID ); +extern BOOL CLIPBOARD_IsCacheRendered(); extern void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange); +extern void CLIPBOARD_EmptyCache( BOOL bChange ); extern BOOL CLIPBOARD_IsPresent(WORD wFormat); +extern char * CLIPBOARD_GetFormatName(UINT wFormat); +extern void CLIPBOARD_ReleaseOwner(); + #endif /* __WINE_CLIPBOARD_H */ diff --git a/include/ttydrv.h b/include/ttydrv.h index 687a6ce4f7c..273d432a8bd 100644 --- a/include/ttydrv.h +++ b/include/ttydrv.h @@ -73,9 +73,13 @@ extern void TTYDRV_USER_EndDebugging(void); extern struct tagCLIPBOARD_DRIVER TTYDRV_CLIPBOARD_Driver; -extern void TTYDRV_CLIPBOARD_Empty(void); +extern void TTYDRV_CLIPBOARD_Acquire(void); +extern void TTYDRV_CLIPBOARD_Release(void); extern void TTYDRV_CLIPBOARD_SetData(UINT wFormat); extern BOOL TTYDRV_CLIPBOARD_GetData(UINT wFormat); +extern BOOL TTYDRV_CLIPBOARD_IsFormatAvailable(UINT wFormat); +extern BOOL TTYDRV_CLIPBOARD_RegisterFormat( LPCSTR FormatName ); +extern BOOL TTYDRV_CLIPBOARD_IsSelectionowner(); extern void TTYDRV_CLIPBOARD_ResetOwner(struct tagWND *pWnd, BOOL bFooBar); /* TTY desktop driver */ diff --git a/include/x11drv.h b/include/x11drv.h index e6921ce8846..e02f73ca19b 100644 --- a/include/x11drv.h +++ b/include/x11drv.h @@ -313,11 +313,17 @@ extern void X11DRV_USER_EndDebugging(void); extern struct tagCLIPBOARD_DRIVER X11DRV_CLIPBOARD_Driver; -extern void X11DRV_CLIPBOARD_Empty(void); +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_RegisterFormat( LPCSTR FormatName ); +extern BOOL X11DRV_CLIPBOARD_IsSelectionowner(); +extern UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName); +extern Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT id); extern void X11DRV_CLIPBOARD_ResetOwner(struct tagWND *pWnd, BOOL bFooBar); -extern void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd); +extern void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd); /* X11 desktop driver */ diff --git a/ole/clipboard.c b/ole/clipboard.c index 05b0a1b753e..3d4ed262bae 100644 --- a/ole/clipboard.c +++ b/ole/clipboard.c @@ -827,7 +827,7 @@ LRESULT CALLBACK OLEClipbrd_WndProc WARN("(): WM_RENDERALLFORMATS failed to retrieve EnumFormatEtc!\n"); return 0; } - + while ( S_OK == IEnumFORMATETC_Next(penumFormatetc, 1, &rgelt, NULL) ) { if ( rgelt.tymed == TYMED_HGLOBAL ) @@ -1093,6 +1093,9 @@ static HRESULT WINAPI OLEClipbrd_IDataObject_GetData( STGMEDIUM* pmedium) { HANDLE hData = 0; + BOOL bClipboardOpen = FALSE; + HRESULT hr = S_OK; + /* * Declare "This" pointer */ @@ -1124,8 +1127,11 @@ static HRESULT WINAPI OLEClipbrd_IDataObject_GetData( */ /* - * Otherwise, delegate to the Windows clipboard function GetClipboardData + * Otherwise, get the data from the windows clipboard using GetClipboardData */ + if ( !(bClipboardOpen = OpenClipboard(theOleClipboard->hWndClipboard)) ) + HANDLE_ERROR( CLIPBRD_E_CANT_OPEN ); + hData = GetClipboardData(pformatetcIn->cfFormat); /* @@ -1135,6 +1141,17 @@ static HRESULT WINAPI OLEClipbrd_IDataObject_GetData( pmedium->u.hGlobal = (HGLOBAL)hData; pmedium->pUnkForRelease = NULL; + hr = S_OK; + +CLEANUP: + /* + * Close Windows clipboard + */ + if ( bClipboardOpen && !CloseClipboard() ) + hr = CLIPBRD_E_CANT_CLOSE; + + if ( FAILED(hr) ) + return hr; return (hData == 0) ? DV_E_FORMATETC : S_OK; } diff --git a/windows/clipboard.c b/windows/clipboard.c index 542b02c715b..1662bd31823 100644 --- a/windows/clipboard.c +++ b/windows/clipboard.c @@ -1,8 +1,19 @@ /* - * WINE clipboard function handling + * WIN32 clipboard implementation * * Copyright 1994 Martin Ayotte * 1996 Alex Korobka + * 1999 Noel Borthwick + * + * NOTES: + * This file contains the implementation for the WIN32 Clipboard API + * and Wine's internal clipboard cache. + * The actual contents of the clipboard are held in the clipboard cache. + * The internal implementation talks to a "clipboard driver" to fill or + * expose the cache to the native device. (Currently only the X11 and + * TTY clipboard driver are available) + * + * TODO: * */ @@ -14,9 +25,11 @@ #include #include "winuser.h" #include "wine/winuser16.h" +#include "wine/winbase16.h" #include "heap.h" -#include "task.h" #include "message.h" +#include "task.h" +#include "queue.h" #include "clipboard.h" #include "xmalloc.h" #include "debugtools.h" @@ -26,39 +39,53 @@ DEFAULT_DEBUG_CHANNEL(clipboard) #define CF_REGFORMATBASE 0xC000 /************************************************************************** - * internal variables + * Clipboard context global variables */ CLIPBOARD_DRIVER *CLIPBOARD_Driver = NULL; -static HQUEUE16 hqClipLock = 0; +static HANDLE hClipLock = 0; static BOOL bCBHasChanged = FALSE; -HWND hWndClipOwner = 0; /* current clipboard owner */ -HWND hWndClipWindow = 0; /* window that opened clipboard */ +HWND hWndClipWindow = 0; /* window that last opened clipboard */ +HWND hWndClipOwner = 0; /* current clipboard owner */ +HANDLE16 hTaskClipOwner = 0; /* clipboard owner's task */ static HWND hWndViewer = 0; /* start of viewers chain */ static WORD LastRegFormat = CF_REGFORMATBASE; +/* Clipboard cache initial data. + * WARNING: This data ordering is dependendent on the WINE_CLIPFORMAT structure + * declared in clipboard.h + */ WINE_CLIPFORMAT ClipFormats[16] = { - { CF_TEXT, 1, 0, "Text", (HANDLE)NULL, (HANDLE)NULL, 0, NULL, &ClipFormats[1] , (HANDLE16)NULL}, - { CF_BITMAP, 1, 0, "Bitmap", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[0], &ClipFormats[2] , (HANDLE16)NULL}, - { CF_METAFILEPICT, 1, 0, "MetaFile Picture", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[1], &ClipFormats[3] , (HANDLE16)NULL}, - { CF_SYLK, 1, 0, "Sylk", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[2], &ClipFormats[4] , (HANDLE16)NULL}, - { CF_DIF, 1, 0, "DIF", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[3], &ClipFormats[5] , (HANDLE16)NULL}, - { CF_TIFF, 1, 0, "TIFF", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[4], &ClipFormats[6] , (HANDLE16)NULL}, - { CF_OEMTEXT, 1, 0, "OEM Text", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[5], &ClipFormats[7] , (HANDLE16)NULL}, - { CF_DIB, 1, 0, "DIB", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[6], &ClipFormats[8] , (HANDLE16)NULL}, - { CF_PALETTE, 1, 0, "Palette", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[7], &ClipFormats[9] , (HANDLE16)NULL}, - { CF_PENDATA, 1, 0, "PenData", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[8], &ClipFormats[10] , (HANDLE16)NULL}, - { CF_RIFF, 1, 0, "RIFF", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[9], &ClipFormats[11] , (HANDLE16)NULL}, - { CF_WAVE, 1, 0, "Wave", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[10], &ClipFormats[12] , (HANDLE16)NULL}, - { CF_OWNERDISPLAY, 1, 0, "Owner Display", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[11], &ClipFormats[13] , (HANDLE16)NULL}, - { CF_DSPTEXT, 1, 0, "DSPText", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[12], &ClipFormats[14] , (HANDLE16)NULL}, - { CF_DSPMETAFILEPICT, 1, 0, "DSPMetaFile Picture", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[13], &ClipFormats[15] , (HANDLE16)NULL}, - { CF_DSPBITMAP, 1, 0, "DSPBitmap", (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[14], NULL , (HANDLE16)NULL} + { CF_TEXT, 1, 0, "Text", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, NULL, &ClipFormats[1]}, + { CF_BITMAP, 1, 0, "Bitmap", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[0], &ClipFormats[2]}, + { CF_METAFILEPICT, 1, 0, "MetaFile Picture", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[1], &ClipFormats[3]}, + { CF_SYLK, 1, 0, "Sylk", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[2], &ClipFormats[4]}, + { CF_DIF, 1, 0, "DIF", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[3], &ClipFormats[5]}, + { CF_TIFF, 1, 0, "TIFF", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[4], &ClipFormats[6]}, + { CF_OEMTEXT, 1, 0, "OEM Text", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[5], &ClipFormats[7]}, + { CF_DIB, 1, 0, "DIB", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[6], &ClipFormats[8]}, + { CF_PALETTE, 1, 0, "Palette", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[7], &ClipFormats[9]}, + { CF_PENDATA, 1, 0, "PenData", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[8], &ClipFormats[10]}, + { CF_RIFF, 1, 0, "RIFF", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[9], &ClipFormats[11]}, + { CF_WAVE, 1, 0, "Wave", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[10], &ClipFormats[12]}, + { CF_OWNERDISPLAY, 1, 0, "Owner Display", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[11], &ClipFormats[13]}, + { CF_DSPTEXT, 1, 0, "DSPText", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[12], &ClipFormats[14]}, + { CF_DSPMETAFILEPICT, 1, 0, "DSPMetaFile Picture", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[13], &ClipFormats[15]}, + { CF_DSPBITMAP, 1, 0, "DSPBitmap", (HANDLE16)NULL, (HANDLE)NULL, (HANDLE)NULL, 0, &ClipFormats[14], NULL} }; + +/************************************************************************** + * Internal Clipboard implementation methods + **************************************************************************/ + + +/************************************************************************** + * CLIPBOARD_LookupFormat + */ static LPWINE_CLIPFORMAT __lookup_format( LPWINE_CLIPFORMAT lpFormat, WORD wID ) { while(TRUE) @@ -70,25 +97,83 @@ static LPWINE_CLIPFORMAT __lookup_format( LPWINE_CLIPFORMAT lpFormat, WORD wID ) return lpFormat; } -/************************************************************************** - * CLIPBOARD_ResetLock - */ -void CLIPBOARD_ResetLock( HQUEUE16 hqCurrent, HQUEUE16 hqNew ) +LPWINE_CLIPFORMAT CLIPBOARD_LookupFormat( WORD wID ) { - if( hqClipLock == hqCurrent ) - { - if( hqNew ) - hqClipLock = hqNew; - else - { - hWndClipOwner = 0; - hWndClipWindow = 0; - EmptyClipboard(); - hqClipLock = 0; - } - } + return __lookup_format( ClipFormats, wID ); } +/************************************************************************** + * CLIPBOARD_IsLocked + * Check if the clipboard cache is available to the caller + */ +BOOL CLIPBOARD_IsLocked() +{ + BOOL bIsLocked = TRUE; + HANDLE16 hTaskCur = GetCurrentTask(); + + /* + * The clipboard is available: + * 1. if the caller's task has opened the clipboard, + * or + * 2. if the caller is the clipboard owners task, AND is responding to a + * WM_RENDERFORMAT message. + */ + if ( hClipLock == hTaskCur ) + bIsLocked = FALSE; + + else if ( hTaskCur == hTaskClipOwner ) + { + /* Check if we're currently executing inside a window procedure + * called in response to a WM_RENDERFORMAT message. A WM_RENDERFORMAT + * handler is not permitted to open the clipboard since it has been opened + * by another client. However the handler must have access to the + * clipboard in order to update data in response to this message. + */ + MESSAGEQUEUE *queue = QUEUE_Lock( GetFastQueue16() ); + + if ( queue + && queue->smWaiting + && queue->smWaiting->msg == WM_RENDERFORMAT + && queue->smWaiting->hSrcQueue + ) + bIsLocked = FALSE; + + QUEUE_Unlock( queue ); + } + + return bIsLocked; +} + +/************************************************************************** + * CLIPBOARD_ReleaseOwner + * Gives up ownership of the clipboard + */ +void CLIPBOARD_ReleaseOwner() +{ + hWndClipOwner = 0; + hTaskClipOwner = 0; +} + +/************************************************************************** + * CLIPBOARD_GlobalFreeProc + * + * This is a callback mechanism to allow HGLOBAL data to be released in + * the context of the process which allocated it. We post a WM_TIMER message + * to the owner window(in CLIPBOARD_DeleteRecord) and destroy the data(in idEvent) + * in this WndProc, which is invoked when the apps message loop calls DispatchMessage. + * This technique is discussed in Matt Pietrek's "Under the Hood". + * An article describing the same may be found in MSDN by searching for WM_TIMER. + * Note that this mechanism will probably stop working when WINE supports + * address space separation. When "queue events" are implemented in Wine we + * should switch to using that mechanism, since it is more robust and does not + * require a procedure address to be passed. See the SetWinEventHook API for + * more info on this. + */ +VOID CALLBACK CLIPBOARD_GlobalFreeProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) +{ + /* idEvent is the HGLOBAL to be deleted */ + GlobalFree( (HGLOBAL)idEvent ); +} /************************************************************************** * CLIPBOARD_DeleteRecord @@ -108,9 +193,18 @@ void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange) if (lpFormat->hData32) { DeleteMetaFile( ((METAFILEPICT *)GlobalLock( lpFormat->hData32 ))->hMF ); - GlobalFree(lpFormat->hData32); + PostMessageA(hWndClipOwner, WM_TIMER, + (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc); if (lpFormat->hDataSrc32) - GlobalFree(lpFormat->hDataSrc32); + { + /* Release lpFormat->hData32 in the context of the process which created it. + * See CLIPBOARD_GlobalFreeProc for more details about this technique. + * GlobalFree(lpFormat->hDataSrc32); + */ + PostMessageA(hWndClipOwner, WM_TIMER, + (WPARAM)lpFormat->hDataSrc32, (LPARAM)CLIPBOARD_GlobalFreeProc); + } + if (lpFormat->hData16) /* HMETAFILE16 and HMETAFILE32 are apparently the same thing, and a shallow copy is enough to share a METAFILEPICT @@ -127,9 +221,23 @@ void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange) else { if (lpFormat->hData32) - GlobalFree(lpFormat->hData32); + { + /* Release lpFormat->hData32 in the context of the process which created it. + * See CLIPBOARD_GlobalFreeProc for more details about this technique. + * GlobalFree( lpFormat->hData32 ); + */ + PostMessageA(hWndClipOwner, WM_TIMER, + (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc); + } if (lpFormat->hDataSrc32) - GlobalFree(lpFormat->hDataSrc32); + { + /* Release lpFormat->hData32 in the context of the process which created it. + * See CLIPBOARD_GlobalFreeProc for more details about this technique. + * GlobalFree(lpFormat->hDataSrc32); + */ + PostMessageA(hWndClipOwner, WM_TIMER, + (WPARAM)lpFormat->hDataSrc32, (LPARAM)CLIPBOARD_GlobalFreeProc); + } if (lpFormat->hData16) GlobalFree16(lpFormat->hData16); } @@ -137,10 +245,28 @@ void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange) lpFormat->wDataPresent = 0; lpFormat->hData16 = 0; lpFormat->hData32 = 0; + lpFormat->hDataSrc32 = 0; + lpFormat->drvData = 0; if( bChange ) bCBHasChanged = TRUE; } +/************************************************************************** + * CLIPBOARD_EmptyCache + */ +void CLIPBOARD_EmptyCache( BOOL bChange ) +{ + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + + while(lpFormat) + { + if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 ) + CLIPBOARD_DeleteRecord( lpFormat, bChange ); + + lpFormat = lpFormat->NextFormat; + } +} + /************************************************************************** * CLIPBOARD_IsPresent */ @@ -159,6 +285,30 @@ BOOL CLIPBOARD_IsPresent(WORD wFormat) return FALSE; } +/************************************************************************** + * CLIPBOARD_IsCacheRendered + * Checks if any data needs to be rendered to the clipboard cache + * RETURNS: + * TRUE - All clipboard data is available in the cache + * FALSE - Some data is marked for delayed render and needs rendering + */ +BOOL CLIPBOARD_IsCacheRendered() +{ + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + + /* check if all formats were rendered */ + while(lpFormat) + { + if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 ) + return FALSE; + + lpFormat = lpFormat->NextFormat; + } + + return TRUE; +} + + /************************************************************************** * CLIPBOARD_IsMemoryObject * Tests if the clipboard format specifies a memory object @@ -216,6 +366,156 @@ HGLOBAL CLIPBOARD_GlobalDupMem( HGLOBAL hGlobalSrc ) return hGlobalDest; } +/************************************************************************** + * CLIPBOARD_GetFormatName + * Gets the format name associated with an ID + */ +char * CLIPBOARD_GetFormatName(UINT wFormat) +{ + LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat ); + return (lpFormat) ? lpFormat->Name : NULL; +} + + +/************************************************************************** + * CLIPBOARD_RenderFormat + */ +static BOOL CLIPBOARD_RenderFormat(LPWINE_CLIPFORMAT lpFormat) +{ + /* + * If WINE is not the selection owner, and the format is available + * we must ask the the driver to render the data to the clipboard cache. + */ + if ( !CLIPBOARD_Driver->pIsSelectionOwner() + && CLIPBOARD_Driver->pIsFormatAvailable( lpFormat->wFormatID ) ) + { + if ( !CLIPBOARD_Driver->pGetData( lpFormat->wFormatID ) ) + return FALSE; + } + /* + * If Wine owns the clipboard, and the data is marked for delayed render, + * render it now. + */ + else if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 ) + { + if( IsWindow(hWndClipOwner) ) + { + /* Send a WM_RENDERFORMAT message to notify the owner to render the + * data requested into the clipboard. + */ + TRACE("Sending WM_RENDERFORMAT message\n"); + SendMessage16(hWndClipOwner,WM_RENDERFORMAT, + (WPARAM16)lpFormat->wFormatID,0L); + } + else + { + WARN("\thWndClipOwner (%04x) is lost!\n", + hWndClipOwner); + CLIPBOARD_ReleaseOwner(); + lpFormat->wDataPresent = 0; + return FALSE; + } + } + + return (lpFormat->hData16 || lpFormat->hData32) ? TRUE : FALSE; +} + + +/************************************************************************** + * CLIPBOARD_RenderText + * + * Renders text to the clipboard buffer converting between UNIX and DOS formats. + * + * RETURNS: pointer to the WINE_CLIPFORMAT if successful, NULL otherwise + * + * FIXME: Should be a pair of driver functions that convert between OEM text and Windows. + * + */ +static LPWINE_CLIPFORMAT CLIPBOARD_RenderText( UINT wFormat ) +{ + LPWINE_CLIPFORMAT lpSource = ClipFormats; + LPWINE_CLIPFORMAT lpTarget; + + /* Asked for CF_TEXT and not available - always attempt to convert from CF_OEM_TEXT */ + if( wFormat == CF_TEXT && !ClipFormats[CF_TEXT-1].wDataPresent ) + { + /* Convert OEMTEXT -> TEXT */ + lpSource = &ClipFormats[CF_OEMTEXT-1]; + lpTarget = &ClipFormats[CF_TEXT-1]; + + TRACE("\tOEMTEXT -> TEXT\n"); + } + /* Asked for CF_OEM_TEXT, and CF_TEXT available */ + else if( wFormat == CF_OEMTEXT && !ClipFormats[CF_OEMTEXT-1].wDataPresent + && ClipFormats[CF_TEXT-1].wDataPresent ) + { + /* Convert TEXT -> OEMTEXT */ + lpSource = &ClipFormats[CF_TEXT-1]; + lpTarget = &ClipFormats[CF_OEMTEXT-1]; + + TRACE("\tTEXT -> OEMTEXT\n"); + } + /* Text format requested is available - no conversion necessary */ + else + { + lpSource = __lookup_format( ClipFormats, wFormat ); + lpTarget = lpSource; + } + + /* First render the source text format */ + if ( !lpSource || !CLIPBOARD_RenderFormat(lpSource) ) return NULL; + + /* Convert to the desired target text format, if necessary */ + if( lpTarget != lpSource && !lpTarget->hData16 && !lpTarget->hData32 ) + { + UINT16 size; + LPCSTR lpstrS; + LPSTR lpstrT; + + if (lpSource->hData32) + { + size = GlobalSize( lpSource->hData32 ); + lpstrS = (LPSTR)GlobalLock(lpSource->hData32); + } + else + { + size = GlobalSize16( lpSource->hData16 ); + lpstrS = (LPSTR)GlobalLock16(lpSource->hData16); + } + + if( !lpstrS ) return NULL; + TRACE("\tconverting from '%s' to '%s', %i chars\n", + lpSource->Name, lpTarget->Name, size); + + lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT, size); + lpstrT = (LPSTR)GlobalLock(lpTarget->hData32); + + if( lpstrT ) + { + if( lpSource->wFormatID == CF_TEXT ) + CharToOemBuffA(lpstrS, lpstrT, size); + else + OemToCharBuffA(lpstrS, lpstrT, size); + TRACE("\tgot %s\n", lpstrT); + GlobalUnlock(lpTarget->hData32); + } + else + lpTarget->hData32 = 0; + + /* Unlock source */ + if (lpSource->hData32) + GlobalUnlock(lpSource->hData32); + else + GlobalUnlock16(lpSource->hData16); + } + + return (lpTarget->hData16 || lpTarget->hData32) ? lpTarget : NULL; +} + +/************************************************************************** + * WIN32 Clipboard implementation + **************************************************************************/ + /************************************************************************** * OpenClipboard16 (USER.137) */ @@ -236,12 +536,14 @@ BOOL WINAPI OpenClipboard( HWND hWnd ) TRACE("(%04x)...\n", hWnd); - if (!hqClipLock) + if (!hClipLock) { - hqClipLock = GetFastQueue16(); - hWndClipWindow = hWnd; - bCBHasChanged = FALSE; - bRet = TRUE; + hClipLock = GetCurrentTask(); + + /* Save current user of the clipboard */ + hWndClipWindow = hWnd; + bCBHasChanged = FALSE; + bRet = TRUE; } else bRet = FALSE; @@ -264,15 +566,15 @@ BOOL16 WINAPI CloseClipboard16(void) */ BOOL WINAPI CloseClipboard(void) { - TRACE("!\n"); + TRACE("()\n"); - if (hqClipLock == GetFastQueue16()) + if (hClipLock == GetCurrentTask()) { hWndClipWindow = 0; if (bCBHasChanged && hWndViewer) SendMessage16(hWndViewer, WM_DRAWCLIPBOARD, 0, 0L); - hqClipLock = 0; + hClipLock = 0; } return TRUE; } @@ -289,31 +591,34 @@ BOOL16 WINAPI EmptyClipboard16(void) /************************************************************************** * EmptyClipboard32 (USER32.169) + * Empties and acquires ownership of the clipboard */ BOOL WINAPI EmptyClipboard(void) { - LPWINE_CLIPFORMAT lpFormat = ClipFormats; - - TRACE("(void)\n"); - - if (hqClipLock != GetFastQueue16()) return FALSE; + TRACE("()\n"); + if (hClipLock != GetCurrentTask()) + { + WARN("Clipboard not opened by calling task!"); + return FALSE; + } + /* destroy private objects */ if (hWndClipOwner) SendMessage16(hWndClipOwner, WM_DESTROYCLIPBOARD, 0, 0L); - - while(lpFormat) - { - if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 ) - CLIPBOARD_DeleteRecord( lpFormat, TRUE ); - lpFormat = lpFormat->NextFormat; - } + /* empty the cache */ + CLIPBOARD_EmptyCache(TRUE); + /* Assign ownership of the clipboard to the current client */ hWndClipOwner = hWndClipWindow; - CLIPBOARD_Driver->pEmpty(); + /* Save the current task */ + hTaskClipOwner = GetCurrentTask(); + + /* Tell the driver to acquire the selection */ + CLIPBOARD_Driver->pAcquire(); return TRUE; } @@ -321,18 +626,22 @@ BOOL WINAPI EmptyClipboard(void) /************************************************************************** * GetClipboardOwner16 (USER.140) + * FIXME: Can't return the owner if the clipbard is owned by an external app */ HWND16 WINAPI GetClipboardOwner16(void) { + TRACE("()\n"); return hWndClipOwner; } /************************************************************************** * GetClipboardOwner32 (USER32.225) + * FIXME: Can't return the owner if the clipbard is owned by an external app */ HWND WINAPI GetClipboardOwner(void) { + TRACE("()\n"); return hWndClipOwner; } @@ -348,14 +657,19 @@ HANDLE16 WINAPI SetClipboardData16( UINT16 wFormat, HANDLE16 hData ) /* NOTE: If the hData is zero and current owner doesn't match * the window that opened the clipboard then this application - * is screwed because WM_RENDERFORMAT will go to the owner + * is screwed because WM_RENDERFORMAT will go to the owner. * (to become the owner it must call EmptyClipboard() before * adding new data). */ - if( (hqClipLock != GetFastQueue16()) || !lpFormat || - (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) ) return 0; + if( CLIPBOARD_IsLocked() || !lpFormat || + (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) ) + { + WARN("Invalid hData or clipboard not opened by calling task!"); + return 0; + } + /* Pass on the request to the driver */ CLIPBOARD_Driver->pSetData(wFormat); if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 ) @@ -396,17 +710,23 @@ HANDLE WINAPI SetClipboardData( UINT wFormat, HANDLE hData ) /* NOTE: If the hData is zero and current owner doesn't match * the window that opened the clipboard then this application - * is screwed because WM_RENDERFORMAT will go to the owner + * is screwed because WM_RENDERFORMAT will go to the owner. * (to become the owner it must call EmptyClipboard() before * adding new data). */ - if( (hqClipLock != GetFastQueue16()) || !lpFormat || - (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) ) return 0; + if( CLIPBOARD_IsLocked() || !lpFormat || + (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) ) + { + WARN("Invalid hData or clipboard not opened by calling task!"); + return 0; + } - CLIPBOARD_Driver->pSetData(wFormat); + /* Tell the driver to acquire the selection */ + CLIPBOARD_Driver->pAcquire(); - if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 ) + if ( lpFormat->wDataPresent && + (lpFormat->hData16 || lpFormat->hData32) ) { CLIPBOARD_DeleteRecord(lpFormat, TRUE); @@ -435,163 +755,76 @@ HANDLE WINAPI SetClipboardData( UINT wFormat, HANDLE hData ) if ( CLIPBOARD_IsMemoryObject(wFormat) && hData && !(GlobalFlags(hData) & GMEM_DDESHARE) ) lpFormat->hData32 = CLIPBOARD_GlobalDupMem( hData ); else - lpFormat->hData32 = hData; /* 0 is legal, see WM_RENDERFORMAT */ + lpFormat->hData32 = hData; /* 0 is legal, see WM_RENDERFORMAT */ lpFormat->hData16 = 0; - return lpFormat->hData32; + return lpFormat->hData32; /* Should we return lpFormat->hDataSrc32 */ } -/************************************************************************** - * CLIPBOARD_RenderFormat - */ -static BOOL CLIPBOARD_RenderFormat(LPWINE_CLIPFORMAT lpFormat) -{ - if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 ) - { - if( IsWindow(hWndClipOwner) ) - SendMessage16(hWndClipOwner,WM_RENDERFORMAT, - (WPARAM16)lpFormat->wFormatID,0L); - else - { - WARN("\thWndClipOwner (%04x) is lost!\n", - hWndClipOwner); - hWndClipOwner = 0; lpFormat->wDataPresent = 0; - return FALSE; - } - } - return (lpFormat->hData16 || lpFormat->hData32) ? TRUE : FALSE; -} - -/************************************************************************** - * CLIPBOARD_RenderText - * - * Convert text between UNIX and DOS formats. - * - * FIXME: Should be a pair of driver functions that convert between OEM text and Windows. - * - */ -static BOOL CLIPBOARD_RenderText(LPWINE_CLIPFORMAT lpTarget, LPWINE_CLIPFORMAT lpSource) -{ - UINT16 size; - LPCSTR lpstrS; - LPSTR lpstrT; - - if (lpSource->hData32) - { - size = GlobalSize( lpSource->hData32 ); - lpstrS = (LPSTR)GlobalLock(lpSource->hData32); - } - else - { - size = GlobalSize16( lpSource->hData16 ); - lpstrS = (LPSTR)GlobalLock16(lpSource->hData16); - } - - if( !lpstrS ) return FALSE; - TRACE("\tconverting from '%s' to '%s', %i chars\n", - lpSource->Name, lpTarget->Name, size); - - lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT, size); - lpstrT = (LPSTR)GlobalLock(lpTarget->hData32); - - if( lpstrT ) - { - if( lpSource->wFormatID == CF_TEXT ) - CharToOemBuffA(lpstrS, lpstrT, size); - else - OemToCharBuffA(lpstrS, lpstrT, size); - TRACE("\tgot %s\n", lpstrT); - GlobalUnlock(lpTarget->hData32); - if (lpSource->hData32) - GlobalUnlock(lpSource->hData32); - else - GlobalUnlock16(lpSource->hData16); - return TRUE; - } - - lpTarget->hData32 = 0; - if (lpSource->hData32) - GlobalUnlock(lpSource->hData32); - else - GlobalUnlock16(lpSource->hData16); - return FALSE; -} - /************************************************************************** * GetClipboardData16 (USER.142) */ HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat ) { LPWINE_CLIPFORMAT lpRender = ClipFormats; - LPWINE_CLIPFORMAT lpUpdate = NULL; - - if (hqClipLock != GetFastQueue16()) return 0; TRACE("(%04X)\n", wFormat); - if( wFormat == CF_TEXT && !lpRender[CF_TEXT-1].wDataPresent - && lpRender[CF_OEMTEXT-1].wDataPresent ) + if (CLIPBOARD_IsLocked()) { - lpRender = &ClipFormats[CF_OEMTEXT-1]; - lpUpdate = &ClipFormats[CF_TEXT-1]; - - TRACE("\tOEMTEXT -> TEXT\n"); + WARN("Clipboard not opened by calling task!"); + return 0; } - else if( wFormat == CF_OEMTEXT && !lpRender[CF_OEMTEXT-1].wDataPresent - && lpRender[CF_TEXT-1].wDataPresent ) + + if( wFormat == CF_TEXT || wFormat == CF_OEMTEXT ) { - lpRender = &ClipFormats[CF_TEXT-1]; - lpUpdate = &ClipFormats[CF_OEMTEXT-1]; - - TRACE("\tTEXT -> OEMTEXT\n"); + lpRender = CLIPBOARD_RenderText(wFormat); + if ( !lpRender ) return 0; } else { lpRender = __lookup_format( ClipFormats, wFormat ); - lpUpdate = lpRender; + if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0; } - if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0; - if( lpUpdate != lpRender && !lpUpdate->hData16 && !lpUpdate->hData32 ) - CLIPBOARD_RenderText(lpUpdate, lpRender); - - if( lpUpdate->hData32 && !lpUpdate->hData16 ) + /* Convert between 32 -> 16 bit data, if necessary */ + if( lpRender->hData32 && !lpRender->hData16 ) { int size; - if( lpUpdate->wFormatID == CF_METAFILEPICT ) + if( lpRender->wFormatID == CF_METAFILEPICT ) size = sizeof( METAFILEPICT16 ); else - size = GlobalSize(lpUpdate->hData32); - lpUpdate->hData16 = GlobalAlloc16(GMEM_ZEROINIT, size); - if( !lpUpdate->hData16 ) + 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( lpUpdate->wFormatID == CF_METAFILEPICT ) + if( lpRender->wFormatID == CF_METAFILEPICT ) { FIXME("\timplement function CopyMetaFilePict32to16\n"); FIXME("\tin the appropriate file.\n"); #ifdef SOMEONE_IMPLEMENTED_ME - CopyMetaFilePict32to16( GlobalLock16(lpUpdate->hData16), - GlobalLock(lpUpdate->hData32) ); + CopyMetaFilePict32to16( GlobalLock16(lpRender->hData16), + GlobalLock(lpRender->hData32) ); #endif } else { - memcpy( GlobalLock16(lpUpdate->hData16), - GlobalLock(lpUpdate->hData32), + memcpy( GlobalLock16(lpRender->hData16), + GlobalLock(lpRender->hData32), size ); } - GlobalUnlock16(lpUpdate->hData16); - GlobalUnlock(lpUpdate->hData32); + GlobalUnlock16(lpRender->hData16); + GlobalUnlock(lpRender->hData32); } } TRACE("\treturning %04x (type %i)\n", - lpUpdate->hData16, lpUpdate->wFormatID); - return lpUpdate->hData16; + lpRender->hData16, lpRender->wFormatID); + return lpRender->hData16; } @@ -601,71 +834,61 @@ HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat ) HANDLE WINAPI GetClipboardData( UINT wFormat ) { LPWINE_CLIPFORMAT lpRender = ClipFormats; - LPWINE_CLIPFORMAT lpUpdate = NULL; - - if (hqClipLock != GetFastQueue16()) return 0; TRACE("(%08X)\n", wFormat); - if( wFormat == CF_TEXT && !lpRender[CF_TEXT-1].wDataPresent - && lpRender[CF_OEMTEXT-1].wDataPresent ) + if (CLIPBOARD_IsLocked()) { - lpRender = &ClipFormats[CF_OEMTEXT-1]; - lpUpdate = &ClipFormats[CF_TEXT-1]; - - TRACE("\tOEMTEXT -> TEXT\n"); + WARN("Clipboard not opened by calling task!"); + return 0; } - else if( wFormat == CF_OEMTEXT && !lpRender[CF_OEMTEXT-1].wDataPresent - && lpRender[CF_TEXT-1].wDataPresent ) + + if( wFormat == CF_TEXT || wFormat == CF_OEMTEXT ) { - lpRender = &ClipFormats[CF_TEXT-1]; - lpUpdate = &ClipFormats[CF_OEMTEXT-1]; - - TRACE("\tTEXT -> OEMTEXT\n"); + lpRender = CLIPBOARD_RenderText(wFormat); + if ( !lpRender ) return 0; } else { lpRender = __lookup_format( ClipFormats, wFormat ); - lpUpdate = lpRender; + if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0; } - if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0; - if( lpUpdate != lpRender && !lpUpdate->hData16 && !lpUpdate->hData32 ) - CLIPBOARD_RenderText(lpUpdate, lpRender); - - if( lpUpdate->hData16 && !lpUpdate->hData32 ) + /* Convert between 16 -> 32 bit data, if necessary */ + if( lpRender->hData16 && !lpRender->hData32 ) { int size; - if( lpUpdate->wFormatID == CF_METAFILEPICT ) + if( lpRender->wFormatID == CF_METAFILEPICT ) size = sizeof( METAFILEPICT ); else - size = GlobalSize16(lpUpdate->hData16); - lpUpdate->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, + size = GlobalSize16(lpRender->hData16); + lpRender->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, size); - if( lpUpdate->wFormatID == CF_METAFILEPICT ) + if( lpRender->wFormatID == CF_METAFILEPICT ) { FIXME("\timplement function CopyMetaFilePict16to32\n"); FIXME("\tin the appropriate file.\n"); #ifdef SOMEONE_IMPLEMENTED_ME - CopyMetaFilePict16to32( GlobalLock16(lpUpdate->hData32), - GlobalLock(lpUpdate->hData16) ); + CopyMetaFilePict16to32( GlobalLock16(lpRender->hData32), + GlobalLock(lpRender->hData16) ); #endif } else { - memcpy( GlobalLock(lpUpdate->hData32), - GlobalLock16(lpUpdate->hData16), + memcpy( GlobalLock(lpRender->hData32), + GlobalLock16(lpRender->hData16), size ); } - GlobalUnlock(lpUpdate->hData32); - GlobalUnlock16(lpUpdate->hData16); + GlobalUnlock(lpRender->hData32); + GlobalUnlock16(lpRender->hData16); } TRACE("\treturning %04x (type %i)\n", - lpUpdate->hData32, lpUpdate->wFormatID); - return lpUpdate->hData32; + lpRender->hData32, lpRender->wFormatID); + return lpRender->hData32; } + /************************************************************************** * CountClipboardFormats16 (USER.143) */ @@ -683,20 +906,30 @@ INT WINAPI CountClipboardFormats(void) INT FormatCount = 0; LPWINE_CLIPFORMAT lpFormat = ClipFormats; - TRACE("(void)\n"); + TRACE("()\n"); while(TRUE) { if (lpFormat == NULL) break; - if( lpFormat->wFormatID != CF_TEXT ) - CLIPBOARD_Driver->pGetData( lpFormat->wFormatID ); - - if (lpFormat->wDataPresent) - { - TRACE("\tdata found for format %i\n", lpFormat->wFormatID); - FormatCount++; - } + if( lpFormat->wFormatID != CF_TEXT ) /* Don't count CF_TEXT */ + { + /* + * The format is available if either: + * 1. The data is already in the cache. + * 2. The selection is not owned by us(WINE) and the data is + * available to the clipboard driver. + */ + if ( lpFormat->wDataPresent || + ( !CLIPBOARD_Driver->pIsSelectionOwner() + && CLIPBOARD_Driver->pIsFormatAvailable( lpFormat->wFormatID ) ) ) + { + TRACE("\tdata found for format %i(%s)\n", + lpFormat->wFormatID, CLIPBOARD_GetFormatName(lpFormat->wFormatID)); + FormatCount++; + } + } + lpFormat = lpFormat->NextFormat; } @@ -724,14 +957,16 @@ UINT16 WINAPI EnumClipboardFormats16( UINT16 wFormat ) */ UINT WINAPI EnumClipboardFormats( UINT wFormat ) { - LPWINE_CLIPFORMAT lpFormat = ClipFormats; + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + BOOL bFormatPresent; TRACE("(%04X)\n", wFormat); - if( hqClipLock != GetFastQueue16() ) return 0; - - if( wFormat < CF_OEMTEXT ) - CLIPBOARD_Driver->pGetData( CF_OEMTEXT ); + if (CLIPBOARD_IsLocked()) + { + WARN("Clipboard not opened by calling task!"); + return 0; + } if (wFormat == 0) /* start from the beginning */ lpFormat = ClipFormats; @@ -748,13 +983,22 @@ UINT WINAPI EnumClipboardFormats( UINT wFormat ) { if (lpFormat == NULL) return 0; - if( lpFormat->wFormatID != CF_OEMTEXT && lpFormat->wFormatID != CF_TEXT ) - CLIPBOARD_Driver->pGetData( lpFormat->wFormatID ); + /* Synthesize CF_TEXT from CF_OEMTEXT and vice versa */ + bFormatPresent = (lpFormat->wDataPresent || + (lpFormat->wFormatID == CF_OEMTEXT && ClipFormats[CF_TEXT-1].wDataPresent) || + (lpFormat->wFormatID == CF_TEXT && ClipFormats[CF_OEMTEXT-1].wDataPresent) ); - if (lpFormat->wDataPresent || - (lpFormat->wFormatID == CF_OEMTEXT && ClipFormats[CF_TEXT-1].wDataPresent) || - (lpFormat->wFormatID == CF_TEXT && ClipFormats[CF_OEMTEXT-1].wDataPresent) ) + /* Query the driver if not yet in the cache */ + if (!bFormatPresent && !CLIPBOARD_Driver->pIsSelectionOwner()) + { + bFormatPresent = + CLIPBOARD_Driver->pIsFormatAvailable( (lpFormat->wFormatID == CF_TEXT) ? + CF_OEMTEXT : lpFormat->wFormatID ); + } + + if (bFormatPresent) break; + lpFormat = lpFormat->NextFormat; } @@ -803,10 +1047,13 @@ UINT16 WINAPI RegisterClipboardFormat16( LPCSTR FormatName ) lpNewFormat->hData16 = 0; lpNewFormat->hDataSrc32 = 0; lpNewFormat->hData32 = 0; - lpNewFormat->BufSize = 0; + lpNewFormat->drvData = 0; lpNewFormat->PrevFormat = lpFormat; lpNewFormat->NextFormat = NULL; + /* Pass on the registration request to the driver */ + CLIPBOARD_Driver->pRegisterFormat( FormatName ); + return LastRegFormat++; } @@ -831,6 +1078,7 @@ UINT WINAPI RegisterClipboardFormatW( LPCWSTR formatName ) return ret; } + /************************************************************************** * GetClipboardFormatName16 (USER.146) */ @@ -877,6 +1125,7 @@ INT WINAPI GetClipboardFormatNameW( UINT wFormat, LPWSTR retStr, INT maxlen ) */ HWND16 WINAPI SetClipboardViewer16( HWND16 hWnd ) { + TRACE("(%04x)\n", hWnd); return SetClipboardViewer( hWnd ); } @@ -900,6 +1149,7 @@ HWND WINAPI SetClipboardViewer( HWND hWnd ) */ HWND16 WINAPI GetClipboardViewer16(void) { + TRACE("()\n"); return hWndViewer; } @@ -909,6 +1159,7 @@ HWND16 WINAPI GetClipboardViewer16(void) */ HWND WINAPI GetClipboardViewer(void) { + TRACE("()\n"); return hWndViewer; } @@ -921,6 +1172,7 @@ BOOL16 WINAPI ChangeClipboardChain16(HWND16 hWnd, HWND16 hWndNext) return ChangeClipboardChain(hWnd, hWndNext); } + /************************************************************************** * ChangeClipboardChain32 (USER32.22) */ @@ -942,7 +1194,6 @@ BOOL WINAPI ChangeClipboardChain(HWND hWnd, HWND hWndNext) } - /************************************************************************** * IsClipboardFormatAvailable16 (USER.193) */ @@ -957,28 +1208,42 @@ BOOL16 WINAPI IsClipboardFormatAvailable16( UINT16 wFormat ) */ BOOL WINAPI IsClipboardFormatAvailable( UINT wFormat ) { - TRACE("(%04X) !\n", wFormat); + BOOL bRet; - CLIPBOARD_Driver->pGetData( (wFormat == CF_TEXT) ? CF_OEMTEXT : wFormat ); + if (wFormat == 0) /* Reject this case quickly */ + bRet = FALSE; - return CLIPBOARD_IsPresent(wFormat); + /* If WINE is not the clipboard selection owner ask the clipboard driver */ + else if ( !CLIPBOARD_Driver->pIsSelectionOwner() ) + bRet = CLIPBOARD_Driver->pIsFormatAvailable( (wFormat == CF_TEXT) ? + CF_OEMTEXT : wFormat ); + /* Check if the format is in the local cache */ + else + bRet = CLIPBOARD_IsPresent(wFormat); + + TRACE("(%04X)- ret(%d)\n", wFormat, bRet); + return bRet; } /************************************************************************** * GetOpenClipboardWindow16 (USER.248) + * FIXME: This wont work if an external app owns the selection */ HWND16 WINAPI GetOpenClipboardWindow16(void) { + TRACE("()\n"); return hWndClipWindow; } /************************************************************************** * GetOpenClipboardWindow32 (USER32.277) + * FIXME: This wont work if an external app owns the selection */ HWND WINAPI GetOpenClipboardWindow(void) { + TRACE("()\n"); return hWndClipWindow; } @@ -999,6 +1264,7 @@ INT16 WINAPI GetPriorityClipboardFormat16( UINT16 *lpPriorityList, INT16 nCount) INT WINAPI GetPriorityClipboardFormat( UINT *lpPriorityList, INT nCount ) { int Counter; + TRACE("()\n"); if(CountClipboardFormats() == 0) { @@ -1014,5 +1280,3 @@ INT WINAPI GetPriorityClipboardFormat( UINT *lpPriorityList, INT nCount ) return -1; } - - diff --git a/windows/ttydrv/clipboard.c b/windows/ttydrv/clipboard.c index f608b7cf42d..d836d803773 100644 --- a/windows/ttydrv/clipboard.c +++ b/windows/ttydrv/clipboard.c @@ -13,9 +13,16 @@ char *TTYDRV_CLIPBOARD_szSelection = NULL; /*********************************************************************** - * TTYDRV_CLIPBOARD_EmptyClipboard + * TTYDRV_CLIPBOARD_Acquire */ -void TTYDRV_CLIPBOARD_Empty() +void TTYDRV_CLIPBOARD_Acquire() +{ +} + +/*********************************************************************** + * TTYDRV_CLIPBOARD_Release + */ +void TTYDRV_CLIPBOARD_Release() { if(TTYDRV_CLIPBOARD_szSelection) { @@ -39,6 +46,35 @@ BOOL TTYDRV_CLIPBOARD_GetData(UINT wFormat) return FALSE; } +/*********************************************************************** + * TTYDRV_CLIPBOARD_IsFormatAvailable + */ +BOOL TTYDRV_CLIPBOARD_IsFormatAvailable(UINT wFormat) +{ + return FALSE; +} + +/************************************************************************** + * TTYDRV_CLIPBOARD_RegisterFormat + * + * Registers a custom clipboard format + * Returns: TRUE - new format registered, FALSE - Format already registered + */ +BOOL TTYDRV_CLIPBOARD_RegisterFormat( LPCSTR FormatName ) +{ + return TRUE; +} + +/************************************************************************** + * X11DRV_CLIPBOARD_IsSelectionowner + * + * Returns: TRUE - We(WINE) own the selection, FALSE - Selection not owned by us + */ +BOOL TTYDRV_CLIPBOARD_IsSelectionowner() +{ + return FALSE; +} + /*********************************************************************** * TTYDRV_CLIPBOARD_ResetOwner */ diff --git a/windows/ttydrv/init.c b/windows/ttydrv/init.c index 4e1b8c50d8e..1cacee2f9cf 100644 --- a/windows/ttydrv/init.c +++ b/windows/ttydrv/init.c @@ -24,9 +24,13 @@ USER_DRIVER TTYDRV_USER_Driver = CLIPBOARD_DRIVER TTYDRV_CLIPBOARD_Driver = { - TTYDRV_CLIPBOARD_Empty, + TTYDRV_CLIPBOARD_Acquire, + TTYDRV_CLIPBOARD_Release, TTYDRV_CLIPBOARD_SetData, TTYDRV_CLIPBOARD_GetData, + TTYDRV_CLIPBOARD_IsFormatAvailable, + TTYDRV_CLIPBOARD_RegisterFormat, + TTYDRV_CLIPBOARD_IsSelectionowner, TTYDRV_CLIPBOARD_ResetOwner }; diff --git a/windows/user.c b/windows/user.c index ea30472f0c6..9297ee75972 100644 --- a/windows/user.c +++ b/windows/user.c @@ -153,7 +153,6 @@ static void USER_QueueCleanup( HQUEUE16 hQueue ) QUEUE_SetExitingQueue( hQueue ); WIN_ResetQueueWindows( desktop, hQueue, (HQUEUE16)0); - CLIPBOARD_ResetLock( hQueue, 0 ); QUEUE_SetExitingQueue( 0 ); /* Free the message queue */ @@ -168,8 +167,13 @@ static void USER_QueueCleanup( HQUEUE16 hQueue ) */ static void USER_AppExit( HINSTANCE16 hInstance ) { - /* FIXME: empty clipboard if needed, maybe destroy menus (Windows - * only complains about them but does nothing); + /* FIXME: maybe destroy menus (Windows only complains about them + * but does nothing); + */ + + /* TODO: Start up persistant WINE X clipboard server process which will + * take ownership of the X selection and continue to service selection + * requests from other apps. */ /* ModuleUnload() in "Internals" */ diff --git a/windows/x11drv/clipboard.c b/windows/x11drv/clipboard.c index 5ed4d240efd..6636a4e0acb 100644 --- a/windows/x11drv/clipboard.c +++ b/windows/x11drv/clipboard.c @@ -1,8 +1,50 @@ /* - * X11 windows driver + * X11 clipboard windows driver * * Copyright 1994 Martin Ayotte * 1996 Alex Korobka + * 1999 Noel Borthwick + * + * NOTES: + * This file contains the X specific implementation for the windows + * Clipboard API. + * + * Wine's internal clipboard is exposed to external apps via the X + * selection mechanism. + * Currently the driver asserts ownership via two selection atoms: + * 1. PRIMARY(XA_PRIMARY) + * 2. CLIPBOARD + * + * In our implementation, the CLIPBOARD selection takes precedence over PRIMARY. + * i.e. if a CLIPBOARD selection is available, it is used instead of PRIMARY. + * When Wine taks ownership of the clipboard, it takes ownership of BOTH selections. + * While giving up selection ownership, if the CLIPBOARD selection is lost, + * it will lose both PRIMARY and CLIPBOARD and empty the clipboard. + * However if only PRIMARY is lost, it will continue to hold the CLIPBOARD selection + * (leaving the clipboard cache content unaffected). + * + * Every format exposed via a windows clipboard format is also exposed through + * a corresponding X selection target. A selection target atom is synthesized + * whenever a new Windows clipboard format is registered via RegisterClipboardFormat, + * or when a built in format is used for the first time. + * Windows native format are exposed by prefixing the format name with "" + * This allows us to uniquely identify windows native formats exposed by other + * running WINE apps. + * + * In order to allow external applications to query WINE for supported formats, + * we respond to the "TARGETS" selection target. (See EVENT_SelectionRequest + * for implementation) We use the same mechanism to query external clients for + * availability of a particular format, by cacheing the list of available targets + * by using the clipboard cache's "delayed render" mechanism. If a selection client + * does not support the "TARGETS" selection target, we actually attempt to retrieve + * the format requested as a fallback mechanism. + * + * Certain Windows native formats are automatically converted to X native formats + * and vice versa. If a native format is available in the selection, it takes + * precedence, in order to avoid unnecessary conversions. + * + * TODO: + * - Support for converting between DIB and PIXMAP formats */ #include "config.h" @@ -10,6 +52,7 @@ #ifndef X_DISPLAY_MISSING #include +#include #include "ts_xlib.h" #include "wine/winuser16.h" @@ -22,129 +65,435 @@ DEFAULT_DEBUG_CHANNEL(clipboard) -extern HWND hWndClipOwner; -extern HWND hWndClipWindow; -extern WINE_CLIPFORMAT ClipFormats[]; +/* Selection masks */ + +#define S_NOSELECTION 0 +#define S_PRIMARY 1 +#define S_CLIPBOARD 2 + +/* X selection context info */ + +static char _CLIPBOARD[] = "CLIPBOARD"; /* CLIPBOARD atom name */ +static char FMT_PREFIX[] = ""; /* Prefix for windows specific formats */ +static int selectionAcquired = 0; /* Contains the current selection masks */ +static Window selectionWindow = None; /* The top level X window which owns the selection */ +static Window selectionPrevWindow = None; /* The last X window that owned the selection */ +static Window PrimarySelectionOwner = None; /* The window which owns the primary selection */ +static Window ClipboardSelectionOwner = None; /* The window which owns the clipboard selection */ +static unsigned long cSelectionTargets = 0; /* Number of target formats reported by TARGETS selection */ +static Atom selectionCacheSrc = XA_PRIMARY; /* The selection source from which the clipboard cache was filled */ -static Bool selectionAcquired = False; -static Window selectionWindow = None; -static Window selectionPrevWindow = None; /************************************************************************** - * X11DRV_CLIPBOARD_CheckSelection [Internal] + * X11DRV_CLIPBOARD_MapPropertyToID * - * Prevent X selection from being lost when a top level window is - * destroyed. + * Map an X selection property type atom name to a windows clipboard format ID */ -static void X11DRV_CLIPBOARD_CheckSelection(WND* pWnd) +UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName) { - TRACE("\tchecking %08x\n", - (unsigned) X11DRV_WND_GetXWindow(pWnd) - ); + /* + * If the property name starts with FMT_PREFIX strip this off and + * get the ID for a custom Windows registered format with this name. + * We can also understand STRING, PIXMAP and BITMAP. + */ + if ( NULL == itemFmtName ) + return 0; + else if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) ) + return RegisterClipboardFormatA(itemFmtName + strlen(FMT_PREFIX)); + else if ( 0 == strcmp(itemFmtName, "STRING") ) + return CF_OEMTEXT; + else if ( 0 == strcmp(itemFmtName, "PIXMAP") ) + return CF_DIB; + else if ( 0 == strcmp(itemFmtName, "BITMAP") ) + return CF_DIB; - if( selectionAcquired && selectionWindow != None && - X11DRV_WND_GetXWindow(pWnd) == selectionWindow ) + WARN("\tNo mapping to Windows clipboard format for property %s\n", itemFmtName); + return 0; +} + +/************************************************************************** + * X11DRV_CLIPBOARD_MapFormatToProperty + * + * Map a windows clipboard format ID to an X selection property atom + */ +Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT wFormat) +{ + Atom prop = None; + + switch (wFormat) { - selectionPrevWindow = selectionWindow; - selectionWindow = None; + case CF_OEMTEXT: + case CF_TEXT: + prop = XA_STRING; + break; - if( pWnd->next ) - selectionWindow = X11DRV_WND_GetXWindow(pWnd->next); - else if( pWnd->parent ) - if( pWnd->parent->child != pWnd ) - selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child); + case CF_DIB: + case CF_BITMAP: + { + /* + * Request a PIXMAP, only if WINE is NOT the selection owner, + * AND the requested format is not in the cache. + */ + if ( !X11DRV_CLIPBOARD_IsSelectionowner() && !CLIPBOARD_IsPresent(wFormat) ) + { + prop = XA_PIXMAP; + break; + } + /* Fall thru to the default case in order to use the native format */ + } + + default: + { + /* + * If an X atom is registered for this format, return that + * Otherwise register a new atom. + */ + char str[256]; + char *fmtName = CLIPBOARD_GetFormatName(wFormat); + strcpy(str, FMT_PREFIX); - TRACE("\tswitching selection from %08x to %08x\n", - (unsigned)selectionPrevWindow, (unsigned)selectionWindow); - - if( selectionWindow != None ) - { - TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime); - if( TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow ) - selectionWindow = None; - } + if (fmtName) + { + strncat(str, fmtName, sizeof(str) - strlen(FMT_PREFIX)); + prop = TSXInternAtom(display, str, False); + } + break; + } } + + if (prop == None) + TRACE("\tNo mapping to X property for Windows clipboard format %d(%s)\n", + wFormat, CLIPBOARD_GetFormatName(wFormat)); + + return prop; +} + +/************************************************************************** + * X11DRV_CLIPBOARD_IsNativeProperty + * + * Checks if a property is a native property type + */ +BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop) +{ + char *itemFmtName = TSXGetAtomName(display, prop); + BOOL bRet = FALSE; + + if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) ) + bRet = TRUE; + + TSXFree(itemFmtName); + return bRet; +} + +/************************************************************************** + * X11DRV_CLIPBOARD_CacheDataFormats + * + * Caches the list of data formats available from the current selection. + * This queries the selection owner for the TARGETS property and saves all + * reported property types. + */ +int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName ) +{ + HWND hWnd = NULL; + HWND hWndClipWindow = GetOpenClipboardWindow(); + WND* wnd = NULL; + XEvent xe; + Atom aTargets; + Atom atype=AnyPropertyType; + int aformat; + unsigned long remain; + Atom* targetList=NULL; + Window w; + Window ownerSelection = NULL; + + /* + * Empty the clipboard cache + */ + CLIPBOARD_EmptyCache(TRUE); + + cSelectionTargets = 0; + selectionCacheSrc = SelectionName; + + hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow(); + + ownerSelection = TSXGetSelectionOwner(display, SelectionName); + if ( !hWnd || (ownerSelection == None) ) + return cSelectionTargets; + + /* + * Query the selection owner for the TARGETS property + */ + wnd = WIN_FindWndPtr(hWnd); + w = X11DRV_WND_FindXWindow(wnd); + WIN_ReleaseWndPtr(wnd); + wnd = NULL; + + aTargets = TSXInternAtom(display, "TARGETS", False); + + TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n", + TSXGetAtomName(display, selectionCacheSrc), (unsigned)ownerSelection ); + + EnterCriticalSection( &X11DRV_CritSection ); + XConvertSelection(display, selectionCacheSrc, aTargets, + TSXInternAtom(display, "SELECTION_DATA", False), + w, CurrentTime); + + /* + * Wait until SelectionNotify is received + */ + while( TRUE ) + { + if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) ) + if( xe.xselection.selection == selectionCacheSrc ) + break; + } + LeaveCriticalSection( &X11DRV_CritSection ); + + /* Verify that the selection returned a valid TARGETS property */ + if ( (xe.xselection.target != aTargets) + || (xe.xselection.property == None) ) + { + TRACE("\tCould not retrieve TARGETS\n"); + return cSelectionTargets; + } + + /* Read the TARGETS property contents */ + if(TSXGetWindowProperty(display, xe.xselection.requestor, xe.xselection.property, + 0, 0x3FFF, True, XA_ATOM, &atype, &aformat, + &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success) + TRACE("\tCouldn't read TARGETS property\n"); + else + { + TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n", + TSXGetAtomName(display,atype),aformat,cSelectionTargets, remain); + /* + * The TARGETS property should have returned us a list of atoms + * corresponding to each selection target format supported. + */ + if(atype == XA_ATOM && aformat == 32) + { + int i; + LPWINE_CLIPFORMAT lpFormat; + + /* Cache these formats in the clipboard cache */ + + for (i = 0; i < cSelectionTargets; i++) + { + char *itemFmtName = TSXGetAtomName(display, targetList[i]); + UINT wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName); + + /* + * If the clipboard format maps to a Windows format, simply store + * the atom identifier and record its availablity status + * in the clipboard cache. + */ + if (wFormat) + { + lpFormat = CLIPBOARD_LookupFormat( wFormat ); + + /* Don't replace if the property already cached is a native format */ + if (lpFormat->wDataPresent + && X11DRV_CLIPBOARD_IsNativeProperty(lpFormat->drvData)) + { + TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s (Skipped)\n", + i, itemFmtName, wFormat, lpFormat->Name); + } + else + { + lpFormat->wDataPresent = 1; + lpFormat->drvData = targetList[i]; + TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s\n", + i, itemFmtName, wFormat, lpFormat->Name); + } + } + + TSXFree(itemFmtName); + } + } + + /* Free the list of targets */ + TSXFree(targetList); + } + + return cSelectionTargets; } /************************************************************************** * X11DRV_CLIPBOARD_ReadSelection + * Reads the contents of the X selection property into the WINE clipboard cache + * converting the selection into a format compatible with the windows clipboard + * if possible. + * This method is invoked only to read the contents of a the selection owned + * by an external application. i.e. when we do not own the X selection. */ -static void X11DRV_CLIPBOARD_ReadSelection(Window w, Atom prop) +static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, Atom reqFormat) { - HANDLE hText = 0; - LPWINE_CLIPFORMAT lpFormat = ClipFormats; + Atom atype=AnyPropertyType; + int aformat; + unsigned long nitems,remain,itemSize; + long lRequestLength; + unsigned char* val=NULL; + LPWINE_CLIPFORMAT lpFormat; + BOOL bRet = FALSE; + HWND hWndClipWindow = GetOpenClipboardWindow(); + + if(prop == None) + return bRet; TRACE("Reading X selection...\n"); - if(prop != None) + TRACE("\tretrieving property %s into %s\n", + TSXGetAtomName(display,reqFormat), TSXGetAtomName(display,prop) ); + + /* + * Retrieve the property in the required X format. + * First request a zero length in order to figure out the request size. + */ + if(TSXGetWindowProperty(display,w,prop,0,0,True,reqFormat, + &atype, &aformat, &nitems, &itemSize, &val) != Success) { - Atom atype=AnyPropertyType; - int aformat; - unsigned long nitems,remain; - unsigned char* val=NULL; + WARN("\tcouldn't get property size\n"); + return bRet; + } + /* Free property if one was returned */ + if ( val ) + { + TSXFree(val); + val = NULL; + } + + TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8); + lRequestLength = (itemSize * aformat/8)/4 + 1; + + if(TSXGetWindowProperty(display,w,prop,0,lRequestLength,True,reqFormat, + &atype, &aformat, &nitems, &remain, &val) != Success) + { + WARN("\tcouldn't read property\n"); + return bRet; + } - TRACE("\tgot property %s\n",TSXGetAtomName(display,prop)); + TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n", + atype ? TSXGetAtomName(display,atype) : NULL, aformat,nitems,remain,val); + + if (remain) + { + WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain); + return bRet; + } + + /* + * Translate the X property into the appropriate Windows clipboard + * format, if possible. + */ + if ( (reqFormat == XA_STRING) + && (atype == XA_STRING) && (aformat == 8) ) /* treat Unix text as CF_OEMTEXT */ + { + HANDLE16 hText = 0; + int i,inlcount = 0; + char* lpstr; + + TRACE("\tselection is '%s'\n",val); + + for(i=0; i <= nitems; i++) + if( val[i] == '\n' ) inlcount++; + + if( nitems ) + { + hText=GlobalAlloc16(GMEM_MOVEABLE, nitems + inlcount + 1); + if( (lpstr = (char*)GlobalLock16(hText)) ) + { + for(i=0,inlcount=0; i <= nitems; i++) + { + if( val[i] == '\n' ) lpstr[inlcount++]='\r'; + lpstr[inlcount++]=val[i]; + } + GlobalUnlock16(hText); + } + else + hText = 0; + } + + if( hText ) + { + /* delete previous CF_TEXT and CF_OEMTEXT data */ + lpFormat = CLIPBOARD_LookupFormat(CF_TEXT); + if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) + CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow)); + + lpFormat = CLIPBOARD_LookupFormat(CF_OEMTEXT); + if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) + CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow)); + + /* Update the CF_OEMTEXT record */ + lpFormat->wDataPresent = 1; + lpFormat->hData32 = 0; + lpFormat->hData16 = hText; + + bRet = TRUE; + } + } + else if ( reqFormat == XA_PIXMAP ) /* treat PIXMAP as CF_DIB or CF_BITMAP */ + { + if (wFormat == CF_BITMAP ) + FIXME("PIXMAP to CF_BITMAP conversion not yet implemented!\n"); + else if (wFormat == CF_DIB ) + FIXME("PIXMAP to CF_DIB conversion not yet implemented!\n"); + } + + /* For other data types simply copy the X data without conversion */ + else + { + HANDLE hClipData = 0; + void* lpClipData; + int cBytes = nitems * aformat/8; - /* TODO: Properties longer than 64K */ + if( cBytes ) + { + /* Turn on the DDESHARE flag to enable shared 32 bit memory */ + hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cBytes ); + if( (lpClipData = GlobalLock(hClipData)) ) + { + memcpy(lpClipData, val, cBytes); + GlobalUnlock(hClipData); + } + else + hClipData = 0; + } + + if( hClipData ) + { + /* delete previous clipboard record if any */ + lpFormat = CLIPBOARD_LookupFormat(wFormat); + if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) + CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow)); + + /* Update the clipboard record */ + lpFormat->wDataPresent = 1; + lpFormat->hData32 = hClipData; + lpFormat->hData16 = 0; - if(TSXGetWindowProperty(display,w,prop,0,0x3FFF,True,XA_STRING, - &atype, &aformat, &nitems, &remain, &val) != Success) - WARN("\tcouldn't read property\n"); - else - { - TRACE("\tType %s,Format %d,nitems %ld,value %s\n", - TSXGetAtomName(display,atype),aformat,nitems,val); - - if(atype == XA_STRING && aformat == 8) - { - int i,inlcount = 0; - char* lpstr; - - TRACE("\tselection is '%s'\n",val); - - for(i=0; i <= nitems; i++) - if( val[i] == '\n' ) inlcount++; - - if( nitems ) - { - hText=GlobalAlloc(GMEM_MOVEABLE, nitems + inlcount + 1); - if( (lpstr = (char*)GlobalLock(hText)) ) - for(i=0,inlcount=0; i <= nitems; i++) - { - if( val[i] == '\n' ) lpstr[inlcount++]='\r'; - lpstr[inlcount++]=val[i]; - } - else hText = 0; - } - } - TSXFree(val); - } - } - - /* delete previous CF_TEXT and CF_OEMTEXT data */ - - if( hText ) - { - lpFormat = &ClipFormats[CF_TEXT-1]; - if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) - CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow)); - lpFormat = &ClipFormats[CF_OEMTEXT-1]; - if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) - CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow)); - lpFormat->wDataPresent = 1; - lpFormat->hData32 = hText; - lpFormat->hData16 = 0; - } + bRet = TRUE; + } + } + + /* Free the retrieved property */ + TSXFree(val); + + return bRet; } /************************************************************************** * X11DRV_CLIPBOARD_ReleaseSelection * - * Wine might have lost XA_PRIMARY selection because of - * EmptyClipboard() or other client. + * Release an XA_PRIMARY or XA_CLIPBOARD selection that we own, in response + * to a SelectionClear event. + * This can occur in response to another client grabbing the X selection. + * If the XA_CLIPBOARD selection is lost we relinquish XA_PRIMARY as well. */ -void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd) +void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd) { + Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False); + /* w is the window that lost selection, * * selectionPrevWindow is nonzero if CheckSelection() was called. @@ -159,17 +508,81 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd) { /* alright, we really lost it */ - selectionAcquired = False; - selectionWindow = None; + if ( selType == xaClipboard ) /* completely give up the selection */ + { + TRACE("Lost CLIPBOARD selection\n"); + + /* We are completely giving up the selection. + * Make sure we can open the windows clipboard first. */ + + if ( !OpenClipboard(hwnd) ) + { + /* + * We can't empty the clipboard if we cant open it so abandon. + * Wine will think that it still owns the selection but this is + * safer than losing the selection without properly emptying + * the clipboard. Perhaps we should forcibly re-assert ownership + * of the CLIPBOARD selection in this case... + */ + ERR("\tClipboard is busy. Could not give up selection!\n"); + return; + } - /* but we'll keep existing data for internal use */ + selectionPrevWindow = selectionWindow; + selectionWindow = None; + PrimarySelectionOwner = ClipboardSelectionOwner = 0; + + /* Voluntarily give up the PRIMARY selection if we still own it */ + if ( selectionAcquired & S_PRIMARY ) + { + XEvent xe; + TRACE("Releasing XA_PRIMARY selection\n"); + + TSXSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime); + + /* Wait until SelectionClear is processed */ + if( selectionPrevWindow ) + while( !XCheckTypedWindowEvent( display, selectionPrevWindow, + SelectionClear, &xe ) ); + } + + /* Empty the windows clipboard. + * We should pretend that we still own the selection BEFORE calling + * EmptyClipboard() since otherwise this has the side effect of + * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection + * to be re-acquired by us! + */ + selectionAcquired = (S_PRIMARY | S_CLIPBOARD); + EmptyClipboard(); + selectionAcquired = S_NOSELECTION; + + CloseClipboard(); + + /* Give up ownership of the windows clipboard */ + CLIPBOARD_ReleaseOwner(); + } + else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */ + { + TRACE("Lost PRIMARY selection\n"); + PrimarySelectionOwner = 0; + selectionAcquired &= ~S_PRIMARY; /* clear S_PRIMARY mask */ + } + + cSelectionTargets = 0; } + /* but we'll keep existing data for internal use */ else if( w == selectionPrevWindow ) { + Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); + w = TSXGetSelectionOwner(display, XA_PRIMARY); if( w == None ) TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime); - } + + w = TSXGetSelectionOwner(display, xaClipboard); + if( w == None ) + TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime); + } } selectionPrevWindow = None; @@ -177,14 +590,18 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd) /************************************************************************** * X11DRV_CLIPBOARD_Empty + * Voluntarily release all currently owned X selections */ -void X11DRV_CLIPBOARD_Empty() +void X11DRV_CLIPBOARD_Release() { if( selectionAcquired ) { XEvent xe; + Window savePrevWindow = selectionWindow; + Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); + BOOL bHasPrimarySelection = selectionAcquired & S_PRIMARY; - selectionAcquired = False; + selectionAcquired = S_NOSELECTION; selectionPrevWindow = selectionWindow; selectionWindow = None; @@ -192,135 +609,384 @@ void X11DRV_CLIPBOARD_Empty() (unsigned)selectionPrevWindow); EnterCriticalSection(&X11DRV_CritSection); - XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime); + TRACE("Releasing CLIPBOARD selection\n"); + XSetSelectionOwner(display, xaClipboard, None, CurrentTime); if( selectionPrevWindow ) while( !XCheckTypedWindowEvent( display, selectionPrevWindow, - SelectionClear, &xe ) ); + SelectionClear, &xe ) ); + + if ( bHasPrimarySelection ) + { + TRACE("Releasing XA_PRIMARY selection\n"); + selectionPrevWindow = savePrevWindow; /* May be cleared in X11DRV_CLIPBOARD_ReleaseSelection */ + XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime); + + if( selectionPrevWindow ) + while( !XCheckTypedWindowEvent( display, selectionPrevWindow, + SelectionClear, &xe ) ); + } + LeaveCriticalSection(&X11DRV_CritSection); } } +/************************************************************************** + * X11DRV_CLIPBOARD_Acquire() + */ +void X11DRV_CLIPBOARD_Acquire() +{ + Window owner; + HWND hWndClipWindow = GetOpenClipboardWindow(); + + /* + * Acquire X selection if we don't already own it. + * Note that we only acquire the selection if it hasn't been already + * acquired by us, and ignore the fact that another X window may be + * asserting ownership. The reason for this is we need *any* top level + * X window to hold selection ownership. The actual clipboard data requests + * are made via GetClipboardData from EVENT_SelectionRequest and this + * ensures that the real HWND owner services the request. + * If the owning X window gets destroyed the selection ownership is + * re-cycled to another top level X window in X11DRV_CLIPBOARD_ResetOwner. + * + */ + + if ( !(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)) ) + { + Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); + WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() ); + owner = X11DRV_WND_FindXWindow(tmpWnd ); + WIN_ReleaseWndPtr(tmpWnd); + + /* Grab PRIMARY selection if not owned */ + if ( !(selectionAcquired & S_PRIMARY) ) + TSXSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime); + + /* Grab CLIPBOARD selection if not owned */ + if ( !(selectionAcquired & S_CLIPBOARD) ) + TSXSetSelectionOwner(display, xaClipboard, owner, CurrentTime); + + if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner ) + selectionAcquired |= S_PRIMARY; + + if( TSXGetSelectionOwner(display,xaClipboard) == owner) + selectionAcquired |= S_CLIPBOARD; + + if (selectionAcquired) + { + selectionWindow = owner; + TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner); + } + } +} + +/************************************************************************** + * X11DRV_CLIPBOARD_IsFormatAvailable + * + * Checks if the specified format is available in the current selection + * Only invoked when WINE is not the selection owner + */ +BOOL X11DRV_CLIPBOARD_IsFormatAvailable(UINT wFormat) +{ + Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); + Window ownerPrimary = TSXGetSelectionOwner(display,XA_PRIMARY); + Window ownerClipboard = TSXGetSelectionOwner(display,xaClipboard); + + /* + * If the selection has not been previously cached, or the selection has changed, + * try and cache the list of available selection targets from the current selection. + */ + if ( !cSelectionTargets || (PrimarySelectionOwner != ownerPrimary) + || (ClipboardSelectionOwner != ownerClipboard) ) + { + /* + * First try cacheing the CLIPBOARD selection. + * If unavailable try PRIMARY. + */ + if ( X11DRV_CLIPBOARD_CacheDataFormats(xaClipboard) == 0 ) + { + X11DRV_CLIPBOARD_CacheDataFormats(XA_PRIMARY); + } + + ClipboardSelectionOwner = ownerClipboard; + PrimarySelectionOwner = ownerPrimary; + } + + /* Exit if there is no selection */ + if ( !ownerClipboard && !ownerPrimary ) + return FALSE; + + if ( wFormat == CF_TEXT ) + wFormat = CF_OEMTEXT; + + /* Check if the format is available in the clipboard cache */ + if ( CLIPBOARD_IsPresent(wFormat) ) + return TRUE; + + /* + * Many X client apps (such as XTerminal) don't support being queried + * for the "TARGETS" target atom. To handle such clients we must actually + * try to convert the selection to the requested type. + */ + if ( !cSelectionTargets ) + return X11DRV_CLIPBOARD_GetData( wFormat ); + + return FALSE; +} + +/************************************************************************** + * X11DRV_CLIPBOARD_RegisterFormat + * + * Registers a custom X clipboard format + * Returns: TRUE - success, FALSE - failure + */ +BOOL X11DRV_CLIPBOARD_RegisterFormat( LPCSTR FormatName ) +{ + Atom prop = None; + char str[256]; + + /* + * If an X atom is registered for this format, return that + * Otherwise register a new atom. + */ + if (FormatName) + { + /* Add a WINE specific prefix to the format */ + strcpy(str, FMT_PREFIX); + strncat(str, FormatName, sizeof(str) - strlen(FMT_PREFIX)); + prop = TSXInternAtom(display, str, False); + } + + return (prop) ? TRUE : FALSE; +} + +/************************************************************************** + * X11DRV_CLIPBOARD_IsSelectionowner + * + * Returns: TRUE - We(WINE) own the selection, FALSE - Selection not owned by us + */ +BOOL X11DRV_CLIPBOARD_IsSelectionowner() +{ + return selectionAcquired; +} + /************************************************************************** * X11DRV_CLIPBOARD_SetData + * + * We don't need to do anything special here since the clipboard code + * maintains the cache. + * */ void X11DRV_CLIPBOARD_SetData(UINT wFormat) { - Window owner; - - /* Acquire X selection if text format */ - - if( !selectionAcquired && - (wFormat == CF_TEXT || wFormat == CF_OEMTEXT) ) - { - WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() ); - owner = X11DRV_WND_FindXWindow(tmpWnd ); - - TSXSetSelectionOwner(display,XA_PRIMARY, owner, CurrentTime); - if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner ) - { - selectionAcquired = True; - selectionWindow = owner; - - TRACE("Grabbed X selection, owner=(%08x)\n", - (unsigned) owner); - } - WIN_ReleaseWndPtr(tmpWnd); - } + /* Make sure we have acquired the X selection */ + X11DRV_CLIPBOARD_Acquire(); } /************************************************************************** * X11DRV_CLIPBOARD_GetData * + * This method is invoked only when we DO NOT own the X selection + * * NOTE: Clipboard driver doesn't get requests for CF_TEXT data, only - * for CF_OEMTEXT. + * for CF_OEMTEXT. + * We always get the data from the selection client each time, + * since we have no way of determining if the data in our cache is stale. */ BOOL X11DRV_CLIPBOARD_GetData(UINT wFormat) { BOOL bRet = selectionAcquired; + HWND hWndClipWindow = GetOpenClipboardWindow(); HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow(); WND* wnd = NULL; + LPWINE_CLIPFORMAT lpFormat; - if( wFormat != CF_OEMTEXT ) return FALSE; - - if( !bRet && (wnd = WIN_FindWndPtr(hWnd)) ) + if( !selectionAcquired && (wnd = WIN_FindWndPtr(hWnd)) ) { XEvent xe; + Atom propRequest; Window w = X11DRV_WND_FindXWindow(wnd); + WIN_ReleaseWndPtr(wnd); + wnd = NULL; - TRACE("Requesting XA_STRING selection...\n"); + /* Map the format ID requested to an X selection property. + * If the format is in the cache, use the atom associated + * with it. + */ + + lpFormat = CLIPBOARD_LookupFormat( wFormat ); + if (lpFormat && lpFormat->wDataPresent && lpFormat->drvData) + propRequest = (Atom)lpFormat->drvData; + else + propRequest = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat); - EnterCriticalSection( &X11DRV_CritSection ); - XConvertSelection(display, XA_PRIMARY, XA_STRING, - XInternAtom(display, "PRIMARY_TEXT", False), - w, CurrentTime); + if (propRequest) + { + TRACE("Requesting %s selection from %s...\n", + TSXGetAtomName(display, propRequest), + TSXGetAtomName(display, selectionCacheSrc) ); - /* wait until SelectionNotify is received */ + EnterCriticalSection( &X11DRV_CritSection ); + XConvertSelection(display, selectionCacheSrc, propRequest, + TSXInternAtom(display, "SELECTION_DATA", False), + w, CurrentTime); + + /* wait until SelectionNotify is received */ + + while( TRUE ) + { + if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) ) + if( xe.xselection.selection == selectionCacheSrc ) + break; + } + LeaveCriticalSection( &X11DRV_CritSection ); + + /* + * Read the contents of the X selection property into WINE's + * clipboard cache converting the selection to be compatible if possible. + */ + bRet = X11DRV_CLIPBOARD_ReadSelection( wFormat, + xe.xselection.requestor, + xe.xselection.property, + xe.xselection.target); + } + else + bRet = FALSE; - while( TRUE ) - { - if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) ) - if( xe.xselection.selection == XA_PRIMARY ) - break; - } - LeaveCriticalSection( &X11DRV_CritSection ); - - if (xe.xselection.target != XA_STRING) - X11DRV_CLIPBOARD_ReadSelection( 0, None ); - else - X11DRV_CLIPBOARD_ReadSelection( xe.xselection.requestor, - xe.xselection.property ); - - /* treat Unix text as CF_OEMTEXT */ - - bRet = (BOOL)ClipFormats[CF_OEMTEXT-1].wDataPresent; - - TRACE("\tpresent CF_OEMTEXT = %i\n", bRet ); - WIN_ReleaseWndPtr(wnd); + TRACE("\tpresent %s = %i\n", CLIPBOARD_GetFormatName(wFormat), bRet ); } + return bRet; } /************************************************************************** * X11DRV_CLIPBOARD_ResetOwner * - * Called from DestroyWindow(). + * Called from DestroyWindow() to prevent X selection from being lost when + * a top level window is destroyed, by switching ownership to another top + * level window. + * Any top level window can own the selection. See X11DRV_CLIPBOARD_Acquire + * for a more detailed description of this. */ void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar) { - LPWINE_CLIPFORMAT lpFormat = ClipFormats; + HWND hWndClipOwner = 0; + Window XWnd = X11DRV_WND_GetXWindow(pWnd); + Atom xaClipboard; + BOOL bLostSelection = FALSE; - if(bFooBar && X11DRV_WND_GetXWindow(pWnd)) - return; + /* There is nothing to do if we don't own the selection, + * or if the X window which currently owns the selecion is different + * from the one passed in. + */ + if ( !selectionAcquired || XWnd != selectionWindow + || selectionWindow == None ) + return; - if(!bFooBar && !X11DRV_WND_GetXWindow(pWnd)) - return; + if ( (bFooBar && XWnd) || (!bFooBar && !XWnd) ) + return; - TRACE("clipboard owner = %04x, selection = %08x\n", - hWndClipOwner, (unsigned)selectionWindow); + hWndClipOwner = GetClipboardOwner(); + xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); + + TRACE("clipboard owner = %04x, selection window = %08x\n", + hWndClipOwner, (unsigned)selectionWindow); - if( pWnd->hwndSelf == hWndClipOwner) +#if(0) + /* Check if all formats are already in the clipboard cache */ + if( !CLIPBOARD_IsCacheRendered() ) { SendMessage16(hWndClipOwner,WM_RENDERALLFORMATS,0,0L); /* check if all formats were rendered */ - - while(lpFormat) - { - if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 ) - { - TRACE("\tdata missing for clipboard format %i\n", - lpFormat->wFormatID); - lpFormat->wDataPresent = 0; - } - lpFormat = lpFormat->NextFormat; - } - hWndClipOwner = 0; + if ( !CLIPBOARD_IsCacheRendered() ) + { + ERR("\tCould not render all formats\n"); + CLIPBOARD_ReleaseOwner(); + } } - +#endif + /* now try to salvage current selection from being destroyed by X */ - if( X11DRV_WND_GetXWindow(pWnd) ) X11DRV_CLIPBOARD_CheckSelection(pWnd); + TRACE("\tchecking %08x\n", (unsigned) XWnd); + + selectionPrevWindow = selectionWindow; + selectionWindow = None; + + if( pWnd->next ) + selectionWindow = X11DRV_WND_GetXWindow(pWnd->next); + else if( pWnd->parent ) + if( pWnd->parent->child != pWnd ) + selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child); + + if( selectionWindow != None ) + { + /* We must pretend that we don't own the selection while making the switch + * since a SelectionClear event will be sent to the last owner. + * If there is no owner X11DRV_CLIPBOARD_ReleaseSelection will do nothing. + */ + int saveSelectionState = selectionAcquired; + selectionAcquired = False; + + TRACE("\tswitching selection from %08x to %08x\n", + (unsigned)selectionPrevWindow, (unsigned)selectionWindow); + + /* Assume ownership for the PRIMARY and CLIPBOARD selection */ + if ( saveSelectionState & S_PRIMARY ) + TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime); + + TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime); + + /* Lose the selection if something went wrong */ + if ( ( (saveSelectionState & S_PRIMARY) && + (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) ) + || (TSXGetSelectionOwner(display, xaClipboard) != selectionWindow) ) + { + bLostSelection = TRUE; + goto END; + } + else + { + /* Update selection state */ + selectionAcquired = saveSelectionState; + if (saveSelectionState & S_PRIMARY) + PrimarySelectionOwner = selectionWindow; + + ClipboardSelectionOwner = selectionWindow; + } + } + else + { + bLostSelection = TRUE; + goto END; + } + +END: + if (bLostSelection) + { + /* Empty the windows clipboard. + * We should pretend that we still own the selection BEFORE calling + * EmptyClipboard() since otherwise this has the side effect of + * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection + * to be re-acquired by us! + */ + + TRACE("\tLost the selection! Emptying the clipboard...\n"); + + OpenClipboard(NULL); + selectionAcquired = (S_PRIMARY | S_CLIPBOARD); + EmptyClipboard(); + selectionAcquired = S_NOSELECTION; + + CloseClipboard(); + + /* Give up ownership of the windows clipboard */ + CLIPBOARD_ReleaseOwner(); + ClipboardSelectionOwner = PrimarySelectionOwner = 0; + selectionWindow = 0; + } } #endif /* !defined(X_DISPLAY_MISSING) */ diff --git a/windows/x11drv/event.c b/windows/x11drv/event.c index 9696ed78ee5..e55924b73be 100644 --- a/windows/x11drv/event.c +++ b/windows/x11drv/event.c @@ -835,64 +835,196 @@ static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event ) /*********************************************************************** * EVENT_SelectionRequest + * Note: We only receive this event when WINE owns the X selection */ static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event ) { XSelectionEvent result; - Atom rprop = None; - Window request = event->requestor; + 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; - if(event->target == XA_STRING) + /* + * 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); + + /* + * 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 ( ( (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 ); + } + else if(event->target == XA_STRING) /* treat CF_TEXT as Unix text */ { HANDLE16 hText; LPSTR text; int size,i,j; - rprop = event->property; + char* lpstr = 0; - if( rprop == None ) - rprop = event->target; + hText = GetClipboardData16(CF_TEXT); + text = GlobalLock16(hText); + size = GlobalSize16(hText); - if( event->selection != XA_PRIMARY ) - rprop = None; - else - if( !CLIPBOARD_IsPresent(CF_OEMTEXT) ) - rprop = None; - else + /* remove carriage returns */ + + lpstr = (char*)HEAP_xalloc( GetProcessHeap(), 0, size-- ); + for(i=0,j=0; i < size && text[i]; i++ ) { - /* open to make sure that clipboard is available */ - - BOOL couldOpen = OpenClipboard( hWnd ); - 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'; - - TSXChangeProperty(display, request, rprop, - XA_STRING, 8, PropModeReplace, - lpstr, j); - HeapFree( GetProcessHeap(), 0, lpstr ); - - /* close only if we opened before */ - - if(couldOpen) CloseClipboard(); + 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 ); } + else if(event->target == XA_PIXMAP) /* Convert DIB's to Pixmaps */ + { + FIXME_(event)("DIB to PIXMAP conversion not yet implemented!\n"); + rprop = None; + } + else /* For other data types (WCF_*) simply copy the data to X without conversion */ + { + 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 */ + } + +END: + /* close clipboard only if we opened before */ + if(couldOpen) CloseClipboard(); if( rprop == None) - TRACE_(event)("Request for %s ignored\n", TSXGetAtomName(display,event->target)); + TRACE_(event)("\tRequest ignored\n"); /* reply to sender */ @@ -911,8 +1043,10 @@ static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event ) */ static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event ) { - if (event->selection != XA_PRIMARY) return; - X11DRV_CLIPBOARD_ReleaseSelection( event->window, hWnd ); + Atom xaClipboard = XInternAtom(display, "CLIPBOARD", False); + + if (event->selection == XA_PRIMARY || event->selection == xaClipboard) + X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd ); } @@ -1210,7 +1344,7 @@ static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event ) bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED; if ( !Options.managed || !bIsDisabled ) - PostMessage16( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 ); + PostMessage16( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 ); } else if ( event->message_type == dndProtocol && (event->data.l[0] == DndFile || event->data.l[0] == DndFiles) ) diff --git a/windows/x11drv/init.c b/windows/x11drv/init.c index 1b6be7cea52..f423702109b 100644 --- a/windows/x11drv/init.c +++ b/windows/x11drv/init.c @@ -28,9 +28,13 @@ USER_DRIVER X11DRV_USER_Driver = CLIPBOARD_DRIVER X11DRV_CLIPBOARD_Driver = { - X11DRV_CLIPBOARD_Empty, + X11DRV_CLIPBOARD_Acquire, + X11DRV_CLIPBOARD_Release, X11DRV_CLIPBOARD_SetData, X11DRV_CLIPBOARD_GetData, + X11DRV_CLIPBOARD_IsFormatAvailable, + X11DRV_CLIPBOARD_RegisterFormat, + X11DRV_CLIPBOARD_IsSelectionowner, X11DRV_CLIPBOARD_ResetOwner };