From b2df5f9f198d862d08f021deadaf81b4ce4d50b2 Mon Sep 17 00:00:00 2001 From: Ulrich Czekalla Date: Mon, 23 Jun 2003 23:02:02 +0000 Subject: [PATCH] - use global atoms for the format ids - add timeout when calling XCheckTypedWindowEvent - fix broken IsClipboardFormatAvailable; it tried to do a trick with EnumClipboardFormats by making incorrect assumptions - in X11DRV_IsClipboardFormatAvailable do a quick exit if no one owns the selection - add 1 second *minimum* time lapse between XSelectionOwner calls - sync clipboard ownership between different wine processes - prevents apps from getting into wierd state where they thought they didn't own the selection but they did and as a result queried themselves for available selection data --- dlls/user/user32.spec | 6 - dlls/user/user_main.c | 6 +- dlls/x11drv/clipboard.c | 2830 +++++++++++++++++++++----------- dlls/x11drv/event.c | 426 ++--- dlls/x11drv/x11drv.h | 60 +- dlls/x11drv/x11drv.spec | 24 +- dlls/x11drv/x11drv_main.c | 7 + include/clipboard.h | 32 +- include/user.h | 20 +- include/wine/server_protocol.h | 35 +- server/Makefile.in | 1 + server/clipboard.c | 160 ++ server/protocol.def | 25 + server/request.h | 2 + server/thread.c | 1 + server/trace.c | 21 + server/user.h | 4 + windows/clipboard.c | 1638 ++++++------------ windows/win.c | 3 + 19 files changed, 2760 insertions(+), 2541 deletions(-) create mode 100644 server/clipboard.c diff --git a/dlls/user/user32.spec b/dlls/user/user32.spec index 6b3242fff66..112f8b26650 100644 --- a/dlls/user/user32.spec +++ b/dlls/user/user32.spec @@ -715,12 +715,6 @@ ################################################################ # Wine dll separation hacks, these will go away, don't use them # -@ cdecl CLIPBOARD_DeleteRecord(ptr long) -@ cdecl CLIPBOARD_EmptyCache(long) -@ cdecl CLIPBOARD_GetFormatName(long ptr long) -@ cdecl CLIPBOARD_IsPresent(long) -@ cdecl CLIPBOARD_LookupFormat(long) -@ cdecl CLIPBOARD_ReleaseOwner() @ cdecl DCE_InvalidateDCE(long ptr) @ cdecl HOOK_CallHooks(long long long long long) @ cdecl NC_GetInsideRect(long ptr) diff --git a/dlls/user/user_main.c b/dlls/user/user_main.c index 24866d27153..9e68ae819a6 100644 --- a/dlls/user/user_main.c +++ b/dlls/user/user_main.c @@ -89,13 +89,15 @@ static BOOL load_driver(void) GET_USER_FUNC(GetScreenSaveActive); GET_USER_FUNC(SetScreenSaveActive); GET_USER_FUNC(AcquireClipboard); - GET_USER_FUNC(ReleaseClipboard); + GET_USER_FUNC(EmptyClipboard); GET_USER_FUNC(SetClipboardData); GET_USER_FUNC(GetClipboardData); + GET_USER_FUNC(CountClipboardFormats); + GET_USER_FUNC(EnumClipboardFormats); GET_USER_FUNC(IsClipboardFormatAvailable); GET_USER_FUNC(RegisterClipboardFormat); GET_USER_FUNC(GetClipboardFormatName); - GET_USER_FUNC(IsSelectionOwner); + GET_USER_FUNC(EndClipboardUpdate); GET_USER_FUNC(ResetSelectionOwner); GET_USER_FUNC(ChangeDisplaySettingsExW); GET_USER_FUNC(EnumDisplaySettingsExW); diff --git a/dlls/x11drv/clipboard.c b/dlls/x11drv/clipboard.c index eea0cde4d1e..4fa8c3bdef8 100644 --- a/dlls/x11drv/clipboard.c +++ b/dlls/x11drv/clipboard.c @@ -4,6 +4,7 @@ * Copyright 1994 Martin Ayotte * 1996 Alex Korobka * 1999 Noel Borthwick + * 2003 Ulrich Czekalla for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -76,416 +77,1273 @@ #include "win.h" #include "x11drv.h" #include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/server.h" WINE_DEFAULT_DEBUG_CHANNEL(clipboard); +#define HGDIOBJ_32(handle16) ((HGDIOBJ)(ULONG_PTR)(handle16)) + /* Maximum wait time for slection notify */ -#define MAXSELECTIONNOTIFYWAIT 5 +#define SELECTION_RETRIES 500 /* wait for .1 seconds */ +#define SELECTION_WAIT 1000 /* us */ +/* Minium seconds that must lapse between owner queries */ +#define OWNERQUERYLAPSETIME 1 /* Selection masks */ - #define S_NOSELECTION 0 #define S_PRIMARY 1 #define S_CLIPBOARD 2 -/* X selection context info */ +#define _CLIPBOARD "CLIPBOARD" /* CLIPBOARD atom name */ +#define _TARGETS "TARGETS" +#define _MULTIPLE "MULTIPLE" +#define _SELECTIONDATA "SELECTION_DATA" + +Atom xaClipboard = None; +Atom xaTargets = None; +Atom xaMultiple = None; +Atom xaSelectionData = None; -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 BOOL clearAllSelections = FALSE; /* Always lose all selections */ +static Atom selectionCacheSrc = XA_PRIMARY; /* The selection source from which the clipboard cache was filled */ 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 HANDLE selectionClearEvent = 0;/* Synchronization object used to block until server is started */ -typedef struct tagPROPERTY +INT X11DRV_RegisterClipboardFormat(LPCSTR FormatName); +void X11DRV_EmptyClipboard(void); +void X11DRV_EndClipboardUpdate(void); +HANDLE X11DRV_CLIPBOARD_ImportClipboardData(LPBYTE lpdata, UINT cBytes); +HANDLE X11DRV_CLIPBOARD_ImportEnhMetaFile(LPBYTE lpdata, UINT cBytes); +HANDLE X11DRV_CLIPBOARD_ImportMetaFilePict(LPBYTE lpdata, UINT cBytes); +HANDLE X11DRV_CLIPBOARD_ImportXAPIXMAP(LPBYTE lpdata, UINT cBytes); +HANDLE X11DRV_CLIPBOARD_ImportXAString(LPBYTE lpdata, UINT cBytes); +HANDLE X11DRV_CLIPBOARD_ExportClipboardData(LPWINE_CLIPDATA lpData, LPDWORD lpBytes); +HANDLE X11DRV_CLIPBOARD_ExportXAString(LPWINE_CLIPDATA lpData, LPDWORD lpBytes); +HANDLE X11DRV_CLIPBOARD_ExportXAPIXMAP(LPWINE_CLIPDATA lpdata, LPDWORD lpBytes); +HANDLE X11DRV_CLIPBOARD_ExportMetaFilePict(LPWINE_CLIPDATA lpdata, LPDWORD lpBytes); +HANDLE X11DRV_CLIPBOARD_ExportEnhMetaFile(LPWINE_CLIPDATA lpdata, LPDWORD lpBytes); +UINT X11DRV_CLIPBOARD_InsertClipboardFormat(LPCSTR FormatName, LPCSTR PropertyName); +static BOOL X11DRV_CLIPBOARD_ReadSelection(LPWINE_CLIPFORMAT lpData, Window w, Atom prop); +static BOOL X11DRV_CLIPBOARD_RenderText(UINT wFormatID); +static void X11DRV_CLIPBOARD_FreeData(LPWINE_CLIPDATA lpData); +static BOOL X11DRV_CLIPBOARD_IsSelectionOwner(void); +static int X11DRV_CLIPBOARD_QueryAvailableData(LPCLIPBOARDINFO lpcbinfo); +static BOOL X11DRV_CLIPBOARD_ReadClipboardData(UINT wFormat); +static BOOL X11DRV_CLIPBOARD_RenderFormat(LPWINE_CLIPDATA lpData); +static HANDLE X11DRV_CLIPBOARD_SerializeMetafile(INT wformat, HANDLE hdata, LPDWORD lpcbytes, BOOL out); +static BOOL X11DRV_CLIPBOARD_SynthesizeData(UINT wFormatID); +static BOOL X11DRV_CLIPBOARD_RenderSynthesizedFormat(LPWINE_CLIPDATA lpData); + +/* Clipboard formats + * WARNING: This data ordering is dependent on the WINE_CLIPFORMAT structure + * declared in clipboard.h + */ +WINE_CLIPFORMAT ClipFormats[] = { - struct tagPROPERTY *next; - Atom atom; - Pixmap pixmap; -} PROPERTY; + { CF_TEXT, "WCF_TEXT", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, NULL, &ClipFormats[1]}, -static PROPERTY *prop_head; + { CF_BITMAP, "WCF_BITMAP", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[0], &ClipFormats[2]}, + { CF_METAFILEPICT, "WCF_METAFILEPICT", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportMetaFilePict, + X11DRV_CLIPBOARD_ExportMetaFilePict, &ClipFormats[1], &ClipFormats[3]}, + + { CF_SYLK, "WCF_SYLK", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[2], &ClipFormats[4]}, + + { CF_DIF, "WCF_DIF", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[3], &ClipFormats[5]}, + + { CF_TIFF, "WCF_TIFF", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[4], &ClipFormats[6]}, + + { CF_OEMTEXT, "WCF_OEMTEXT", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[5], &ClipFormats[7]}, + + { CF_DIB, "WCF_DIB", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportXAPIXMAP, + X11DRV_CLIPBOARD_ExportXAPIXMAP, &ClipFormats[6], &ClipFormats[8]}, + + { CF_PALETTE, "WCF_PALETTE", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[7], &ClipFormats[9]}, + + { CF_PENDATA, "WCF_PENDATA", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[8], &ClipFormats[10]}, + + { CF_RIFF, "WCF_RIFF", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[9], &ClipFormats[11]}, + + { CF_WAVE, "WCF_WAVE", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[10], &ClipFormats[12]}, + + { CF_UNICODETEXT, "WCF_UNICODETEXT", XA_STRING, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportXAString, + X11DRV_CLIPBOARD_ExportXAString, &ClipFormats[11], &ClipFormats[13]}, + + { CF_ENHMETAFILE, "WCF_ENHMETAFILE", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportEnhMetaFile, + X11DRV_CLIPBOARD_ExportEnhMetaFile, &ClipFormats[12], &ClipFormats[14]}, + + { CF_HDROP, "WCF_HDROP", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[13], &ClipFormats[15]}, + + { CF_LOCALE, "WCF_LOCALE", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[14], &ClipFormats[16]}, + + { CF_DIBV5, "WCF_DIBV5", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[15], &ClipFormats[17]}, + + { CF_OWNERDISPLAY, "WCF_OWNERDISPLAY", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[16], &ClipFormats[18]}, + + { CF_DSPTEXT, "WCF_DSPTEXT", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[17], &ClipFormats[19]}, + + { CF_DSPBITMAP, "WCF_DSPBITMAP", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[18], &ClipFormats[20]}, + + { CF_DSPMETAFILEPICT, "WCF_DSPMETAFILEPICT", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[19], &ClipFormats[21]}, + + { CF_DSPENHMETAFILE, "WCF_DSPENHMETAFILE", 0, CF_FLAG_BUILTINFMT, X11DRV_CLIPBOARD_ImportClipboardData, + X11DRV_CLIPBOARD_ExportClipboardData, &ClipFormats[20], NULL} +}; + + +/* Maps X properties to Windows formats */ +PROPERTYFORMATMAP PropertyFormatMap[] = +{ + { "text/rtf", "Rich Text Format" }, + /* Temporarily disable text/html because Evolution incorrectly pastes strings with extra nulls */ + /*{ "text/html", "HTML Format" },*/ + { "image/gif", "GIF" }, +}; + + +/* Maps equivalent X properties. It is assumed that lpszProperty must already + be in ClipFormats or PropertyFormatMap. */ +PROPERTYALIASMAP PropertyAliasMap[] = +{ + /* lpszProperty, Alias */ + { "text/rtf", 0, "text/richtext", 0 }, +}; + + +/* + * Cached clipboard data. + */ +static LPWINE_CLIPDATA ClipData = NULL; +static UINT ClipDataCount = 0; + +/* + * Clipboard sequence number + */ +UINT wSeqNo = 0; /************************************************************************** - * X11DRV_CLIPBOARD_MapPropertyToFormat - * - * Map an X selection property type atom name to a windows clipboard format ID - */ -UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName) -{ - /* - * 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_UNICODETEXT; - 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_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; -} + * Internal Clipboard implementation methods + **************************************************************************/ /************************************************************************** - * X11DRV_CLIPBOARD_MapFormatToProperty - * - * Map a windows clipboard format ID to an X selection property atom + * X11DRV_InitClipboard */ -Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT wFormat) +BOOL X11DRV_InitClipboard(Display *display) { - Atom prop = None; - - switch (wFormat) - { - /* We support only CF_UNICODETEXT, other formats are synthesized */ - case CF_OEMTEXT: - case CF_TEXT: - return None; - - case CF_UNICODETEXT: - prop = XA_STRING; - break; - - 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_IsSelectionOwner() && !CLIPBOARD_IsPresent(wFormat) ) - { - prop = XA_PIXMAP; - break; - } - /* Fall through 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]; - int plen = strlen(FMT_PREFIX); - - strcpy(str, FMT_PREFIX); - - if (CLIPBOARD_GetFormatName(wFormat, str + plen, sizeof(str) - plen)) - prop = TSXInternAtom(thread_display(), str, False); - - break; - } - } - - if (prop == None) - TRACE("\tNo mapping to X property for Windows clipboard format %d(%s)\n", - wFormat, CLIPBOARD_GetFormatName(wFormat, NULL, 0)); - - return prop; -} - -/************************************************************************** - * X11DRV_CLIPBOARD_IsNativeProperty - * - * Checks if a property is a native Wine property type - */ -BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop) -{ - char *itemFmtName = TSXGetAtomName(thread_display(), prop); - BOOL bRet = FALSE; - - if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) ) - bRet = TRUE; - - TSXFree(itemFmtName); - return bRet; -} - - -/************************************************************************** - * X11DRV_CLIPBOARD_LaunchServer - * Launches the clipboard server. This is called from X11DRV_CLIPBOARD_ResetOwner - * when the selection can no longer be recyled to another top level window. - * In order to make the selection persist after Wine shuts down a server - * process is launched which services subsequent selection requests. - */ -BOOL X11DRV_CLIPBOARD_LaunchServer() -{ - int iWndsLocks; - char clearSelection[8] = "0"; - int persistent_selection = 1; + INT i; HKEY hkey; - int fd[2], err; + PROPERTYALIASMAP *lpalias; + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + + xaClipboard = TSXInternAtom( display, _CLIPBOARD, FALSE ); + xaTargets = TSXInternAtom( display, _TARGETS, FALSE ); + xaMultiple = TSXInternAtom(display, _MULTIPLE, False); + xaSelectionData = TSXInternAtom(display, _SELECTIONDATA, False); - /* If persistant selection has been disabled in the .winerc Clipboard section, - * don't launch the server - */ if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Clipboard", &hkey)) { char buffer[20]; DWORD type, count = sizeof(buffer); - if(!RegQueryValueExA(hkey, "PersistentSelection", 0, &type, buffer, &count)) - persistent_selection = atoi(buffer); - - /* Get the clear selection preference */ - count = sizeof(clearSelection); - RegQueryValueExA(hkey, "ClearAllSelections", 0, &type, clearSelection, &count); - RegCloseKey(hkey); + if(!RegQueryValueExA(hkey, "ClearAllSelections", 0, &type, buffer, &count)) + clearAllSelections = atoi(buffer); + RegCloseKey(hkey); } - if ( !persistent_selection ) - return FALSE; - /* 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. - */ - - if(pipe(fd) == -1) return FALSE; - fcntl(fd[1], F_SETFD, 1); /* set close on exec */ - - selectionWindow = selectionPrevWindow; - if ( !fork() ) + /* Register known formats */ + while (lpFormat) { - /* NOTE: This code only executes in the context of the child process - * Do note make any Wine specific calls here. - */ - int dbgClasses = 0; - char selMask[8], dbgClassMask[8]; + if (!lpFormat->wFormatID) + lpFormat->wFormatID = GlobalAddAtomA(lpFormat->Name); - close(fd[0]); - sprintf(selMask, "%d", selectionAcquired); + if (!lpFormat->drvData) + lpFormat->drvData = TSXInternAtom(display, lpFormat->Name, False); - /* Build the debug class mask to pass to the server, by inheriting - * the settings for the clipboard debug channel. - */ - dbgClasses |= FIXME_ON(clipboard) ? 1 : 0; - dbgClasses |= ERR_ON(clipboard) ? 2 : 0; - dbgClasses |= WARN_ON(clipboard) ? 4 : 0; - dbgClasses |= TRACE_ON(clipboard) ? 8 : 0; - sprintf(dbgClassMask, "%d", dbgClasses); - - /* Exec the clipboard server passing it the selection and debug class masks */ - execl( BINDIR "/wineclipsrv", "wineclipsrv", - selMask, dbgClassMask, clearSelection, NULL ); - execlp( "wineclipsrv", "wineclipsrv", selMask, dbgClassMask, clearSelection, NULL ); - - /* Exec Failed! */ - perror("Could not start Wine clipboard server"); - write(fd[1], &err, sizeof(err)); - _exit( 1 ); /* Exit the child process */ + lpFormat = lpFormat->NextFormat; } - close(fd[1]); - if(read(fd[0], &err, sizeof(err)) > 0) { /* exec failed */ - close(fd[0]); - return FALSE; - } - close(fd[0]); + /* Register known mapping between window formats and X properties */ + for (i = 0; i < sizeof(PropertyFormatMap)/sizeof(PROPERTYFORMATMAP); i++) + X11DRV_CLIPBOARD_InsertClipboardFormat(PropertyFormatMap[i].lpszFormat, + PropertyFormatMap[i].lpszProperty); - /* Wait until the clipboard server acquires the selection. - * We must release the windows lock to enable Wine to process - * selection messages in response to the servers requests. - */ - - iWndsLocks = WIN_SuspendWndsLock(); - - /* We must wait until the server finishes acquiring the selection, - * before proceeding, otherwise the window which owns the selection - * will be destroyed prematurely! - * Create a non-signalled, auto-reset event which will be set by - * X11DRV_CLIPBOARD_ReleaseSelection, and wait until this gets - * signalled before proceeding. - */ - - if ( !(selectionClearEvent = CreateEventA(NULL, FALSE, FALSE, NULL)) ) - ERR("Could not create wait object. Clipboard server won't start!\n"); - else + /* Register known mapping between X properties */ + for (i = 0; i < sizeof(PropertyAliasMap)/sizeof(PROPERTYALIASMAP); i++) { - /* Wait until we lose the selection, timing out after a minute */ - DWORD start_time, timeout, elapsed, ret; - - TRACE("Waiting for clipboard server to acquire selection\n"); - - timeout = 60000; - start_time = GetTickCount(); - elapsed=0; - do - { - ret = MsgWaitForMultipleObjects( 1, &selectionClearEvent, FALSE, timeout - elapsed, QS_ALLINPUT ); - if (ret != WAIT_OBJECT_0+1) - break; - elapsed = GetTickCount() - start_time; - if (elapsed > timeout) - break; - } - while (1); - if ( ret != WAIT_OBJECT_0 ) - TRACE("Server could not acquire selection, or a timeout occurred!\n"); - else - TRACE("Server successfully acquired selection\n"); - - /* Release the event */ - CloseHandle(selectionClearEvent); - selectionClearEvent = 0; + lpalias = &PropertyAliasMap[i]; + lpalias->drvDataProperty = TSXInternAtom(display, lpalias->lpszProperty, False); + lpalias->drvDataAlias = TSXInternAtom(display, lpalias->lpszAlias, False); } - WIN_RestoreWndsLock(iWndsLocks); - return TRUE; } /************************************************************************** - * X11DRV_CLIPBOARD_CacheDataFormats + * X11DRV_CLIPBOARD_LookupFormat + */ +LPWINE_CLIPFORMAT X11DRV_CLIPBOARD_LookupFormat(WORD wID) +{ + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + + while(lpFormat) + { + if (lpFormat->wFormatID == wID) + break; + + lpFormat = lpFormat->NextFormat; + } + + return lpFormat; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_LookupProperty + */ +LPWINE_CLIPFORMAT X11DRV_CLIPBOARD_LookupProperty(UINT drvData) +{ + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + + while(lpFormat) + { + if (lpFormat->drvData == drvData) + break; + + lpFormat = lpFormat->NextFormat; + } + + return lpFormat; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_LookupAliasProperty + */ +LPWINE_CLIPFORMAT X11DRV_CLIPBOARD_LookupAliasProperty(UINT drvDataAlias) +{ + unsigned int i; + LPWINE_CLIPFORMAT lpFormat = NULL; + + for (i = 0; i < sizeof(PropertyAliasMap)/sizeof(PROPERTYALIASMAP); i++) + { + if (PropertyAliasMap[i].drvDataAlias == drvDataAlias) + { + lpFormat = X11DRV_CLIPBOARD_LookupProperty(PropertyAliasMap[i].drvDataProperty); + break; + } + } + + return lpFormat; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_LookupPropertyAlias + */ +UINT X11DRV_CLIPBOARD_LookupPropertyAlias(UINT drvDataProperty) +{ + unsigned int i; + UINT alias = 0; + + for (i = 0; i < sizeof(PropertyAliasMap)/sizeof(PROPERTYALIASMAP); i++) + { + if (PropertyAliasMap[i].drvDataProperty == drvDataProperty) + { + alias = PropertyAliasMap[i].drvDataAlias; + break; + } + } + + return alias; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_LookupData + */ +LPWINE_CLIPDATA X11DRV_CLIPBOARD_LookupData(DWORD wID) +{ + LPWINE_CLIPDATA lpData = ClipData; + + if (lpData) + { + do + { + if (lpData->wFormatID == wID) + break; + + lpData = lpData->NextData; + } + while(lpData != ClipData); + + if (lpData->wFormatID != wID) + lpData = NULL; + } + + return lpData; +} + + +/************************************************************************** + * InsertClipboardFormat + */ +UINT X11DRV_CLIPBOARD_InsertClipboardFormat(LPCSTR FormatName, LPCSTR PropertyName) +{ + LPWINE_CLIPFORMAT lpFormat; + LPWINE_CLIPFORMAT lpNewFormat; + + /* allocate storage for new format entry */ + lpNewFormat = (LPWINE_CLIPFORMAT) HeapAlloc(GetProcessHeap(), + 0, sizeof(WINE_CLIPFORMAT)); + + if(lpNewFormat == NULL) + { + WARN("No more memory for a new format!\n"); + return 0; + } + + if (!(lpNewFormat->Name = HeapAlloc(GetProcessHeap(), 0, strlen(FormatName)+1))) + { + WARN("No more memory for the new format name!\n"); + HeapFree(GetProcessHeap(), 0, lpNewFormat); + return 0; + } + + strcpy(lpNewFormat->Name, FormatName); + lpNewFormat->wFlags = 0; + lpNewFormat->wFormatID = GlobalAddAtomA(lpNewFormat->Name); + lpNewFormat->drvData = TSXInternAtom(thread_display(), PropertyName, False); + lpNewFormat->lpDrvImportFunc = X11DRV_CLIPBOARD_ImportClipboardData; + lpNewFormat->lpDrvExportFunc = X11DRV_CLIPBOARD_ExportClipboardData; + + /* Link Format */ + lpFormat = ClipFormats; + + while(lpFormat->NextFormat) /* Move to last entry */ + lpFormat = lpFormat->NextFormat; + + lpNewFormat->NextFormat = NULL; + lpFormat->NextFormat = lpNewFormat; + lpNewFormat->PrevFormat = lpFormat; + + TRACE("Registering format(%d): %s drvData(%d): %s\n", + lpNewFormat->wFormatID, + FormatName, + lpNewFormat->drvData, + PropertyName); + + return lpNewFormat->wFormatID; +} + + + + +/************************************************************************** + * X11DRV_CLIPBOARD_GetClipboardInfo + */ +static BOOL X11DRV_CLIPBOARD_GetClipboardInfo(LPCLIPBOARDINFO cbInfo) +{ + BOOL bRet = FALSE; + + SERVER_START_REQ( set_clipboard_info ) + { + req->flags = 0; + + if (wine_server_call_err( req )) + { + ERR("Failed to get clipboard owner.\n"); + } + else + { + cbInfo->hWndOpen = reply->old_clipboard; + cbInfo->hWndOwner = reply->old_owner; + cbInfo->hWndViewer = reply->old_viewer; + cbInfo->seqno = reply->seqno; + cbInfo->flags = reply->flags; + + bRet = TRUE; + } + } + SERVER_END_REQ; + + return bRet; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ReleaseOwnership + */ +static BOOL X11DRV_CLIPBOARD_ReleaseOwnership(void) +{ + BOOL bRet = FALSE; + + SERVER_START_REQ( set_clipboard_info ) + { + req->flags = SET_CB_RELOWNER | SET_CB_SEQNO; + + if (wine_server_call_err( req )) + { + ERR("Failed to set clipboard.\n"); + } + else + { + bRet = TRUE; + } + } + SERVER_END_REQ; + + return bRet; +} + + + +/************************************************************************** + * X11DRV_CLIPBOARD_InsertClipboardData + * + * Caller *must* have the clipboard open and be the owner. + */ +static BOOL X11DRV_CLIPBOARD_InsertClipboardData(UINT wFormat, HANDLE16 hData16, HANDLE hData32, DWORD flags) +{ + LPWINE_CLIPDATA lpData = X11DRV_CLIPBOARD_LookupData(wFormat); + + TRACE("format=%d lpData=%p hData16=%08x hData32=%08x flags=0x%08lx\n", + wFormat, lpData, hData16, (unsigned int)hData32, flags); + + if (lpData) + { + X11DRV_CLIPBOARD_FreeData(lpData); + + lpData->hData16 = hData16; /* 0 is legal, see WM_RENDERFORMAT */ + lpData->hData32 = hData32; + } + else + { + lpData = (LPWINE_CLIPDATA) HeapAlloc(GetProcessHeap(), + 0, sizeof(WINE_CLIPDATA)); + + lpData->wFormatID = wFormat; + lpData->hData16 = hData16; /* 0 is legal, see WM_RENDERFORMAT */ + lpData->hData32 = hData32; + + if (ClipData) + { + LPWINE_CLIPDATA lpPrevData = ClipData->PrevData; + + lpData->PrevData = lpPrevData; + lpData->NextData = ClipData; + + lpPrevData->NextData = lpData; + ClipData->PrevData = lpData; + } + else + { + lpData->NextData = lpData; + lpData->PrevData = lpData; + ClipData = lpData; + } + + ClipDataCount++; + } + + lpData->wFlags = flags; + + return TRUE; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_FreeData + * + * Free clipboard data handle. + */ +static void X11DRV_CLIPBOARD_FreeData(LPWINE_CLIPDATA lpData) +{ + TRACE("%d\n", lpData->wFormatID); + + if ((lpData->wFormatID >= CF_GDIOBJFIRST && + lpData->wFormatID <= CF_GDIOBJLAST) || + lpData->wFormatID == CF_BITMAP || + lpData->wFormatID == CF_DIB || + lpData->wFormatID == CF_PALETTE) + { + if (lpData->hData32) + DeleteObject(lpData->hData32); + + if (lpData->hData16) + DeleteObject(HGDIOBJ_32(lpData->hData16)); + } + else if (lpData->wFormatID == CF_METAFILEPICT) + { + if (lpData->hData32) + { + DeleteMetaFile(((METAFILEPICT *)GlobalLock( lpData->hData32 ))->hMF ); + GlobalFree(lpData->hData32); + + if (lpData->hData16) + /* HMETAFILE16 and HMETAFILE32 are apparently the same thing, + and a shallow copy is enough to share a METAFILEPICT + structure between 16bit and 32bit clipboards. The MetaFile + should of course only be deleted once. */ + GlobalFree16(lpData->hData16); + } + + if (lpData->hData16) + { + METAFILEPICT16* lpMetaPict = (METAFILEPICT16 *) GlobalLock16(lpData->hData16); + + if (lpMetaPict) + { + DeleteMetaFile16(lpMetaPict->hMF); + lpMetaPict->hMF = 0; + } + + GlobalFree16(lpData->hData16); + } + } + else if (lpData->wFormatID == CF_ENHMETAFILE) + { + if (lpData->hData32) + DeleteEnhMetaFile(lpData->hData32); + } + else if (lpData->wFormatID < CF_PRIVATEFIRST || + lpData->wFormatID > CF_PRIVATELAST) + { + if (lpData->hData32) + GlobalFree(lpData->hData32); + + if (lpData->hData16) + GlobalFree16(lpData->hData16); + } + + lpData->hData16 = 0; + lpData->hData32 = 0; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_UpdateCache + */ +static BOOL X11DRV_CLIPBOARD_UpdateCache(LPCLIPBOARDINFO lpcbinfo) +{ + BOOL bret = TRUE; + + if (!X11DRV_CLIPBOARD_IsSelectionOwner()) + { + if (!X11DRV_CLIPBOARD_GetClipboardInfo(lpcbinfo)) + { + ERR("Failed to retrieve clipboard information.\n"); + bret = FALSE; + } + else if (wSeqNo < lpcbinfo->seqno) + { + X11DRV_EmptyClipboard(); + + if (!X11DRV_CLIPBOARD_QueryAvailableData(lpcbinfo)) + { + ERR("Failed to cache clipboard data owned by another process.\n"); + bret = FALSE; + } + else + { + X11DRV_EndClipboardUpdate(); + } + + wSeqNo = lpcbinfo->seqno; + } + } + + return bret; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_RenderFormat + */ +static BOOL X11DRV_CLIPBOARD_RenderFormat(LPWINE_CLIPDATA lpData) +{ + BOOL bret = TRUE; + + TRACE(" 0x%04x hData32(0x%08x) hData16(0x%08x)\n", + lpData->wFormatID, (unsigned int)lpData->hData32, lpData->hData16); + + if (lpData->hData32 || lpData->hData16) + return bret; /* Already rendered */ + + if (!X11DRV_CLIPBOARD_IsSelectionOwner()) + { + if (!X11DRV_CLIPBOARD_ReadClipboardData(lpData->wFormatID)) + { + ERR("Failed to cache clipboard data owned by another process. Format=%d\n", + lpData->wFormatID); + bret = FALSE; + } + } + else + { + if (lpData->wFlags & CF_FLAG_SYNTHESIZED) + bret = X11DRV_CLIPBOARD_RenderSynthesizedFormat(lpData); + else + { + CLIPBOARDINFO cbInfo; + + if (X11DRV_CLIPBOARD_GetClipboardInfo(&cbInfo) && + cbInfo.hWndOwner) + { + /* Send a WM_RENDERFORMAT message to notify the owner to render the + * data requested into the clipboard. + */ + TRACE("Sending WM_RENDERFORMAT message to hwnd(%p)\n", cbInfo.hWndOwner); + SendMessageW(cbInfo.hWndOwner, WM_RENDERFORMAT, (WPARAM)lpData->wFormatID, 0); + + if (!lpData->hData32 && !lpData->hData16) + bret = FALSE; + } + else + { + ERR("hWndClipOwner is lost!\n"); + bret = FALSE; + } + } + } + + return bret; +} + + +/************************************************************************** + * CLIPBOARD_ConvertText + * Returns number of required/converted characters - not bytes! + */ +static INT CLIPBOARD_ConvertText(WORD src_fmt, void const *src, INT src_size, + WORD dst_fmt, void *dst, INT dst_size) +{ + UINT cp; + + if(src_fmt == CF_UNICODETEXT) + { + switch(dst_fmt) + { + case CF_TEXT: + cp = CP_ACP; + break; + case CF_OEMTEXT: + cp = CP_OEMCP; + break; + default: + return 0; + } + return WideCharToMultiByte(cp, 0, src, src_size, dst, dst_size, NULL, NULL); + } + + if(dst_fmt == CF_UNICODETEXT) + { + switch(src_fmt) + { + case CF_TEXT: + cp = CP_ACP; + break; + case CF_OEMTEXT: + cp = CP_OEMCP; + break; + default: + return 0; + } + return MultiByteToWideChar(cp, 0, src, src_size, dst, dst_size); + } + + if(!dst_size) return src_size; + + if(dst_size > src_size) dst_size = src_size; + + if(src_fmt == CF_TEXT ) + CharToOemBuffA(src, dst, dst_size); + else + OemToCharBuffA(src, dst, dst_size); + + return dst_size; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_RenderSynthesizedFormat + */ +static BOOL X11DRV_CLIPBOARD_RenderSynthesizedFormat(LPWINE_CLIPDATA lpData) +{ + BOOL bret = FALSE; + + if (lpData->wFlags & CF_FLAG_SYNTHESIZED) + { + UINT wFormatID = lpData->wFormatID; + + if (wFormatID == CF_UNICODETEXT || wFormatID == CF_TEXT || wFormatID == CF_OEMTEXT) + bret = X11DRV_CLIPBOARD_RenderText(wFormatID); + else + { + switch (wFormatID) + { + case CF_ENHMETAFILE: + case CF_METAFILEPICT: + case CF_DIB: + case CF_BITMAP: + FIXME("Synthesizing wFormatID(0x%08x) not implemented\n", wFormatID); + break; + + default: + FIXME("Called to synthesize unknown format\n"); + break; + } + } + + lpData->wFlags &= ~CF_FLAG_SYNTHESIZED; + } + + return bret; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_RenderText + * + * Renders text to the clipboard buffer converting between UNIX and DOS formats. + * + * FIXME: Should be a pair of driver functions that convert between OEM text and Windows. + * + */ +static BOOL X11DRV_CLIPBOARD_RenderText(UINT wFormatID) +{ + LPCSTR lpstrS; + LPSTR lpstrT; + HANDLE hData32; + INT src_chars, dst_chars, alloc_size; + LPWINE_CLIPDATA lpSource = NULL; + + TRACE(" %d\n", wFormatID); + + if (X11DRV_CLIPBOARD_LookupData(wFormatID)) + return TRUE; + + if ((lpSource = X11DRV_CLIPBOARD_LookupData(CF_UNICODETEXT))) + { + TRACE("UNICODETEXT -> %d\n", wFormatID); + } + else if ((lpSource = X11DRV_CLIPBOARD_LookupData(CF_TEXT))) + { + TRACE("TEXT -> %d\n", wFormatID); + } + else if ((lpSource = X11DRV_CLIPBOARD_LookupData(CF_OEMTEXT))) + { + TRACE("OEMTEXT -> %d\n", wFormatID); + } + + if (!lpSource) + return FALSE; + + /* First render the source text format */ + if (!X11DRV_CLIPBOARD_RenderFormat(lpSource)) + return FALSE; + + if (lpSource->hData32) + { + lpstrS = (LPSTR)GlobalLock(lpSource->hData32); + } + else + { + lpstrS = (LPSTR)GlobalLock16(lpSource->hData16); + } + + if (!lpstrS) + return FALSE; + + /* Text always NULL terminated */ + if(lpSource->wFormatID == CF_UNICODETEXT) + src_chars = strlenW((LPCWSTR)lpstrS) + 1; + else + src_chars = strlen(lpstrS) + 1; + + /* Calculate number of characters in the destination buffer */ + dst_chars = CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, + src_chars, wFormatID, NULL, 0); + + if (!dst_chars) + return FALSE; + + TRACE("Converting from '%d' to '%d', %i chars\n", + lpSource->wFormatID, wFormatID, src_chars); + + /* Convert characters to bytes */ + if(wFormatID == CF_UNICODETEXT) + alloc_size = dst_chars * sizeof(WCHAR); + else + alloc_size = dst_chars; + + hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | + GMEM_DDESHARE, alloc_size); + + lpstrT = (LPSTR)GlobalLock(hData32); + + if (lpstrT) + { + CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars, + wFormatID, lpstrT, dst_chars); + GlobalUnlock(hData32); + } + + /* Unlock source */ + if (lpSource->hData32) + GlobalUnlock(lpSource->hData32); + else + GlobalUnlock16(lpSource->hData16); + + return X11DRV_CLIPBOARD_InsertClipboardData(wFormatID, 0, hData32, 0); +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ImportXAString + * + * Import XA_STRING, converting the string to CF_UNICODE. + */ +HANDLE X11DRV_CLIPBOARD_ImportXAString(LPBYTE lpdata, UINT cBytes) +{ + LPSTR lpstr; + UINT i, inlcount = 0; + HANDLE hUnicodeText = 0; + + for (i = 0; i <= cBytes; i++) + { + if (lpdata[i] == '\n') + inlcount++; + } + + if ((lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cBytes + inlcount + 1))) + { + UINT count; + UINT text_cp = CP_ACP; + + for (i = 0, inlcount = 0; i <= cBytes; i++) + { + if (lpdata[i] == '\n') + lpstr[inlcount++] = '\r'; + + lpstr[inlcount++] = lpdata[i]; + } + + GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | + LOCALE_RETURN_NUMBER, (WCHAR *)&text_cp, (sizeof(UINT)/sizeof(WCHAR))); + + count = MultiByteToWideChar(text_cp, 0, lpstr, -1, NULL, 0); + hUnicodeText = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, count * sizeof(WCHAR)); + + if(hUnicodeText) + { + WCHAR *textW = GlobalLock(hUnicodeText); + MultiByteToWideChar(text_cp, 0, lpstr, -1, textW, count); + GlobalUnlock(hUnicodeText); + } + + HeapFree(GetProcessHeap(), 0, lpstr); + } + + return hUnicodeText; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ImportXAPIXMAP + * + * Import XA_PIXMAP, converting the image to CF_DIB. + */ +HANDLE X11DRV_CLIPBOARD_ImportXAPIXMAP(LPBYTE lpdata, UINT cBytes) +{ + HANDLE hTargetImage = 0; /* Handle to store the converted DIB */ + Pixmap *pPixmap = (Pixmap *) lpdata; + HWND hwnd = GetOpenClipboardWindow(); + HDC hdc = GetDC(hwnd); + + hTargetImage = X11DRV_DIB_CreateDIBFromPixmap(*pPixmap, hdc, TRUE); + + ReleaseDC(hwnd, hdc); + + return hTargetImage; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ImportMetaFilePict + * + * Import MetaFilePict. + */ +HANDLE X11DRV_CLIPBOARD_ImportMetaFilePict(LPBYTE lpdata, UINT cBytes) +{ + return X11DRV_CLIPBOARD_SerializeMetafile(CF_METAFILEPICT, (HANDLE)lpdata, (LPDWORD)&cBytes, FALSE); +} + + +/************************************************************************** + * X11DRV_ImportEnhMetaFile + * + * Import EnhMetaFile. + */ +HANDLE X11DRV_CLIPBOARD_ImportEnhMetaFile(LPBYTE lpdata, UINT cBytes) +{ + return X11DRV_CLIPBOARD_SerializeMetafile(CF_ENHMETAFILE, (HANDLE)lpdata, (LPDWORD)&cBytes, FALSE); +} + + +/************************************************************************** + * X11DRV_ImportClipbordaData + * + * Generic import clipboard data routine. + */ +HANDLE X11DRV_CLIPBOARD_ImportClipboardData(LPBYTE lpdata, UINT cBytes) +{ + LPVOID lpClipData; + HANDLE hClipData = 0; + + 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, lpdata, cBytes); + GlobalUnlock(hClipData); + } + else + hClipData = 0; + } + + return hClipData; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ExportClipboardData + * + * Generic export clipboard data routine. + */ +HANDLE X11DRV_CLIPBOARD_ExportClipboardData(LPWINE_CLIPDATA lpData, LPDWORD lpBytes) +{ + LPVOID lpClipData; + UINT cBytes = 0; + HANDLE hClipData = 0; + + *lpBytes = 0; /* Assume failure */ + + if (!X11DRV_CLIPBOARD_RenderFormat(lpData)) + ERR("Failed to export %d format\n", lpData->wFormatID); + else + { + cBytes = GlobalSize(lpData->hData32); + + hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cBytes); + + if ((lpClipData = GlobalLock(hClipData))) + { + LPVOID lpdata = GlobalLock(lpData->hData32); + + memcpy(lpClipData, lpdata, cBytes); + *lpBytes = cBytes; + + GlobalUnlock(lpData->hData32); + GlobalUnlock(hClipData); + } + } + + return hClipData; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ExportXAString + * + * Export CF_UNICODE converting the string to XA_STRING + */ +HANDLE X11DRV_CLIPBOARD_ExportXAString(LPWINE_CLIPDATA lpData, LPDWORD lpBytes) +{ + INT i, j; + UINT size; + LPWSTR uni_text; + LPSTR text, lpstr; + UINT text_cp = CP_ACP; + + *lpBytes = 0; /* Assume return has zero bytes */ + + GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTUNIXCODEPAGE | + LOCALE_RETURN_NUMBER, (WCHAR *)&text_cp, (sizeof(UINT)/sizeof(WCHAR))); + + if (!X11DRV_CLIPBOARD_RenderFormat(lpData)) + { + ERR("Failed to export %d format\n", lpData->wFormatID); + return 0; + } + + uni_text = GlobalLock(lpData->hData32); + + size = WideCharToMultiByte(text_cp, 0, uni_text, -1, NULL, 0, NULL, NULL); + + text = HeapAlloc(GetProcessHeap(), 0, size); + if (!text) + return None; + WideCharToMultiByte(text_cp, 0, uni_text, -1, text, size, NULL, NULL); + + /* remove carriage returns */ + + lpstr = (char*)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size-- ); + if(lpstr == NULL) return None; + 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'; + + *lpBytes = j; /* Number of bytes in string */ + + HeapFree(GetProcessHeap(), 0, text); + GlobalUnlock(lpData->hData32); + + return lpstr; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ExportXAPIXMAP + * + * Export CF_DIB to XA_PIXMAP. + */ +HANDLE X11DRV_CLIPBOARD_ExportXAPIXMAP(LPWINE_CLIPDATA lpdata, LPDWORD lpBytes) +{ + HDC hdc; + Pixmap pixmap; + + if (!X11DRV_CLIPBOARD_RenderFormat(lpdata)) + { + ERR("Failed to export %d format\n", lpdata->wFormatID); + return 0; + } + + hdc = GetDC(0); + + /* For convert from packed DIB to Pixmap */ + pixmap = X11DRV_DIB_CreatePixmapFromDIB(lpdata, hdc); + *lpBytes = 4; /* pixmap is a 32bit value */ + + ReleaseDC(0, hdc); + + return (HANDLE) pixmap; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ExportMetaFilePict + * + * Export MetaFilePict. + */ +HANDLE X11DRV_CLIPBOARD_ExportMetaFilePict(LPWINE_CLIPDATA lpdata, LPDWORD lpBytes) +{ + if (!X11DRV_CLIPBOARD_RenderFormat(lpdata)) + { + ERR("Failed to export %d format\n", lpdata->wFormatID); + return 0; + } + + return X11DRV_CLIPBOARD_SerializeMetafile(CF_METAFILEPICT, (HANDLE)lpdata->hData32, + lpBytes, TRUE); +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_ExportEnhMetaFile + * + * Export EnhMetaFile. + */ +HANDLE X11DRV_CLIPBOARD_ExportEnhMetaFile(LPWINE_CLIPDATA lpdata, LPDWORD lpBytes) +{ + if (!X11DRV_CLIPBOARD_RenderFormat(lpdata)) + { + ERR("Failed to export %d format\n", lpdata->wFormatID); + return 0; + } + + return X11DRV_CLIPBOARD_SerializeMetafile(CF_ENHMETAFILE, (HANDLE)lpdata->hData32, + lpBytes, TRUE); +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_QueryTargets + */ +static BOOL X11DRV_CLIPBOARD_QueryTargets(Display *display, Window w, Atom selection, XEvent *xe) +{ + INT i; + Bool res; + + wine_tsx11_lock(); + XConvertSelection(display, selection, xaTargets, xaSelectionData, w, CurrentTime); + wine_tsx11_unlock(); + + /* + * Wait until SelectionNotify is received + */ + for (i = 0; i < SELECTION_RETRIES; i++) + { + wine_tsx11_lock(); + res = XCheckTypedWindowEvent(display, w, SelectionNotify, xe); + wine_tsx11_unlock(); + if (res && xe->xselection.selection == selection) break; + + usleep(SELECTION_WAIT); + } + + /* Verify that the selection returned a valid TARGETS property */ + if ((xe->xselection.target != xaTargets) || (xe->xselection.property == None)) + { + /* Selection owner failed to respond or we missed the SelectionNotify */ + WARN("Failed to retrieve TARGETS for selection %ld.\n", selection); + return FALSE; + } + + return TRUE; +} + + +/************************************************************************** + * X11DRV_CLIPBOARD_QueryAvailableData * * 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 ) +static int X11DRV_CLIPBOARD_QueryAvailableData(LPCLIPBOARDINFO lpcbinfo) { Display *display = thread_display(); - HWND hWnd = 0; - HWND hWndClipWindow = GetOpenClipboardWindow(); XEvent xe; - Atom aTargets; Atom atype=AnyPropertyType; int aformat; unsigned long remain; Atom* targetList=NULL; Window w; - Window ownerSelection = 0; - time_t maxtm; + HWND hWndClipWindow; + unsigned long cSelectionTargets = 0; - TRACE("enter\n"); - /* - * Empty the clipboard cache - */ - CLIPBOARD_EmptyCache(TRUE); + if (selectionAcquired & (S_PRIMARY | S_CLIPBOARD)) + { + ERR("Received request to cache selection but process is owner=(%08x)\n", + (unsigned) selectionWindow); - cSelectionTargets = 0; - selectionCacheSrc = SelectionName; + selectionAcquired = S_NOSELECTION; - hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow(); + if (TSXGetSelectionOwner(display,XA_PRIMARY) == selectionWindow) + selectionAcquired |= S_PRIMARY; - ownerSelection = TSXGetSelectionOwner(display, SelectionName); - if ( !hWnd || (ownerSelection == None) ) - return cSelectionTargets; + if (TSXGetSelectionOwner(display,xaClipboard) == selectionWindow) + selectionAcquired |= S_CLIPBOARD; + + if (!(selectionAcquired == (S_PRIMARY | S_CLIPBOARD))) + { + WARN("Lost selection but process didn't process SelectClear\n"); + selectionWindow = None; + } + else + { + return 0; /* Prevent self request */ + } + } + + if (lpcbinfo->flags & CB_OWNER) + hWndClipWindow = lpcbinfo->hWndOwner; + else if (lpcbinfo->flags & CB_OPEN) + hWndClipWindow = lpcbinfo->hWndOpen; + else + hWndClipWindow = GetActiveWindow(); + + if (!hWndClipWindow) + { + WARN("No window available to retrieve selection!n"); + return 0; + } + + w = X11DRV_get_whole_window(GetAncestor(hWndClipWindow, GA_ROOT)); /* * Query the selection owner for the TARGETS property */ - w = X11DRV_get_whole_window( GetAncestor(hWnd,GA_ROOT) ); - - aTargets = TSXInternAtom(display, "TARGETS", False); - - TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n", - TSXGetAtomName(display, selectionCacheSrc), (unsigned)ownerSelection ); - wine_tsx11_lock(); - XConvertSelection(display, selectionCacheSrc, aTargets, - TSXInternAtom(display, "SELECTION_DATA", False), - w, CurrentTime); - - /* - * Wait until SelectionNotify is received - */ - maxtm = time(NULL) + MAXSELECTIONNOTIFYWAIT; /* Timeout after a maximum wait */ - while( maxtm - time(NULL) > 0 ) - { - if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) ) - if( xe.xselection.selection == selectionCacheSrc ) - break; - } - wine_tsx11_unlock(); - - /* Verify that the selection returned a valid TARGETS property */ - if ( (xe.xselection.target != aTargets) - || (xe.xselection.property == None) ) - { - TRACE("\tExit, could not retrieve TARGETS\n"); - return cSelectionTargets; - } + if (X11DRV_CLIPBOARD_QueryTargets(display, w, XA_PRIMARY, &xe)) + selectionCacheSrc = XA_PRIMARY; + else if (X11DRV_CLIPBOARD_QueryTargets(display, w, xaClipboard, &xe)) + selectionCacheSrc = xaClipboard; + else + return 0; /* Read the TARGETS property contents */ if(TSXGetWindowProperty(display, xe.xselection.requestor, xe.xselection.property, - 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat, - &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success) - TRACE("\tCouldn't read TARGETS property\n"); + 0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat, &cSelectionTargets, + &remain, (unsigned char**)&targetList) != Success) + { + WARN("Failed to read TARGETS property\n"); + } else { - TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n", + TRACE("Type %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 || atype == aTargets) && aformat == 32 ) + if ((atype == XA_ATOM || atype == xaTargets) && aformat == 32) { - int i; - LPWINE_CLIPFORMAT lpFormat; + INT i; /* 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); + LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupProperty(targetList[i]); - /* - * 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) + if (!lpFormat) + lpFormat = X11DRV_CLIPBOARD_LookupAliasProperty(targetList[i]); + + if (!lpFormat) { - lpFormat = CLIPBOARD_LookupFormat( wFormat ); + LPSTR lpName = TSXGetAtomName(display, targetList[i]); + X11DRV_RegisterClipboardFormat(lpName); - /* 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) ) - ) + lpFormat = X11DRV_CLIPBOARD_LookupProperty(targetList[i]); + + if (!lpFormat) { - 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); + ERR("Failed to cache %s property\n", lpName); + continue; } + + TSXFree(lpName); } - TSXFree(itemFmtName); + TRACE("Atom#%d Property(%d): --> FormatID(%d) %s\n", + i, lpFormat->drvData, lpFormat->wFormatID, lpFormat->Name); + + X11DRV_CLIPBOARD_InsertClipboardData(lpFormat->wFormatID, 0, 0, 0); } } @@ -496,6 +1354,109 @@ int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName ) return cSelectionTargets; } + +/************************************************************************** + * X11DRV_CLIPBOARD_ReadClipboardData + * + * This method is invoked only when we DO NOT own the X selection + * + * 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. + */ +static BOOL X11DRV_CLIPBOARD_ReadClipboardData(UINT wFormat) +{ + Display *display = thread_display(); + BOOL bRet = FALSE; + Bool res; + + HWND hWndClipWindow = GetOpenClipboardWindow(); + HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow(); + + LPWINE_CLIPFORMAT lpFormat; + + TRACE("%d\n", wFormat); + + if (!selectionAcquired) + { + Window w = X11DRV_get_whole_window(GetAncestor(hWnd, GA_ROOT)); + if(!w) + { + FIXME("No parent win found %p %p\n", hWnd, hWndClipWindow); + return FALSE; + } + + lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat); + + if (lpFormat->drvData) + { + DWORD i; + UINT alias; + XEvent xe; + + TRACE("Requesting %s selection (%d) from win(%08x)\n", + lpFormat->Name, lpFormat->drvData, (UINT)selectionCacheSrc); + + wine_tsx11_lock(); + XConvertSelection(display, selectionCacheSrc, lpFormat->drvData, + xaSelectionData, w, CurrentTime); + wine_tsx11_unlock(); + + /* wait until SelectionNotify is received */ + for (i = 0; i < SELECTION_RETRIES; i++) + { + wine_tsx11_lock(); + res = XCheckTypedWindowEvent(display, w, SelectionNotify, &xe); + wine_tsx11_unlock(); + if (res && xe.xselection.selection == selectionCacheSrc) break; + + usleep(SELECTION_WAIT); + } + + /* If the property wasn't available check for aliases */ + if (xe.xselection.property == None && + (alias = X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData))) + { + wine_tsx11_lock(); + XConvertSelection(display, selectionCacheSrc, alias, + xaSelectionData, w, CurrentTime); + wine_tsx11_unlock(); + + /* wait until SelectionNotify is received */ + for (i = 0; i < SELECTION_RETRIES; i++) + { + wine_tsx11_lock(); + res = XCheckTypedWindowEvent(display, w, SelectionNotify, &xe); + wine_tsx11_unlock(); + if (res && xe.xselection.selection == selectionCacheSrc) break; + + usleep(SELECTION_WAIT); + } + } + + /* Verify that the selection returned a valid TARGETS property */ + if (xe.xselection.property != None) + { + /* + * 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(lpFormat, xe.xselection.requestor, + xe.xselection.property); + } + } + } + else + { + ERR("Received request to cache selection data but process is owner\n"); + } + + TRACE("Returning %d\n", bRet); + + return bRet; +} + + /************************************************************************** * X11DRV_CLIPBOARD_ReadSelection * Reads the contents of the X selection property into the WINE clipboard cache @@ -504,7 +1465,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 reqType) +static BOOL X11DRV_CLIPBOARD_ReadSelection(LPWINE_CLIPFORMAT lpData, Window w, Atom prop) { Display *display = thread_display(); Atom atype=AnyPropertyType; @@ -513,236 +1474,153 @@ static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, At long lRequestLength,bwc; unsigned char* val; unsigned char* buffer; - LPWINE_CLIPFORMAT lpFormat; BOOL bRet = FALSE; - HWND hWndClipWindow = GetOpenClipboardWindow(); - if(prop == None) return bRet; - TRACE("Reading X selection...\n"); - - TRACE("\tretrieving property %s from window %ld into %s\n", - TSXGetAtomName(display,reqType), (long)w, TSXGetAtomName(display,prop) ); + TRACE("Reading X selection type %s\n", lpData->Name); /* * First request a zero length in order to figure out the request size. */ - if(TSXGetWindowProperty(display,w,prop,0,0,False, AnyPropertyType/*reqType*/, - &atype, &aformat, &nitems, &itemSize, &val) != Success) + if(TSXGetWindowProperty(display,w,prop,0,0,False, AnyPropertyType, + &atype, &aformat, &nitems, &itemSize, &val) != Success) { - WARN("\tcouldn't get property size\n"); + WARN("Failed to get property size\n"); return bRet; } /* Free zero length return data if any */ - if ( val ) + if (val) { TSXFree(val); val = NULL; } - TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8); + TRACE("Retrieving %ld bytes\n", itemSize * aformat/8); + lRequestLength = (itemSize * aformat/8)/4 + 1; + bwc = aformat/8; - bwc = aformat/8; - /* we want to read the property, but not it too large of chunks or - we could hang the cause problems. Lets go for 4k blocks */ - - if(TSXGetWindowProperty(display,w,prop,0,4096,False, - AnyPropertyType/*reqType*/, - &atype, &aformat, &nitems, &remain, &buffer) - != Success) + /* Read property in 4K blocks */ + if (TSXGetWindowProperty(display,w,prop,0,4096,False, AnyPropertyType/*reqType*/, + &atype, &aformat, &nitems, &remain, &buffer) != Success) { - WARN("\tcouldn't read property\n"); + WARN("Failed to read property\n"); return bRet; } - val = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, - nitems*bwc); - memcpy(val,buffer,nitems*bwc); - TSXFree(buffer); - for (total = nitems*bwc,val_cnt=0; remain;) - { + val = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nitems*bwc); + memcpy(val,buffer,nitems*bwc); + + TSXFree(buffer); + + for (total = nitems*bwc, val_cnt = 0; remain;) + { val_cnt +=nitems*bwc; - TSXGetWindowProperty(display, w, prop, - (total / 4), 4096, False, - AnyPropertyType, &atype, - &aformat, &nitems, &remain, - &buffer); + if (TSXGetWindowProperty(display, w, prop, (total / 4), 4096, False, + AnyPropertyType, &atype, &aformat, &nitems, &remain, &buffer) != Success) + { + WARN("Failed to read property\n"); + HeapFree(GetProcessHeap(), 0, val); + return bRet; + } total += nitems*bwc; HeapReAlloc(GetProcessHeap(),0,val, total); memcpy(&val[val_cnt], buffer, nitems*(aformat/8)); TSXFree(buffer); - } - nitems = total; - - /* - * Translate the X property into the appropriate Windows clipboard - * format, if possible. - */ - if ( (reqType == XA_STRING) - && (atype == XA_STRING) && (aformat == 8) ) - /* convert Unix text to CF_UNICODETEXT */ - { - int i,inlcount = 0; - char* lpstr; - - for(i=0; i <= nitems; i++) - if( val[i] == '\n' ) inlcount++; - - if( (lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nitems + inlcount + 1)) ) - { - static UINT text_cp = (UINT)-1; - UINT count; - HANDLE hUnicodeText; - - for(i=0,inlcount=0; i <= nitems; i++) - { - if( val[i] == '\n' ) lpstr[inlcount++]='\r'; - lpstr[inlcount++]=val[i]; - } - - if(text_cp == (UINT)-1) - { - HKEY hkey; - /* default value */ - text_cp = CP_ACP; - if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", &hkey)) - { - char buf[20]; - DWORD type, count = sizeof(buf); - if(!RegQueryValueExA(hkey, "TextCP", 0, &type, buf, &count)) - text_cp = atoi(buf); - RegCloseKey(hkey); - } - } - - count = MultiByteToWideChar(text_cp, 0, lpstr, -1, NULL, 0); - hUnicodeText = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, count * sizeof(WCHAR)); - if(hUnicodeText) - { - WCHAR *textW = GlobalLock(hUnicodeText); - MultiByteToWideChar(text_cp, 0, lpstr, -1, textW, count); - GlobalUnlock(hUnicodeText); - if (!SetClipboardData(CF_UNICODETEXT, hUnicodeText)) - { - ERR("Not SET! Need to free our own block\n"); - GlobalFree(hUnicodeText); - } - bRet = TRUE; - } - HeapFree(GetProcessHeap(), 0, lpstr); - } - } - else if ( reqType == XA_PIXMAP || reqType == XA_BITMAP ) /* treat PIXMAP as CF_DIB or CF_BITMAP */ - { - /* Get the first pixmap handle passed to us */ - Pixmap *pPixmap = (Pixmap *)val; - HANDLE hTargetImage = 0; /* 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(hwnd, hdc); - } - - 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 native properties simply copy the X data without conversion */ - else if (X11DRV_CLIPBOARD_IsNativeProperty(reqType)) /* * */ - { - HANDLE hClipData = 0; - void* lpClipData; - int cBytes = nitems * aformat/8; + bRet = X11DRV_CLIPBOARD_InsertClipboardData(lpData->wFormatID, 0, lpData->lpDrvImportFunc(val, total), 0); - if( cBytes ) - { - if (wFormat == CF_METAFILEPICT || wFormat == CF_ENHMETAFILE) - { - hClipData = X11DRV_CLIPBOARD_SerializeMetafile(wFormat, (HANDLE)val, cBytes, FALSE); - } - else - { - /* 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; - - bRet = TRUE; - } - } - 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 */ HeapFree(GetProcessHeap(),0,val); + return bRet; } + +/************************************************************************** + * CLIPBOARD_SerializeMetafile + */ +static HANDLE X11DRV_CLIPBOARD_SerializeMetafile(INT wformat, HANDLE hdata, LPDWORD lpcbytes, BOOL out) +{ + HANDLE h = 0; + + TRACE(" wFormat=%d hdata=%08x out=%d\n", wformat, (unsigned int) hdata, out); + + if (out) /* Serialize out, caller should free memory */ + { + *lpcbytes = 0; /* Assume failure */ + + if (wformat == CF_METAFILEPICT) + { + LPMETAFILEPICT lpmfp = (LPMETAFILEPICT) GlobalLock(hdata); + int size = GetMetaFileBitsEx(lpmfp->hMF, 0, NULL); + + h = GlobalAlloc(0, size + sizeof(METAFILEPICT)); + if (h) + { + char *pdata = GlobalLock(h); + + memcpy(pdata, lpmfp, sizeof(METAFILEPICT)); + GetMetaFileBitsEx(lpmfp->hMF, size, pdata + sizeof(METAFILEPICT)); + + *lpcbytes = size + sizeof(METAFILEPICT); + + GlobalUnlock(h); + } + + GlobalUnlock(hdata); + } + else if (wformat == CF_ENHMETAFILE) + { + int size = GetEnhMetaFileBits(hdata, 0, NULL); + + h = GlobalAlloc(0, size); + if (h) + { + LPVOID pdata = GlobalLock(h); + + GetEnhMetaFileBits(hdata, size, pdata); + *lpcbytes = size; + + GlobalUnlock(h); + } + } + } + else + { + if (wformat == CF_METAFILEPICT) + { + h = GlobalAlloc(0, sizeof(METAFILEPICT)); + if (h) + { + LPMETAFILEPICT pmfp = (LPMETAFILEPICT) GlobalLock(h); + + memcpy(pmfp, (LPVOID)hdata, sizeof(METAFILEPICT)); + pmfp->hMF = SetMetaFileBitsEx(*lpcbytes - sizeof(METAFILEPICT), + (char *)hdata + sizeof(METAFILEPICT)); + + GlobalUnlock(h); + } + } + else if (wformat == CF_ENHMETAFILE) + { + h = SetEnhMetaFileBits(*lpcbytes, (LPVOID)hdata); + } + } + + return h; +} + + /************************************************************************** * X11DRV_CLIPBOARD_ReleaseSelection * @@ -754,87 +1632,92 @@ END: void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd) { Display *display = thread_display(); - Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False); - int clearAllSelections = 0; - HKEY hkey; - - if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Clipboard", &hkey)) - { - char buffer[20]; - DWORD type, count = sizeof(buffer); - if(!RegQueryValueExA(hkey, "ClearAllSelections", 0, &type, buffer, &count)) - clearAllSelections = atoi(buffer); - RegCloseKey(hkey); - } /* w is the window that lost the selection - * selectionPrevWindow is nonzero if CheckSelection() was called. */ + TRACE("event->window = %08x (selectionWindow = %08x) selectionAcquired=0x%08x\n", + (unsigned)w, (unsigned)selectionWindow, (unsigned)selectionAcquired); - TRACE("\tevent->window = %08x (sw = %08x, spw=%08x)\n", - (unsigned)w, (unsigned)selectionWindow, (unsigned)selectionPrevWindow ); - - if( selectionAcquired ) + if (selectionAcquired) { - if( w == selectionWindow || selectionPrevWindow == None) - { + if (w == selectionWindow) + { /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc * dictate that *all* selections should be cleared on loss of a selection, * we must give up all the selections we own. */ - if ( clearAllSelections || (selType == xaClipboard) ) + if (clearAllSelections || (selType == xaClipboard)) { - /* completely give up the selection */ - TRACE("Lost CLIPBOARD (+PRIMARY) selection\n"); + CLIPBOARDINFO cbinfo; - /* We are completely giving up the selection. - * Make sure we can open the windows clipboard first. */ + /* completely give up the selection */ + TRACE("Lost CLIPBOARD (+PRIMARY) selection\n"); - 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; - } - - /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */ - if ( (selType == xaClipboard) - && (selectionAcquired & S_PRIMARY) ) - { - XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime); - } - - /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */ - if ( (selType == XA_PRIMARY) - && (selectionAcquired & S_CLIPBOARD) ) - { - XSetSelectionOwner(display, xaClipboard, None, CurrentTime); - } - - selectionWindow = None; - PrimarySelectionOwner = ClipboardSelectionOwner = 0; - - /* 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! + /* We are completely giving up the selection. There is a + * potential race condition where the apps that now owns + * the selection has already grabbed both selections. In + * this case, if we clear any selection we may clear the + * new owners selection. To prevent this common case we + * try to open the clipboard. If we can't, we assume it + * was a wine apps that took it and has taken both selectons. + * In this case, don't bother releasing the other selection. + * Otherwise only release the selection if we still own it. */ - selectionAcquired = (S_PRIMARY | S_CLIPBOARD); - EmptyClipboard(); - CloseClipboard(); + X11DRV_CLIPBOARD_GetClipboardInfo(&cbinfo); - /* Give up ownership of the windows clipboard */ - CLIPBOARD_ReleaseOwner(); + if (cbinfo.flags & CB_OWNER) + { + /* Since we're still the owner, this wasn't initiated by + another Wine process */ + if (OpenClipboard(hwnd)) + { + /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */ + if ((selType == xaClipboard) && (selectionAcquired & S_PRIMARY)) + { + TRACE("Lost clipboard. Check if we need to release PRIMARY\n"); + if (selectionWindow == TSXGetSelectionOwner(display,XA_PRIMARY)) + { + TRACE("We still own PRIMARY. Releasing PRIMARY.\n"); + XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime); + } + else + TRACE("We no longer own PRIMARY\n"); + } - /* Reset the selection flags now that we are done */ - selectionAcquired = S_NOSELECTION; + /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD */ + if ((selType == XA_PRIMARY) && (selectionAcquired & S_CLIPBOARD)) + { + TRACE("Lost PRIMARY. Check if we need to release CLIPBOARD\n"); + if (selectionWindow == TSXGetSelectionOwner(display,xaClipboard)) + { + TRACE("We still own CLIPBOARD. Releasing CLIPBOARD.\n"); + XSetSelectionOwner(display, xaClipboard, None, CurrentTime); + } + else + TRACE("We no longer own CLIPBOARD\n"); + } + + /* Destroy private objects */ + SendMessageW(cbinfo.hWndOwner, WM_DESTROYCLIPBOARD, 0, 0); + + /* Give up ownership of the windows clipboard */ + X11DRV_CLIPBOARD_ReleaseOwnership(); + + CloseClipboard(); + } + } + else + { + TRACE("Lost selection to other Wine process.\n"); + } + + selectionWindow = None; + PrimarySelectionOwner = ClipboardSelectionOwner = 0; + + X11DRV_EmptyClipboard(); + + /* Reset the selection flags now that we are done */ + selectionAcquired = S_NOSELECTION; } else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */ { @@ -842,94 +1725,92 @@ void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd) 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); - } } - - /* Signal to a selectionClearEvent listener if the selection is completely lost */ - if (selectionClearEvent && !selectionAcquired) - { - TRACE("Lost all selections, signalling to selectionClearEvent listener\n"); - SetEvent(selectionClearEvent); - } - - selectionPrevWindow = None; } + /************************************************************************** - * ReleaseClipboard (X11DRV.@) - * Voluntarily release all currently owned X selections + * IsSelectionOwner (X11DRV.@) + * + * Returns: TRUE if the selection is owned by this process, FALSE otherwise */ -void X11DRV_ReleaseClipboard(void) +static BOOL X11DRV_CLIPBOARD_IsSelectionOwner(void) { - Display *display = thread_display(); - if( selectionAcquired ) - { - XEvent xe; - Window savePrevWindow = selectionWindow; - Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); - BOOL bHasPrimarySelection = selectionAcquired & S_PRIMARY; - - selectionAcquired = S_NOSELECTION; - selectionPrevWindow = selectionWindow; - selectionWindow = None; - - TRACE("\tgiving up selection (spw = %08x)\n", - (unsigned)selectionPrevWindow); - - wine_tsx11_lock(); - - TRACE("Releasing CLIPBOARD selection\n"); - XSetSelectionOwner(display, xaClipboard, None, CurrentTime); - if( selectionPrevWindow ) - while( !XCheckTypedWindowEvent( display, selectionPrevWindow, - 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 ) ); - } - wine_tsx11_unlock(); - } - - /* Get rid of any Pixmap resources we may still have */ - while (prop_head) - { - PROPERTY *prop = prop_head; - prop_head = prop->next; - XFreePixmap( gdi_display, prop->pixmap ); - HeapFree( GetProcessHeap(), 0, prop ); - } + return selectionAcquired; } + +/************************************************************************** + * X11DRV Clipboard Exports + **************************************************************************/ + + +/************************************************************************** + * RegisterClipboardFormat (X11DRV.@) + * + * Registers a custom X clipboard format + * Returns: Format id or 0 on failure + */ +INT X11DRV_RegisterClipboardFormat(LPCSTR FormatName) +{ + LPWINE_CLIPFORMAT lpFormat = ClipFormats; + + if (FormatName == NULL) + return 0; + + TRACE("('%s') !\n", FormatName); + + /* walk format chain to see if it's already registered */ + while(TRUE) + { + if ( !strcasecmp(lpFormat->Name, FormatName) && + (lpFormat->wFlags & CF_FLAG_BUILTINFMT) == 0) + return lpFormat->wFormatID; + + if (!lpFormat->NextFormat) + break; + + lpFormat = lpFormat->NextFormat; + } + + return X11DRV_CLIPBOARD_InsertClipboardFormat(FormatName, FormatName); +} + + +/************************************************************************** + * X11DRV_GetClipboardFormatName + */ +INT X11DRV_GetClipboardFormatName(UINT wFormat, LPSTR retStr, INT maxlen) +{ + INT len = 0; + LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat); + + TRACE("(%04X, %p, %d) !\n", wFormat, retStr, maxlen); + + if (!lpFormat || (lpFormat->wFlags & CF_FLAG_BUILTINFMT)) + { + TRACE("Unknown format 0x%08x!\n", wFormat); + SetLastError(ERROR_INVALID_PARAMETER); + } + else + { + strncpy(retStr, lpFormat->Name, maxlen - 1); + retStr[maxlen - 1] = 0; + + len = strlen(retStr); + } + + return len; +} + + /************************************************************************** * AcquireClipboard (X11DRV.@) */ -void X11DRV_AcquireClipboard(void) +void X11DRV_AcquireClipboard(HWND hWndClipWindow) { Display *display = thread_display(); - Window owner; - HWND hWndClipWindow = GetOpenClipboardWindow(); /* * Acquire X selection if we don't already own it. @@ -943,24 +1824,22 @@ void X11DRV_AcquireClipboard(void) * re-cycled to another top level X window in X11DRV_CLIPBOARD_ResetOwner. * */ - - if ( !(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)) ) + if (!(selectionAcquired == (S_PRIMARY | S_CLIPBOARD))) { - Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); - owner = X11DRV_get_whole_window( GetAncestor( hWndClipWindow, GA_ROOT ) ); + Window owner = X11DRV_get_whole_window(GetAncestor(hWndClipWindow, GA_ROOT)); /* Grab PRIMARY selection if not owned */ - if ( !(selectionAcquired & S_PRIMARY) ) + if (!(selectionAcquired & S_PRIMARY)) TSXSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime); /* Grab CLIPBOARD selection if not owned */ - if ( !(selectionAcquired & S_CLIPBOARD) ) + if (!(selectionAcquired & S_CLIPBOARD)) TSXSetSelectionOwner(display, xaClipboard, owner, CurrentTime); - if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner ) + if (TSXGetSelectionOwner(display,XA_PRIMARY) == owner) selectionAcquired |= S_PRIMARY; - if( TSXGetSelectionOwner(display,xaClipboard) == owner) + if (TSXGetSelectionOwner(display,xaClipboard) == owner) selectionAcquired |= S_CLIPBOARD; if (selectionAcquired) @@ -969,197 +1848,236 @@ void X11DRV_AcquireClipboard(void) TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner); } } + else + { + WARN("Received request to acquire selection but process is already owner=(%08x)\n", (unsigned) selectionWindow); + + selectionAcquired = S_NOSELECTION; + + if (TSXGetSelectionOwner(display,XA_PRIMARY) == selectionWindow) + selectionAcquired |= S_PRIMARY; + + if (TSXGetSelectionOwner(display,xaClipboard) == selectionWindow) + selectionAcquired |= S_CLIPBOARD; + + if (!(selectionAcquired == (S_PRIMARY | S_CLIPBOARD))) + { + WARN("Lost selection but process didn't process SelectClear\n"); + selectionWindow = None; + } + } } + /************************************************************************** - * IsClipboardFormatAvailable (X11DRV.@) - * - * Checks if the specified format is available in the current selection - * Only invoked when WINE is not the selection owner + * X11DRV_EmptyClipboard + */ +void X11DRV_EmptyClipboard(void) +{ + if (ClipData) + { + LPWINE_CLIPDATA lpData; + LPWINE_CLIPDATA lpNext = ClipData; + + do + { + lpData = lpNext; + lpNext = lpData->NextData; + lpData->PrevData->NextData = lpData->NextData; + lpData->NextData->PrevData = lpData->PrevData; + X11DRV_CLIPBOARD_FreeData(lpData); + HeapFree(GetProcessHeap(), 0, lpData); + } while (lpNext != lpData); + } + + TRACE(" %d entries deleted from cache.\n", ClipDataCount); + + ClipData = NULL; + ClipDataCount = 0; +} + + + +/************************************************************************** + * X11DRV_SetClipboardData + */ +BOOL X11DRV_SetClipboardData(UINT wFormat, HANDLE16 hData16, HANDLE hData32) +{ + BOOL bResult = FALSE; + + if (X11DRV_CLIPBOARD_InsertClipboardData(wFormat, hData16, hData32, 0)) + bResult = TRUE; + + return bResult; +} + + +/************************************************************************** + * CountClipboardFormats + */ +INT X11DRV_CountClipboardFormats(void) +{ + CLIPBOARDINFO cbinfo; + + X11DRV_CLIPBOARD_UpdateCache(&cbinfo); + + TRACE(" count=%d\n", ClipDataCount); + + return ClipDataCount; +} + + +/************************************************************************** + * X11DRV_EnumClipboardFormats + */ +UINT X11DRV_EnumClipboardFormats(UINT wFormat) +{ + CLIPBOARDINFO cbinfo; + UINT wNextFormat = 0; + + TRACE("(%04X)\n", wFormat); + + X11DRV_CLIPBOARD_UpdateCache(&cbinfo); + + if (!wFormat) + { + if (ClipData) + wNextFormat = ClipData->wFormatID; + } + else + { + LPWINE_CLIPDATA lpData = X11DRV_CLIPBOARD_LookupData(wFormat); + + if (lpData && lpData->NextData != ClipData) + wNextFormat = lpData->NextData->wFormatID; + } + + return wNextFormat; +} + + +/************************************************************************** + * X11DRV_IsClipboardFormatAvailable */ BOOL X11DRV_IsClipboardFormatAvailable(UINT wFormat) { - Display *display = thread_display(); - Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); - Window ownerPrimary = TSXGetSelectionOwner(display,XA_PRIMARY); - Window ownerClipboard = TSXGetSelectionOwner(display,xaClipboard); + BOOL bRet = FALSE; + CLIPBOARDINFO cbinfo; - TRACE("enter for %d\n", wFormat); + TRACE("(%04X)\n", wFormat); - /* - * 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 caching the CLIPBOARD selection. - * If unavailable try PRIMARY. - */ - if ( X11DRV_CLIPBOARD_CacheDataFormats(xaClipboard) == 0 ) - { - X11DRV_CLIPBOARD_CacheDataFormats(XA_PRIMARY); - } + X11DRV_CLIPBOARD_UpdateCache(&cbinfo); - ClipboardSelectionOwner = ownerClipboard; - PrimarySelectionOwner = ownerPrimary; - } + if (wFormat != 0 && X11DRV_CLIPBOARD_LookupData(wFormat)) + bRet = TRUE; - /* Exit if there is no selection */ - if ( !ownerClipboard && !ownerPrimary ) - { - TRACE("There is no selection owner\n"); - return FALSE; - } - - /* 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_GetClipboardData( wFormat ); - - TRACE("There is no selection\n"); - return FALSE; -} - -/************************************************************************** - * RegisterClipboardFormat (X11DRV.@) - * - * Registers a custom X clipboard format - * Returns: Format id or 0 on failure - */ -INT X11DRV_RegisterClipboardFormat( LPCSTR FormatName ) -{ - Display *display = thread_display(); - 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; -} - -/************************************************************************** - * IsSelectionOwner (X11DRV.@) - * - * Returns: TRUE - We(WINE) own the selection, FALSE - Selection not owned by us - */ -BOOL X11DRV_IsSelectionOwner(void) -{ - return selectionAcquired; -} - -/************************************************************************** - * SetClipboardData (X11DRV.@) - * - * We don't need to do anything special here since the clipboard code - * maintains the cache. - * - */ -void X11DRV_SetClipboardData(UINT wFormat) -{ - /* Make sure we have acquired the X selection */ - X11DRV_AcquireClipboard(); -} - -/************************************************************************** - * GetClipboardData (X11DRV.@) - * - * This method is invoked only when we DO NOT own the X selection - * - * NOTE: Clipboard driver get requests only for CF_UNICODETEXT data. - * 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_GetClipboardData(UINT wFormat) -{ - Display *display = thread_display(); - BOOL bRet = selectionAcquired; - HWND hWndClipWindow = GetOpenClipboardWindow(); - HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow(); - LPWINE_CLIPFORMAT lpFormat; - - TRACE("%d\n", wFormat); - - if (!selectionAcquired) - { - XEvent xe; - Atom propRequest; - Window w = X11DRV_get_whole_window( GetAncestor( hWnd, GA_ROOT )); - if(!w) - { - FIXME("No parent win found %p %p\n", hWnd, hWndClipWindow); - return FALSE; - } - - /* 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); - - if (propRequest) - { - TRACE("Requesting %s selection from %s...\n", - TSXGetAtomName(display, propRequest), - TSXGetAtomName(display, selectionCacheSrc) ); - wine_tsx11_lock(); - 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; - } - wine_tsx11_unlock(); - - /* - * 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; - - TRACE("\tpresent %s = %i\n", CLIPBOARD_GetFormatName(wFormat, NULL, 0), bRet ); - } - - TRACE("Returning %d\n", bRet); + TRACE("(%04X)- ret(%d)\n", wFormat, bRet); return bRet; } + +/************************************************************************** + * GetClipboardData (USER.142) + */ +BOOL X11DRV_GetClipboardData(UINT wFormat, HANDLE16* phData16, HANDLE* phData32) +{ + CLIPBOARDINFO cbinfo; + LPWINE_CLIPDATA lpRender; + + TRACE("(%04X)\n", wFormat); + + X11DRV_CLIPBOARD_UpdateCache(&cbinfo); + + if ((lpRender = X11DRV_CLIPBOARD_LookupData(wFormat))) + { + if ( !lpRender->hData32 ) + X11DRV_CLIPBOARD_RenderFormat(lpRender); + + /* Convert between 32 -> 16 bit data, if necessary */ + if (lpRender->hData32 && !lpRender->hData16) + { + int size; + + if (lpRender->wFormatID == CF_METAFILEPICT) + size = sizeof(METAFILEPICT16); + else + 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); + } + + GlobalUnlock16(lpRender->hData16); + GlobalUnlock(lpRender->hData32); + } + } + + /* Convert between 32 -> 16 bit data, if necessary */ + if (lpRender->hData16 && !lpRender->hData32) + { + int size; + + if (lpRender->wFormatID == CF_METAFILEPICT) + size = sizeof(METAFILEPICT16); + else + size = GlobalSize(lpRender->hData32); + + lpRender->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | + GMEM_DDESHARE, size); + + if (lpRender->wFormatID == CF_METAFILEPICT) + { + FIXME("\timplement function CopyMetaFilePict16to32\n"); + FIXME("\tin the appropriate file.\n"); +#ifdef SOMEONE_IMPLEMENTED_ME + CopyMetaFilePict16to32(GlobalLock16(lpRender->hData32), + GlobalLock(lpRender->hData16)); +#endif + } + else + { + memcpy(GlobalLock(lpRender->hData32), + GlobalLock16(lpRender->hData16), size); + } + + GlobalUnlock(lpRender->hData32); + GlobalUnlock16(lpRender->hData16); + } + + if (phData16) + *phData16 = lpRender->hData16; + + if (phData32) + *phData32 = lpRender->hData32; + + TRACE(" returning hData16(%04x) hData32(%04x) (type %d)\n", + lpRender->hData16, (unsigned int) lpRender->hData32, lpRender->wFormatID); + + return lpRender->hData16 || lpRender->hData32; + } + + return 0; +} + + /************************************************************************** * ResetSelectionOwner (X11DRV.@) * @@ -1175,50 +2093,51 @@ void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar) HWND hWndClipOwner = 0; HWND tmp; Window XWnd = X11DRV_get_whole_window(hwnd); - Atom xaClipboard; BOOL bLostSelection = FALSE; + Window selectionPrevWindow; /* 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 + if (!selectionAcquired || XWnd != selectionWindow || selectionWindow == None ) return; - if ( (bFooBar && XWnd) || (!bFooBar && !XWnd) ) + if ((bFooBar && XWnd) || (!bFooBar && !XWnd)) return; hWndClipOwner = GetClipboardOwner(); - xaClipboard = TSXInternAtom(display, _CLIPBOARD, False); TRACE("clipboard owner = %p, selection window = %08x\n", hWndClipOwner, (unsigned)selectionWindow); /* now try to salvage current selection from being destroyed by X */ - - TRACE("\tchecking %08x\n", (unsigned) XWnd); + TRACE("checking %08x\n", (unsigned) XWnd); selectionPrevWindow = selectionWindow; selectionWindow = None; - if (!(tmp = GetWindow( hwnd, GW_HWNDNEXT ))) tmp = GetWindow( hwnd, GW_HWNDFIRST ); - if (tmp && tmp != hwnd) selectionWindow = X11DRV_get_whole_window(tmp); + if (!(tmp = GetWindow(hwnd, GW_HWNDNEXT))) + tmp = GetWindow(hwnd, GW_HWNDFIRST); - if( selectionWindow != None ) + if (tmp && tmp != hwnd) + selectionWindow = X11DRV_get_whole_window(tmp); + + 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; + selectionAcquired = S_NOSELECTION; TRACE("\tswitching selection from %08x to %08x\n", (unsigned)selectionPrevWindow, (unsigned)selectionWindow); /* Assume ownership for the PRIMARY and CLIPBOARD selection */ - if ( saveSelectionState & S_PRIMARY ) + if (saveSelectionState & S_PRIMARY) TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime); TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime); @@ -1227,12 +2146,11 @@ void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar) selectionAcquired = saveSelectionState; /* Lose the selection if something went wrong */ - if ( ( (saveSelectionState & S_PRIMARY) && - (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) ) - || (TSXGetSelectionOwner(display, xaClipboard) != selectionWindow) ) + if (((saveSelectionState & S_PRIMARY) && + (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow)) || + (TSXGetSelectionOwner(display, xaClipboard) != selectionWindow)) { bLostSelection = TRUE; - goto END; } else { @@ -1246,167 +2164,93 @@ void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar) else { bLostSelection = TRUE; - goto END; } -END: if (bLostSelection) { - /* Launch the clipboard server if the selection can no longer be recyled - * to another top level window. */ + TRACE("Lost the selection!\n"); - if ( !X11DRV_CLIPBOARD_LaunchServer() ) - { - /* Empty the windows clipboard if the server was not launched. - * 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( 0 ); - selectionAcquired = (S_PRIMARY | S_CLIPBOARD); - EmptyClipboard(); - - CloseClipboard(); - - /* Give up ownership of the windows clipboard */ - CLIPBOARD_ReleaseOwner(); - } - - selectionAcquired = S_NOSELECTION; - ClipboardSelectionOwner = PrimarySelectionOwner = 0; - selectionWindow = 0; + X11DRV_CLIPBOARD_ReleaseOwnership(); + selectionAcquired = S_NOSELECTION; + ClipboardSelectionOwner = PrimarySelectionOwner = 0; + selectionWindow = 0; } } -/************************************************************************** - * 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 ) -{ - PROPERTY *prop = HeapAlloc( GetProcessHeap(), 0, sizeof(*prop) ); - if (!prop) return FALSE; - prop->atom = property; - prop->pixmap = pixmap; - prop->next = prop_head; - prop_head = prop; - 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. - */ - PROPERTY **prop = &prop_head; - - while (*prop) - { - if ((*prop)->atom == property) - { - PROPERTY *next = (*prop)->next; - XFreePixmap( gdi_display, (*prop)->pixmap ); - HeapFree( GetProcessHeap(), 0, *prop ); - *prop = next; - } - else prop = &(*prop)->next; - } -} - -/************************************************************************** - * X11DRV_GetClipboardFormatName - */ -BOOL X11DRV_GetClipboardFormatName( UINT wFormat, LPSTR retStr, UINT maxlen ) -{ - BOOL bRet = FALSE; - char *itemFmtName = TSXGetAtomName(thread_display(), wFormat); - INT prefixlen = strlen(FMT_PREFIX); - - if ( 0 == strncmp(itemFmtName, FMT_PREFIX, prefixlen ) ) - { - strncpy(retStr, itemFmtName + prefixlen, maxlen); - bRet = TRUE; - } - - TSXFree(itemFmtName); - - return bRet; -} - /************************************************************************** - * CLIPBOARD_SerializeMetafile + * X11DRV_CLIPBOARD_SynthesizeData */ -HANDLE X11DRV_CLIPBOARD_SerializeMetafile(INT wformat, HANDLE hdata, INT cbytes, BOOL out) +static BOOL X11DRV_CLIPBOARD_SynthesizeData(UINT wFormatID) { - HANDLE h = 0; + BOOL bsyn = TRUE; + LPWINE_CLIPDATA lpSource = NULL; - if (out) /* Serialize out, caller should free memory */ + TRACE(" %d\n", wFormatID); + + /* Don't need to synthesize if it already exists */ + if (X11DRV_CLIPBOARD_LookupData(wFormatID)) + return TRUE; + + if (wFormatID == CF_UNICODETEXT || wFormatID == CF_TEXT || wFormatID == CF_OEMTEXT) { - if (wformat == CF_METAFILEPICT) - { - LPMETAFILEPICT lpmfp = (LPMETAFILEPICT) GlobalLock(hdata); - int size = GetMetaFileBitsEx(lpmfp->hMF, 0, NULL); - - h = GlobalAlloc(0, size + sizeof(METAFILEPICT)); - if (h) - { - METAFILEPICT *pdata = GlobalLock(h); - - memcpy(pdata, lpmfp, sizeof(METAFILEPICT)); - GetMetaFileBitsEx(lpmfp->hMF, size, pdata + 1); - - GlobalUnlock(h); - } - - GlobalUnlock(hdata); - } - else if (wformat == CF_ENHMETAFILE) - { - int size = GetEnhMetaFileBits(hdata, 0, NULL); - - h = GlobalAlloc(0, size); - if (h) - { - LPVOID pdata = GlobalLock(h); - GetEnhMetaFileBits(hdata, size, pdata); - GlobalUnlock(h); - } - } + bsyn = ((lpSource = X11DRV_CLIPBOARD_LookupData(CF_UNICODETEXT)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED) || + ((lpSource = X11DRV_CLIPBOARD_LookupData(CF_TEXT)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED) || + ((lpSource = X11DRV_CLIPBOARD_LookupData(CF_OEMTEXT)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED); } - else + else if (wFormatID == CF_ENHMETAFILE) { - if (wformat == CF_METAFILEPICT) - { - h = GlobalAlloc(0, sizeof(METAFILEPICT)); - if (h) - { - LPMETAFILEPICT pmfp = (LPMETAFILEPICT) GlobalLock(h); - - memcpy(pmfp, (LPVOID)hdata, sizeof(METAFILEPICT)); - pmfp->hMF = SetMetaFileBitsEx(cbytes - sizeof(METAFILEPICT), - (char *)hdata + sizeof(METAFILEPICT)); - - GlobalUnlock(h); - } - } - else if (wformat == CF_ENHMETAFILE) - { - h = SetEnhMetaFileBits(cbytes, (LPVOID)hdata); - } + bsyn = (lpSource = X11DRV_CLIPBOARD_LookupData(CF_METAFILEPICT)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED; + } + else if (wFormatID == CF_METAFILEPICT) + { + bsyn = (lpSource = X11DRV_CLIPBOARD_LookupData(CF_METAFILEPICT)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED; + } + else if (wFormatID == CF_DIB) + { + bsyn = (lpSource = X11DRV_CLIPBOARD_LookupData(CF_BITMAP)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED; + } + else if (wFormatID == CF_BITMAP) + { + bsyn = (lpSource = X11DRV_CLIPBOARD_LookupData(CF_DIB)) && + ~lpSource->wFlags & CF_FLAG_SYNTHESIZED; } - return h; + if (bsyn) + X11DRV_CLIPBOARD_InsertClipboardData(wFormatID, 0, 0, CF_FLAG_SYNTHESIZED); + + return bsyn; +} + + + +/************************************************************************** + * X11DRV_EndClipboardUpdate + * TODO: + * Add locale if it hasn't already been added + */ +void X11DRV_EndClipboardUpdate(void) +{ + INT count = ClipDataCount; + + /* Do Unicode <-> Text <-> OEM mapping */ + X11DRV_CLIPBOARD_SynthesizeData(CF_UNICODETEXT); + X11DRV_CLIPBOARD_SynthesizeData(CF_TEXT); + X11DRV_CLIPBOARD_SynthesizeData(CF_OEMTEXT); + + /* Enhmetafile <-> MetafilePict mapping */ + X11DRV_CLIPBOARD_SynthesizeData(CF_ENHMETAFILE); + X11DRV_CLIPBOARD_SynthesizeData(CF_METAFILEPICT); + + /* DIB <-> Bitmap mapping */ + X11DRV_CLIPBOARD_SynthesizeData(CF_DIB); + X11DRV_CLIPBOARD_SynthesizeData(CF_BITMAP); + + TRACE("%d formats added to cached data\n", ClipDataCount - count); } diff --git a/dlls/x11drv/event.c b/dlls/x11drv/event.c index e5f31c962b0..febad5cd0a9 100644 --- a/dlls/x11drv/event.c +++ b/dlls/x11drv/event.c @@ -47,6 +47,7 @@ #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(event); +WINE_DECLARE_DEBUG_CHANNEL(clipboard); /* X context to associate a hwnd to an X window */ extern XContext winContext; @@ -272,10 +273,9 @@ static void EVENT_ProcessEvent( XEvent *event ) if (XFindContext( display, event->xany.window, winContext, (char **)&hWnd ) != 0) hWnd = 0; /* Not for a registered window */ wine_tsx11_unlock(); + if (!hWnd && event->xany.window == root_window) hWnd = GetDesktopWindow(); - if ( !hWnd && event->xany.window != root_window - && event->type != PropertyNotify - && event->type != MappingNotify) + if (!hWnd && event->type != PropertyNotify && event->type != MappingNotify) WARN( "Got event %s for unknown Window %08lx\n", event_names[event->type], event->xany.window ); else @@ -574,6 +574,30 @@ static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event ) } +/*********************************************************************** + * EVENT_SelectionRequest_AddTARGETS + * Utility function for EVENT_SelectionRequest_TARGETS. + */ +static void EVENT_SelectionRequest_AddTARGETS(Atom* targets, unsigned long* cTargets, Atom prop) +{ + int i; + BOOL bExists; + + /* Scan through what we have so far to avoid duplicates */ + for (i = 0, bExists = FALSE; i < *cTargets; i++) + { + if (targets[i] == prop) + { + bExists = TRUE; + break; + } + } + + if (!bExists) + targets[(*cTargets)++] = prop; +} + + /*********************************************************************** * EVENT_SelectionRequest_TARGETS * Service a TARGETS selection request event @@ -581,62 +605,46 @@ static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event ) static Atom EVENT_SelectionRequest_TARGETS( Display *display, 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("Request for %s\n", TSXGetAtomName(display, target)); + UINT alias; + ULONG cTargets; /* * 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 + * We include the TARGETS item, and propery aliases */ - cTargets = CountClipboardFormats() + 1; - if ( CLIPBOARD_IsPresent(CF_DIB) || CLIPBOARD_IsPresent(CF_BITMAP) ) - cTargets++; + cTargets = X11DRV_CountClipboardFormats() + 1; + + for (wFormat = 0; (wFormat = X11DRV_EnumClipboardFormats(wFormat));) + { + LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat); + if (lpFormat && X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData)) + cTargets++; + } + + TRACE_(clipboard)(" found %ld formats\n", cTargets); /* Allocate temp buffer */ targets = (Atom*)HeapAlloc( GetProcessHeap(), 0, cTargets * sizeof(Atom)); - if(targets == NULL) return None; + if(targets == NULL) + return None; /* Create TARGETS property list (First item in list is TARGETS itself) */ - - for ( targets[0] = xaTargets, cTargets = 1, wFormat = 0, bHavePixmap = FALSE; - (wFormat = EnumClipboardFormats( wFormat )); ) + for (targets[0] = xaTargets, cTargets = 1, wFormat = 0; + (wFormat = X11DRV_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; + LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat); - /* Add PIXMAP prop for bitmaps additionally */ - if ( (wFormat == CF_DIB || wFormat == CF_BITMAP ) - && !bHavePixmap ) - { - targets[cTargets++] = XA_PIXMAP; - bHavePixmap = TRUE; - } - } - } + EVENT_SelectionRequest_AddTARGETS(targets, &cTargets, lpFormat->drvData); + + /* Check if any alias should be listed */ + alias = X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData); + if (alias) + EVENT_SelectionRequest_AddTARGETS(targets, &cTargets, alias); } - if (TRACE_ON(event)) + if (TRACE_ON(clipboard)) { int i; for ( i = 0; i < cTargets; i++) @@ -644,260 +652,18 @@ static Atom EVENT_SelectionRequest_TARGETS( Display *display, Window requestor, if (targets[i]) { char *itemFmtName = TSXGetAtomName(display, targets[i]); - TRACE("\tAtom# %d: Type %s\n", i, itemFmtName); + TRACE_(clipboard)("\tAtom# %d: Property %ld Type %s\n", i, targets[i], itemFmtName); TSXFree(itemFmtName); } } } - /* Update the X property */ - TRACE("\tUpdating property %s...\n", 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("(Rc=%d)\n", xRc); + TSXChangeProperty(display, requestor, rprop, XA_ATOM, 32, + PropModeReplace, (unsigned char *)targets, cTargets); - HeapFree( GetProcessHeap(), 0, targets ); - - return rprop; -} - - -/*********************************************************************** - * EVENT_SelectionRequest_STRING - * Service a STRING selection request event - */ -static Atom EVENT_SelectionRequest_STRING( Display *display, Window requestor, - Atom target, Atom rprop ) -{ - static UINT text_cp = (UINT)-1; - HANDLE hUnicodeText; - LPWSTR uni_text; - LPSTR text; - int size,i,j; - char* lpstr = 0; - char *itemFmtName; - int xRc; - - if(text_cp == (UINT)-1) - { - HKEY hkey; - /* default value */ - text_cp = CP_ACP; - if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", &hkey)) - { - char buf[20]; - DWORD type, count = sizeof(buf); - if(!RegQueryValueExA(hkey, "TextCP", 0, &type, buf, &count)) - text_cp = atoi(buf); - RegCloseKey(hkey); - } - } - - /* - * Map the requested X selection property type atom name to a - * windows clipboard format ID. - */ - itemFmtName = TSXGetAtomName(display, target); - TRACE("Request for %s (wFormat=%x %s)\n", - itemFmtName, CF_UNICODETEXT, CLIPBOARD_GetFormatName(CF_UNICODETEXT, NULL, 0)); - TSXFree(itemFmtName); - - hUnicodeText = GetClipboardData(CF_UNICODETEXT); - if(!hUnicodeText) - return None; - uni_text = GlobalLock(hUnicodeText); - if(!uni_text) - return None; - - size = WideCharToMultiByte(text_cp, 0, uni_text, -1, NULL, 0, NULL, NULL); - text = HeapAlloc(GetProcessHeap(), 0, size); - if (!text) - return None; - WideCharToMultiByte(text_cp, 0, uni_text, -1, text, size, NULL, NULL); - - /* remove carriage returns */ - - lpstr = (char*)HeapAlloc( GetProcessHeap(), 0, size-- ); - if(lpstr == NULL) return None; - 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("\tUpdating property %s...\n", TSXGetAtomName(display, rprop)); - xRc = TSXChangeProperty(display, requestor, rprop, - XA_STRING, 8, PropModeReplace, - lpstr, j); - TRACE("(Rc=%d)\n", xRc); - - GlobalUnlock(hUnicodeText); - HeapFree(GetProcessHeap(), 0, text); - HeapFree( GetProcessHeap(), 0, lpstr ); - - return rprop; -} - -/*********************************************************************** - * EVENT_SelectionRequest_PIXMAP - * Service a PIXMAP selection request event - */ -static Atom EVENT_SelectionRequest_PIXMAP( Display *display, Window requestor, - Atom target, Atom rprop ) -{ - HANDLE hClipData = 0; - Pixmap pixmap = 0; - 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("Request for %s (wFormat=%x %s)\n", - itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat, NULL, 0 )); - TSXFree(itemFmtName); - - hClipData = GetClipboardData(wFormat); - if ( !hClipData ) - { - TRACE("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(hwnd, hdc); - } - else if (wFormat == CF_BITMAP) - { - HWND hwnd = GetOpenClipboardWindow(); - HDC hdc = GetDC(hwnd); - - pixmap = X11DRV_BITMAP_CreatePixmapFromBitmap(hClipData, hdc); - - ReleaseDC(hwnd, hdc); - } - else - { - FIXME("%s to PIXMAP conversion not yet implemented!\n", - CLIPBOARD_GetFormatName(wFormat, NULL, 0)); - rprop = None; - goto END; - } - - TRACE("\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("(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("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( Display *display, Window requestor, - Atom target, Atom rprop ) -{ - HANDLE hClipData = 0; - void* lpClipData; - UINT wFormat; - char * itemFmtName; - int cBytes; - int xRc; - int bemf; - - /* - * 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("Request for %s (wFormat=%x %s)\n", - itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat, NULL, 0)); - TSXFree(itemFmtName); - - hClipData = GetClipboardData(wFormat); - - bemf = wFormat == CF_METAFILEPICT || wFormat == CF_ENHMETAFILE; - if (bemf) - hClipData = X11DRV_CLIPBOARD_SerializeMetafile(wFormat, hClipData, sizeof(hClipData), TRUE); - - if( hClipData && (lpClipData = GlobalLock(hClipData)) ) - { - cBytes = GlobalSize(hClipData); - - TRACE("\tUpdating property %s, %d bytes...\n", - TSXGetAtomName(display, rprop), cBytes); - - xRc = TSXChangeProperty(display, requestor, rprop, - target, 8, PropModeReplace, - (unsigned char *)lpClipData, cBytes); - TRACE("(Rc=%d)\n", xRc); - - GlobalUnlock(hClipData); - } - else - { - TRACE("\tCould not retrieve native format!\n"); - rprop = None; /* Fail the request */ - } - - if (bemf) /* We must free serialized metafile data */ - GlobalFree(hClipData); + HeapFree(GetProcessHeap(), 0, targets); return rprop; } @@ -1016,10 +782,8 @@ static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BO XSelectionEvent result; Atom rprop = None; Window request = event->requestor; - BOOL couldOpen = FALSE; - Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False); - Atom xaTargets = TSXInternAtom(display, "TARGETS", False); - Atom xaMultiple = TSXInternAtom(display, "MULTIPLE", False); + + TRACE_(clipboard)("\n"); /* * We can only handle the selection request if : @@ -1029,8 +793,7 @@ static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BO */ if ( !bIsMultiple ) { - if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) ) - || !(couldOpen = OpenClipboard(hWnd)) ) + if (((event->selection != XA_PRIMARY) && (event->selection != xaClipboard))) goto END; } @@ -1051,36 +814,48 @@ static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BO /* MULTIPLE selection request */ rprop = EVENT_SelectionRequest_MULTIPLE( hWnd, event ); } - else if(event->target == XA_STRING) /* treat CF_TEXT as Unix text */ - { - /* XA_STRING selection request */ - rprop = EVENT_SelectionRequest_STRING( display, request, event->target, rprop ); - } - else if(event->target == XA_PIXMAP) /* Convert DIB's to Pixmaps */ - { - /* XA_PIXMAP selection request */ - rprop = EVENT_SelectionRequest_PIXMAP( display, request, event->target, rprop ); - } - else if(event->target == XA_BITMAP) /* Convert DIB's to 1-bit Pixmaps */ - { - /* XA_BITMAP selection request - TODO: create a monochrome Pixmap */ - rprop = EVENT_SelectionRequest_PIXMAP( display, request, XA_PIXMAP, rprop ); - } - else if(X11DRV_CLIPBOARD_IsNativeProperty(event->target)) /* * */ - { - /* All selection requests */ - rprop = EVENT_SelectionRequest_WCF( display, request, event->target, rprop ); - } else - rprop = None; /* Don't support this format */ + { + LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupProperty(event->target); + + if (!lpFormat) + lpFormat = X11DRV_CLIPBOARD_LookupAliasProperty(event->target); + + if (lpFormat) + { + LPWINE_CLIPDATA lpData = X11DRV_CLIPBOARD_LookupData(lpFormat->wFormatID); + + if (lpData) + { + unsigned char* lpClipData; + DWORD cBytes; + HANDLE hClipData = lpFormat->lpDrvExportFunc(lpData, &cBytes); + + if (hClipData && (lpClipData = GlobalLock(hClipData))) + { + + TRACE_(clipboard)("\tUpdating property %s, %ld bytes\n", + lpFormat->Name, cBytes); + + TSXChangeProperty(display, request, rprop, event->target, + 8, PropModeReplace, (unsigned char *)lpClipData, cBytes); + + GlobalUnlock(hClipData); + GlobalFree(hClipData); + } + } + } + else + { + if (TRACE_ON(clipboard)) + { + TRACE_(clipboard)("Request for property %s (%ld) failed\n", + TSXGetAtomName(display, event->target), event->target); + } + } + } END: - /* close clipboard only if we opened before */ - if(couldOpen) CloseClipboard(); - - if( rprop == None) - TRACE("\tRequest ignored\n"); - /* reply to sender * SelectionNotify should be sent only at the end of a MULTIPLE request */ @@ -1103,8 +878,6 @@ END: */ static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event ) { - Atom xaClipboard = TSXInternAtom(event->display, "CLIPBOARD", False); - if (event->selection == XA_PRIMARY || event->selection == xaClipboard) X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd ); } @@ -1125,9 +898,6 @@ static void EVENT_PropertyNotify( XPropertyEvent *event ) { TRACE("\tPropertyDelete for atom %s on window %ld\n", TSXGetAtomName(event->display, event->atom), (long)event->window); - - if (X11DRV_IsSelectionOwner()) - X11DRV_CLIPBOARD_FreeResources( event->atom ); break; } diff --git a/dlls/x11drv/x11drv.h b/dlls/x11drv/x11drv.h index 46048615092..ace4f62e1de 100644 --- a/dlls/x11drv/x11drv.h +++ b/dlls/x11drv/x11drv.h @@ -373,15 +373,59 @@ extern Atom _kde_net_wm_system_tray_window_for; /* X11 clipboard driver */ -extern void X11DRV_CLIPBOARD_FreeResources( Atom property ); -extern BOOL X11DRV_CLIPBOARD_RegisterPixmapResource( Atom property, Pixmap pixmap ); -extern BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop); -extern UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName); -extern Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT id); +typedef struct tagPROPERTYFORMATMAP +{ + LPCSTR lpszProperty; + LPCSTR lpszFormat; +} PROPERTYFORMATMAP; + +typedef struct tagPROPERTYALIASMAP +{ + LPCSTR lpszProperty; + UINT drvDataProperty; + LPCSTR lpszAlias; + UINT drvDataAlias; +} PROPERTYALIASMAP; + +typedef struct tagWINE_CLIPDATA { + UINT wFormatID; + HANDLE16 hData16; + HANDLE hData32; + UINT wFlags; + struct tagWINE_CLIPDATA *PrevData; + struct tagWINE_CLIPDATA *NextData; +} WINE_CLIPDATA, *LPWINE_CLIPDATA; + +typedef HANDLE (*DRVEXPORTFUNC)(LPWINE_CLIPDATA lpData, LPDWORD lpBytes); +typedef HANDLE (*DRVIMPORTFUNC)(LPBYTE hData, UINT cBytes); + +typedef struct tagWINE_CLIPFORMAT { + UINT wFormatID; + LPSTR Name; + UINT drvData; + UINT wFlags; + DRVIMPORTFUNC lpDrvImportFunc; + DRVEXPORTFUNC lpDrvExportFunc; + struct tagWINE_CLIPFORMAT *PrevFormat; + struct tagWINE_CLIPFORMAT *NextFormat; +} WINE_CLIPFORMAT, *LPWINE_CLIPFORMAT; + +#define CF_FLAG_BUILTINFMT 1 /* Built-in windows format */ +#define CF_FLAG_SYNTHESIZED 8 /* Implicitly converted data */ + +extern Atom xaClipboard; +extern Atom xaTargets; +extern Atom xaMultiple; + +extern BOOL X11DRV_InitClipboard(Display *display); extern void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd); -extern BOOL X11DRV_IsSelectionOwner(void); -extern BOOL X11DRV_GetClipboardData(UINT wFormat); -extern HANDLE X11DRV_CLIPBOARD_SerializeMetafile(INT wformat, HANDLE hdata, INT cbytes, BOOL out); +extern INT X11DRV_CountClipboardFormats(void); +extern UINT X11DRV_EnumClipboardFormats(UINT wFormat); +extern LPWINE_CLIPFORMAT X11DRV_CLIPBOARD_LookupFormat(WORD wID); +extern LPWINE_CLIPFORMAT X11DRV_CLIPBOARD_LookupProperty(UINT drvData); +extern LPWINE_CLIPDATA X11DRV_CLIPBOARD_LookupData(DWORD wID); +extern UINT X11DRV_CLIPBOARD_LookupPropertyAlias(UINT drvDataProperty); +extern LPWINE_CLIPFORMAT X11DRV_CLIPBOARD_LookupAliasProperty(UINT drvDataAlias); /* X11 event driver */ diff --git a/dlls/x11drv/x11drv.spec b/dlls/x11drv/x11drv.spec index 94469d72218..c500c75d8ac 100644 --- a/dlls/x11drv/x11drv.spec +++ b/dlls/x11drv/x11drv.spec @@ -74,32 +74,34 @@ @ cdecl SetScreenSaveActive(long) X11DRV_SetScreenSaveActive @ cdecl ChangeDisplaySettingsExW (ptr ptr long long long) X11DRV_ChangeDisplaySettingsExW @ cdecl EnumDisplaySettingsExW (ptr long ptr long) X11DRV_EnumDisplaySettingsExW +@ cdecl AcquireClipboard() X11DRV_AcquireClipboard +@ cdecl CountClipboardFormats() X11DRV_CountClipboardFormats @ cdecl CreateWindow(long ptr long) X11DRV_CreateWindow @ cdecl DestroyWindow(long) X11DRV_DestroyWindow -@ cdecl GetDC(long long long long) X11DRV_GetDC +@ cdecl EmptyClipboard() X11DRV_EmptyClipboard +@ cdecl EndClipboardUpdate(long) X11DRV_EndClipboardUpdate +@ cdecl EnumClipboardFormats(long) X11DRV_EnumClipboardFormats @ cdecl ForceWindowRaise(long) X11DRV_ForceWindowRaise +@ cdecl GetClipboardData(long ptr ptr) X11DRV_GetClipboardData +@ cdecl GetClipboardFormatName(long str long) X11DRV_GetClipboardFormatName +@ cdecl GetDC(long long long long) X11DRV_GetDC +@ cdecl IsClipboardFormatAvailable(long) X11DRV_IsClipboardFormatAvailable @ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) X11DRV_MsgWaitForMultipleObjectsEx +@ cdecl RegisterClipboardFormat(str) X11DRV_RegisterClipboardFormat @ cdecl ReleaseDC(long long) X11DRV_ReleaseDC +@ cdecl ResetSelectionOwner(long long) X11DRV_ResetSelectionOwner @ cdecl ScrollDC(long long long ptr ptr long ptr) X11DRV_ScrollDC @ cdecl ScrollWindowEx(long long long ptr ptr long ptr long) X11DRV_ScrollWindowEx +@ cdecl SetClipboardData(long long long) X11DRV_SetClipboardData @ cdecl SetFocus(long) X11DRV_SetFocus @ cdecl SetParent(long long) X11DRV_SetParent +@ cdecl SetWindowIcon(long long long) X11DRV_SetWindowIcon @ cdecl SetWindowPos(ptr) X11DRV_SetWindowPos @ cdecl SetWindowRgn(long long long) X11DRV_SetWindowRgn -@ cdecl SetWindowIcon(long long long) X11DRV_SetWindowIcon @ cdecl SetWindowStyle(ptr long) X11DRV_SetWindowStyle @ cdecl SetWindowText(long wstr) X11DRV_SetWindowText @ cdecl ShowWindow(long long) X11DRV_ShowWindow @ cdecl SysCommandSizeMove(long long) X11DRV_SysCommandSizeMove -@ cdecl AcquireClipboard() X11DRV_AcquireClipboard -@ cdecl ReleaseClipboard() X11DRV_ReleaseClipboard -@ cdecl SetClipboardData(long) X11DRV_SetClipboardData -@ cdecl GetClipboardData(long) X11DRV_GetClipboardData -@ cdecl IsClipboardFormatAvailable(long) X11DRV_IsClipboardFormatAvailable -@ cdecl RegisterClipboardFormat(str) X11DRV_RegisterClipboardFormat -@ cdecl GetClipboardFormatName(long str long) X11DRV_GetClipboardFormatName -@ cdecl IsSelectionOwner() X11DRV_IsSelectionOwner -@ cdecl ResetSelectionOwner(ptr long) X11DRV_ResetSelectionOwner # X11 locks @ cdecl -norelay wine_tsx11_lock() diff --git a/dlls/x11drv/x11drv_main.c b/dlls/x11drv/x11drv_main.c index 5e4df9d3b7e..4b90bdb53e3 100644 --- a/dlls/x11drv/x11drv_main.c +++ b/dlls/x11drv/x11drv_main.c @@ -362,6 +362,13 @@ static void process_attach(void) ExitProcess(1); } + /* Initialize clipboard */ + if (!X11DRV_InitClipboard( display )) + { + ERR( "Couldn't Initialize clipboard.\n" ); + ExitProcess(1); + } + #ifdef HAVE_LIBXXF86VM /* initialize XVidMode */ X11DRV_XF86VM_Init(); diff --git a/include/clipboard.h b/include/clipboard.h index 500b7e566b3..9279b13bca4 100644 --- a/include/clipboard.h +++ b/include/clipboard.h @@ -2,6 +2,7 @@ * Copyright 1994 Martin Ayotte * Copyright 1996 Alex Korobka * Copyright 1999 Noel Borthwick + * Copyright 2003 Ulrich Czekalla for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,29 +23,14 @@ #define __WINE_CLIPBOARD_H #include "windef.h" -#include "wine/windef16.h" -struct tagWND; - -typedef struct tagWINE_CLIPFORMAT { - UINT wFormatID; - UINT wRefCount; - BOOL wDataPresent; - LPSTR Name; - HANDLE16 hData16; - HANDLE hDataSrc32; - HANDLE hData32; - ULONG drvData; - struct tagWINE_CLIPFORMAT *PrevFormat; - struct tagWINE_CLIPFORMAT *NextFormat; -} WINE_CLIPFORMAT, *LPWINE_CLIPFORMAT; - -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, LPSTR buf, INT size); -extern void CLIPBOARD_ReleaseOwner(); +typedef struct tagCLIPBOARDINFO +{ + HWND hWndOpen; + HWND hWndOwner; + HWND hWndViewer; + UINT seqno; + UINT flags; +} CLIPBOARDINFO, *LPCLIPBOARDINFO; #endif /* __WINE_CLIPBOARD_H */ diff --git a/include/user.h b/include/user.h index 1108e9aae20..1362696c7b2 100644 --- a/include/user.h +++ b/include/user.h @@ -82,15 +82,17 @@ typedef struct tagUSER_DRIVER { BOOL (*pGetScreenSaveActive)(void); void (*pSetScreenSaveActive)(BOOL); /* clipboard functions */ - void (*pAcquireClipboard)(void); /* Acquire selection */ - void (*pReleaseClipboard)(void); /* Release selection */ - void (*pSetClipboardData)(UINT); /* Set specified selection data */ - BOOL (*pGetClipboardData)(UINT); /* Get specified selection data */ - BOOL (*pIsClipboardFormatAvailable)(UINT); /* Check if specified format is available */ - INT (*pRegisterClipboardFormat)(LPCSTR); /* Register a clipboard format */ - BOOL (*pGetClipboardFormatName)(UINT, LPSTR, UINT); /* Get a clipboard format name */ - BOOL (*pIsSelectionOwner)(void); /* Check if we own the selection */ + void (*pAcquireClipboard)(HWND); /* Acquire selection */ + BOOL (*pCountClipboardFormats)(void); /* Count available clipboard formats */ + void (*pEmptyClipboard)(void); /* Empty clipboard data */ + BOOL (*pEndClipboardUpdate)(void); /* End clipboard update */ + BOOL (*pEnumClipboardFormats)(UINT); /* Enumerate clipboard formats */ + BOOL (*pGetClipboardData)(UINT, HANDLE16*, HANDLE*); /* Get specified selection data */ + BOOL (*pGetClipboardFormatName)(UINT, LPSTR, UINT); /* Get a clipboard format name */ + BOOL (*pIsClipboardFormatAvailable)(UINT); /* Check if specified format is available */ + INT (*pRegisterClipboardFormat)(LPCSTR); /* Register a clipboard format */ void (*pResetSelectionOwner)(HWND, BOOL); + BOOL (*pSetClipboardData)(UINT, HANDLE16, HANDLE); /* Set specified selection data */ /* display modes */ LONG (*pChangeDisplaySettingsExW)(LPCWSTR,LPDEVMODEW,HWND,DWORD,LPVOID); BOOL (*pEnumDisplaySettingsExW)(LPCWSTR,DWORD,LPDEVMODEW,DWORD); @@ -163,6 +165,8 @@ extern INT SYSPARAMS_GetMouseButtonSwap( void ); extern HPALETTE WINAPI SelectPalette( HDC hDC, HPALETTE hPal, BOOL bForceBackground ); +extern BOOL CLIPBOARD_ReleaseOwner(void); + extern DWORD USER16_AlertableWait; /* HANDLE16 <-> HANDLE conversions */ diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 27a94cfff1b..b0f664aec71 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3041,6 +3041,36 @@ struct get_next_hook_reply }; + +struct set_clipboard_info_request +{ + struct request_header __header; + unsigned int flags; + user_handle_t clipboard; + user_handle_t owner; + user_handle_t viewer; + unsigned int seqno; +}; +struct set_clipboard_info_reply +{ + struct reply_header __header; + unsigned int flags; + user_handle_t old_clipboard; + user_handle_t old_owner; + user_handle_t old_viewer; + unsigned int seqno; +}; + +#define SET_CB_OPEN 0x001 +#define SET_CB_OWNER 0x002 +#define SET_CB_VIEWER 0x004 +#define SET_CB_SEQNO 0x008 +#define SET_CB_RELOWNER 0x010 +#define SET_CB_CLOSE 0x020 +#define CB_OPEN 0x040 +#define CB_OWNER 0x080 + + enum request { REQ_new_process, @@ -3218,6 +3248,7 @@ enum request REQ_start_hook_chain, REQ_finish_hook_chain, REQ_get_next_hook, + REQ_set_clipboard_info, REQ_NB_REQUESTS }; @@ -3400,6 +3431,7 @@ union generic_request struct start_hook_chain_request start_hook_chain_request; struct finish_hook_chain_request finish_hook_chain_request; struct get_next_hook_request get_next_hook_request; + struct set_clipboard_info_request set_clipboard_info_request; }; union generic_reply { @@ -3580,8 +3612,9 @@ union generic_reply struct start_hook_chain_reply start_hook_chain_reply; struct finish_hook_chain_reply finish_hook_chain_reply; struct get_next_hook_reply get_next_hook_reply; + struct set_clipboard_info_reply set_clipboard_info_reply; }; -#define SERVER_PROTOCOL_VERSION 111 +#define SERVER_PROTOCOL_VERSION 112 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/Makefile.in b/server/Makefile.in index 1d6211c04ba..828b37f674c 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -9,6 +9,7 @@ C_SRCS = \ async.c \ atom.c \ change.c \ + clipboard.c \ console.c \ context_i386.c \ context_powerpc.c \ diff --git a/server/clipboard.c b/server/clipboard.c new file mode 100644 index 00000000000..5a885a0febe --- /dev/null +++ b/server/clipboard.c @@ -0,0 +1,160 @@ +/* + * Server-side clipboard management + * + * Copyright (C) 2002 Ulrich Czekalla + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include + +#include "request.h" +#include "object.h" +#include "user.h" + +struct thread *cbthread; /* thread id that has clipboard open */ +static user_handle_t clipboard; /* window that has clipboard open */ + +struct thread *cbowner; /* thread id that owns the clipboard */ +static user_handle_t owner; /* window that owns the clipboard data */ + +static user_handle_t viewer; /* first window in clipboard viewer list */ +static unsigned int seqno; /* clipboard change sequence number */ +static time_t seqnots; /* time stamp of last seqno increment */ + +#define MINUPDATELAPSE 2 + +/* Called when thread terminates to allow release of clipboard */ +void cleanup_clipboard_thread(struct thread *thread) +{ + if (thread == cbthread) + { + clipboard = 0; + cbthread = NULL; + } + if (thread == cbowner) + { + owner = 0; + cbowner = NULL; + } +} + +static int set_clipboard_window(user_handle_t win, int clear) +{ + if (cbthread && cbthread != current) + { + set_error(STATUS_WAS_LOCKED); + return 0; + } + else if (!clear) + { + clipboard = win; + cbthread = current; + } + else + { + cbthread = NULL; + clipboard = 0; + } + return 1; +} + + +static int set_clipboard_owner(user_handle_t win, int clear) +{ + if (cbthread == current) + { + if (!clear) + { + cbowner = current; + owner = win; + } + else + { + cbowner = 0; + owner = 0; + } + seqno++; + return 1; + } + else + { + set_error(STATUS_WAS_LOCKED); + return 0; + } +} + + +static int get_seqno(void) +{ + time_t tm = time(NULL); + + if (!cbowner && (tm > (seqnots + MINUPDATELAPSE))) + { + seqnots = tm; + seqno++; + } + return seqno; +} + + +DECL_HANDLER(set_clipboard_info) +{ + reply->old_clipboard = clipboard; + reply->old_owner = owner; + reply->old_viewer = viewer; + + if (req->flags & SET_CB_OPEN) + { + if (!set_clipboard_window(req->clipboard, 0)) + return; + } + else if (req->flags & SET_CB_CLOSE) + { + if (!set_clipboard_window(0, 1)) + return; + } + + if (req->flags & SET_CB_OWNER) + { + if (!set_clipboard_owner(req->owner, 0)) + return; + } + else if (req->flags & SET_CB_RELOWNER) + { + if (!set_clipboard_owner(0, 1)) + return; + } + + if (req->flags & SET_CB_VIEWER) + viewer = req->viewer; + + if (req->flags & SET_CB_SEQNO) + seqno++; + + reply->seqno = get_seqno(); + + if (cbthread == current) + reply->flags |= CB_OPEN; + + if (cbowner == current) + reply->flags |= CB_OWNER; +} diff --git a/server/protocol.def b/server/protocol.def index fb820a8a4b5..bb3e37a89ee 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2124,3 +2124,28 @@ enum message_type int next_unicode; /* is the next a unicode hook? */ VARARG(module,unicode_str); /* module name */ @END + + +/* Set/get clipboard information */ +@REQ(set_clipboard_info) + unsigned int flags; /* flags for fields to set (see below) */ + user_handle_t clipboard; /* clipboard window */ + user_handle_t owner; /* clipboard owner */ + user_handle_t viewer; /* first clipboard viewer */ + unsigned int seqno; /* change sequence number */ +@REPLY + unsigned int flags; /* status flags (see below) */ + user_handle_t old_clipboard; /* old clipboard window */ + user_handle_t old_owner; /* old clipboard owner */ + user_handle_t old_viewer; /* old clipboard viewer */ + unsigned int seqno; /* current sequence number */ +@END + +#define SET_CB_OPEN 0x001 +#define SET_CB_OWNER 0x002 +#define SET_CB_VIEWER 0x004 +#define SET_CB_SEQNO 0x008 +#define SET_CB_RELOWNER 0x010 +#define SET_CB_CLOSE 0x020 +#define CB_OPEN 0x040 +#define CB_OWNER 0x080 diff --git a/server/request.h b/server/request.h index 60d943972c4..e28594effca 100644 --- a/server/request.h +++ b/server/request.h @@ -278,6 +278,7 @@ DECL_HANDLER(remove_hook); DECL_HANDLER(start_hook_chain); DECL_HANDLER(finish_hook_chain); DECL_HANDLER(get_next_hook); +DECL_HANDLER(set_clipboard_info); #ifdef WANT_REQUEST_HANDLERS @@ -459,6 +460,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_start_hook_chain, (req_handler)req_finish_hook_chain, (req_handler)req_get_next_hook, + (req_handler)req_set_clipboard_info, }; #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/thread.c b/server/thread.c index c6ef4acec4a..a600bf149f1 100644 --- a/server/thread.c +++ b/server/thread.c @@ -210,6 +210,7 @@ static void cleanup_thread( struct thread *thread ) if (thread->wait_fd) release_object( thread->wait_fd ); if (thread->hooks) release_object( thread->hooks ); free_msg_queue( thread ); + cleanup_clipboard_thread(thread); destroy_thread_windows( thread ); for (i = 0; i < MAX_INFLIGHT_FDS; i++) { diff --git a/server/trace.c b/server/trace.c index 1e93cb1c061..2067695ca20 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2458,6 +2458,24 @@ static void dump_get_next_hook_reply( const struct get_next_hook_reply *req ) dump_varargs_unicode_str( cur_size ); } +static void dump_set_clipboard_info_request( const struct set_clipboard_info_request *req ) +{ + fprintf( stderr, " flags=%08x,", req->flags ); + fprintf( stderr, " clipboard=%p,", req->clipboard ); + fprintf( stderr, " owner=%p,", req->owner ); + fprintf( stderr, " viewer=%p,", req->viewer ); + fprintf( stderr, " seqno=%08x", req->seqno ); +} + +static void dump_set_clipboard_info_reply( const struct set_clipboard_info_reply *req ) +{ + fprintf( stderr, " flags=%08x,", req->flags ); + fprintf( stderr, " old_clipboard=%p,", req->old_clipboard ); + fprintf( stderr, " old_owner=%p,", req->old_owner ); + fprintf( stderr, " old_viewer=%p,", req->old_viewer ); + fprintf( stderr, " seqno=%08x", req->seqno ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -2634,6 +2652,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_start_hook_chain_request, (dump_func)dump_finish_hook_chain_request, (dump_func)dump_get_next_hook_request, + (dump_func)dump_set_clipboard_info_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -2812,6 +2831,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_start_hook_chain_reply, (dump_func)0, (dump_func)dump_get_next_hook_reply, + (dump_func)dump_set_clipboard_info_reply, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -2990,6 +3010,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "start_hook_chain", "finish_hook_chain", "get_next_hook", + "set_clipboard_info", }; /* ### make_requests end ### */ diff --git a/server/user.h b/server/user.h index 24c18d5a8d4..0d038b6934c 100644 --- a/server/user.h +++ b/server/user.h @@ -42,6 +42,10 @@ extern user_handle_t get_user_full_handle( user_handle_t handle ); extern void *free_user_handle( user_handle_t handle ); extern void *next_user_handle( user_handle_t *handle, enum user_object type ); +/* clipboard functions */ + +extern void cleanup_clipboard_thread( struct thread *thread ); + /* hook functions */ extern void close_global_hooks(void); diff --git a/windows/clipboard.c b/windows/clipboard.c index 7d0510b6feb..e9699d4569a 100644 --- a/windows/clipboard.c +++ b/windows/clipboard.c @@ -4,6 +4,7 @@ * Copyright 1994 Martin Ayotte * 1996 Alex Korobka * 1999 Noel Borthwick + * 2003 Ulrich Czekalla for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -43,6 +44,7 @@ #include "winbase.h" #include "wingdi.h" #include "winuser.h" +#include "winerror.h" #include "wine/winuser16.h" #include "wine/winbase16.h" #include "heap.h" @@ -52,689 +54,164 @@ #include "wine/debug.h" #include "wine/unicode.h" +#include "wine/server.h" WINE_DEFAULT_DEBUG_CHANNEL(clipboard); #define CF_REGFORMATBASE 0xC000 -#define HGDIOBJ_32(handle16) ((HGDIOBJ)(ULONG_PTR)(handle16)) -/************************************************************************** - * Clipboard context global variables +/* + * Indicates if data has changed since open. */ - -static DWORD ClipLock = 0; -static BOOL bCBHasChanged = FALSE; - -static HWND hWndClipWindow; /* window that last opened clipboard */ -static HWND hWndClipOwner; /* current clipboard owner */ -static DWORD ClipOwner; /* clipboard owner's thread id */ -static HWND hWndViewer; /* start of viewers chain */ - -/* Clipboard cache initial data. - * WARNING: This data ordering is dependent on the WINE_CLIPFORMAT structure - * declared in clipboard.h - */ -WINE_CLIPFORMAT ClipFormats[] = { - { CF_TEXT, 1, 0, "Text", 0, 0, 0, 0, NULL, &ClipFormats[1]}, - { CF_BITMAP, 1, 0, "Bitmap", 0, 0, 0, 0, &ClipFormats[0], &ClipFormats[2]}, - { CF_METAFILEPICT, 1, 0, "MetaFile Picture", 0, 0, 0, 0, &ClipFormats[1], &ClipFormats[3]}, - { CF_SYLK, 1, 0, "Sylk", 0, 0, 0, 0, &ClipFormats[2], &ClipFormats[4]}, - { CF_DIF, 1, 0, "DIF", 0, 0, 0, 0, &ClipFormats[3], &ClipFormats[5]}, - { CF_TIFF, 1, 0, "TIFF", 0, 0, 0, 0, &ClipFormats[4], &ClipFormats[6]}, - { CF_OEMTEXT, 1, 0, "OEM Text", 0, 0, 0, 0, &ClipFormats[5], &ClipFormats[7]}, - { CF_DIB, 1, 0, "DIB", 0, 0, 0, 0, &ClipFormats[6], &ClipFormats[8]}, - { CF_PALETTE, 1, 0, "Palette", 0, 0, 0, 0, &ClipFormats[7], &ClipFormats[9]}, - { CF_PENDATA, 1, 0, "PenData", 0, 0, 0, 0, &ClipFormats[8], &ClipFormats[10]}, - { CF_RIFF, 1, 0, "RIFF", 0, 0, 0, 0, &ClipFormats[9], &ClipFormats[11]}, - { CF_WAVE, 1, 0, "Wave", 0, 0, 0, 0, &ClipFormats[10], &ClipFormats[12]}, - { CF_UNICODETEXT, 1, 0, "Unicode Text", 0, 0, 0, 0, &ClipFormats[11], &ClipFormats[13]}, - { CF_OWNERDISPLAY, 1, 0, "Owner Display", 0, 0, 0, 0, &ClipFormats[12], &ClipFormats[14]}, - { CF_DSPTEXT, 1, 0, "DSPText", 0, 0, 0, 0, &ClipFormats[13], &ClipFormats[15]}, - { CF_DSPMETAFILEPICT, 1, 0, "DSPMetaFile Picture", 0, 0, 0, 0, &ClipFormats[14], &ClipFormats[16]}, - { CF_DSPBITMAP, 1, 0, "DSPBitmap", 0, 0, 0, 0, &ClipFormats[15], &ClipFormats[17]}, - { CF_HDROP, 1, 0, "HDROP", 0, 0, 0, 0, &ClipFormats[16], &ClipFormats[18]}, - { CF_ENHMETAFILE, 1, 0, "Enhmetafile", 0, 0, 0, 0, &ClipFormats[17], NULL} -}; +static BOOL bCBHasChanged = FALSE; /************************************************************************** - * Internal Clipboard implementation methods - **************************************************************************/ - - -/************************************************************************** - * CLIPBOARD_LookupFormat + * CLIPBOARD_SetClipboardOwner */ -static LPWINE_CLIPFORMAT __lookup_format( LPWINE_CLIPFORMAT lpFormat, WORD wID ) +BOOL CLIPBOARD_SetClipboardOwner(HWND hWnd) { - while(TRUE) + BOOL bRet = FALSE; + + TRACE(" hWnd(%p)\n", hWnd); + + SERVER_START_REQ( set_clipboard_info ) { - if (lpFormat == NULL || - lpFormat->wFormatID == wID) break; - lpFormat = lpFormat->NextFormat; - } - return lpFormat; -} + req->flags = SET_CB_OWNER; + req->owner = WIN_GetFullHandle( hWnd ); -LPWINE_CLIPFORMAT CLIPBOARD_LookupFormat( WORD wID ) -{ - return __lookup_format( ClipFormats, wID ); -} - -/************************************************************************** - * CLIPBOARD_IsLocked - * Check if the clipboard cache is available to the caller - */ -BOOL CLIPBOARD_IsLocked() -{ - BOOL bIsLocked = TRUE; - - /* - * 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 ( ClipLock == GetCurrentThreadId() ) - bIsLocked = FALSE; - - else if ( ClipOwner == GetCurrentThreadId() ) - { - /* 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. - */ -#if 0 - MESSAGEQUEUE *queue = QUEUE_Current(); - - if ( queue - && queue->smWaiting - && queue->smWaiting->msg == WM_RENDERFORMAT - && queue->smWaiting->hSrcQueue - ) - bIsLocked = FALSE; -#else - /* FIXME: queue check no longer possible */ - bIsLocked = FALSE; -#endif - } - - return bIsLocked; -} - -/************************************************************************** - * CLIPBOARD_ReleaseOwner - * Gives up ownership of the clipboard - */ -void CLIPBOARD_ReleaseOwner() -{ - hWndClipOwner = 0; - ClipOwner = 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 - */ -void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange) -{ - if( (lpFormat->wFormatID >= CF_GDIOBJFIRST && - lpFormat->wFormatID <= CF_GDIOBJLAST) || lpFormat->wFormatID == CF_BITMAP - || lpFormat->wFormatID == CF_PALETTE) - { - if (lpFormat->hData32) - DeleteObject(lpFormat->hData32); - if (lpFormat->hData16) - DeleteObject(HGDIOBJ_32(lpFormat->hData16)); - } - else if( lpFormat->wFormatID == CF_METAFILEPICT ) - { - if (lpFormat->hData32) - { - DeleteMetaFile( ((METAFILEPICT *)GlobalLock( lpFormat->hData32 ))->hMF ); - PostMessageA(hWndClipOwner, WM_TIMER, - (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc); - if (lpFormat->hDataSrc32) + if (wine_server_call_err( req )) { - /* 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 - structure between 16bit and 32bit clipboards. The MetaFile - should of course only be deleted once. */ - GlobalFree16(lpFormat->hData16); - } - if (lpFormat->hData16) - { - DeleteMetaFile16( ((METAFILEPICT16 *)GlobalLock16( lpFormat->hData16 ))->hMF ); - GlobalFree16(lpFormat->hData16); - } - } - else - { - if (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) - { - /* 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); - } - - 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 - */ -BOOL CLIPBOARD_IsPresent(WORD wFormat) -{ - /* special case */ - - if( wFormat == CF_TEXT || wFormat == CF_OEMTEXT || wFormat == CF_UNICODETEXT ) - return ClipFormats[CF_TEXT-1].wDataPresent || - ClipFormats[CF_OEMTEXT-1].wDataPresent || - ClipFormats[CF_UNICODETEXT-1].wDataPresent; - else - { - LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat ); - if( lpFormat ) return (lpFormat->wDataPresent); - } - 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 - */ -BOOL CLIPBOARD_IsMemoryObject( WORD wFormat ) -{ - switch(wFormat) - { - case CF_BITMAP: - case CF_METAFILEPICT: - case CF_DSPTEXT: - case CF_ENHMETAFILE: - case CF_HDROP: - case CF_PALETTE: - case CF_PENDATA: - return FALSE; - default: - return TRUE; - } -} - -/*********************************************************************** - * CLIPBOARD_GlobalDupMem( HGLOBAL ) - * Helper method to duplicate an HGLOBAL chunk of memory into shared memory - */ -HGLOBAL CLIPBOARD_GlobalDupMem( HGLOBAL hGlobalSrc ) -{ - HGLOBAL hGlobalDest; - PVOID pGlobalSrc, pGlobalDest; - DWORD cBytes; - - if ( !hGlobalSrc ) - return 0; - - cBytes = GlobalSize(hGlobalSrc); - if ( 0 == cBytes ) - return 0; - - /* Turn on the DDESHARE and _MOVEABLE flags explicitly */ - hGlobalDest = GlobalAlloc( GlobalFlags(hGlobalSrc) | GMEM_DDESHARE | GMEM_MOVEABLE, - cBytes ); - if ( !hGlobalDest ) - return 0; - - pGlobalSrc = GlobalLock(hGlobalSrc); - pGlobalDest = GlobalLock(hGlobalDest); - if ( !pGlobalSrc || !pGlobalDest ) - return 0; - - memcpy(pGlobalDest, pGlobalSrc, cBytes); - - GlobalUnlock(hGlobalSrc); - GlobalUnlock(hGlobalDest); - - return hGlobalDest; -} - -/************************************************************************** - * CLIPBOARD_GetFormatName - * Gets the format name associated with an ID - */ -char * CLIPBOARD_GetFormatName(UINT wFormat, LPSTR buf, INT size) -{ - LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat ); - - if (lpFormat) - { - if (buf) - { - strncpy(buf, lpFormat->Name, size); - CharLowerA(buf); - } - - return lpFormat->Name; - } - else - return 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 driver to render the data to the clipboard cache. - */ - TRACE("enter format=%d\n", lpFormat->wFormatID); - if ( !USER_Driver.pIsSelectionOwner() - && USER_Driver.pIsClipboardFormatAvailable( lpFormat->wFormatID ) ) - { - if ( !USER_Driver.pGetClipboardData( 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"); - SendMessageW( hWndClipOwner, WM_RENDERFORMAT, (WPARAM)lpFormat->wFormatID, 0 ); - } - else - { - WARN("\thWndClipOwner (%p) is lost!\n", hWndClipOwner); - CLIPBOARD_ReleaseOwner(); - lpFormat->wDataPresent = 0; - return FALSE; - } - } - - return (lpFormat->hData16 || lpFormat->hData32) ? TRUE : FALSE; -} - -/************************************************************************** - * CLIPBOARD_ConvertText - * Returns number of required/converted characters - not bytes! - */ -static INT CLIPBOARD_ConvertText(WORD src_fmt, void const *src, INT src_size, - WORD dst_fmt, void *dst, INT dst_size) -{ - UINT cp; - - if(src_fmt == CF_UNICODETEXT) - { - switch(dst_fmt) - { - case CF_TEXT: - cp = CP_ACP; - break; - case CF_OEMTEXT: - cp = CP_OEMCP; - break; - default: - return 0; - } - return WideCharToMultiByte(cp, 0, src, src_size, dst, dst_size, NULL, NULL); - } - - if(dst_fmt == CF_UNICODETEXT) - { - switch(src_fmt) - { - case CF_TEXT: - cp = CP_ACP; - break; - case CF_OEMTEXT: - cp = CP_OEMCP; - break; - default: - return 0; - } - return MultiByteToWideChar(cp, 0, src, src_size, dst, dst_size); - } - - if(!dst_size) return src_size; - - if(dst_size > src_size) dst_size = src_size; - - if(src_fmt == CF_TEXT ) - CharToOemBuffA(src, dst, dst_size); - else - OemToCharBuffA(src, dst, dst_size); - - return dst_size; -} - -/************************************************************************** - * 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 = NULL; - BOOL foundData = FALSE; - - /* Asked for CF_TEXT */ - if( wFormat == CF_TEXT) - { - if(ClipFormats[CF_TEXT-1].wDataPresent) - { - lpSource = &ClipFormats[CF_TEXT-1]; - lpTarget = &ClipFormats[CF_TEXT-1]; - foundData = TRUE; - TRACE("\t TEXT -> TEXT\n"); - } - else if(ClipFormats[CF_UNICODETEXT-1].wDataPresent) - { - /* Convert UNICODETEXT -> TEXT */ - lpSource = &ClipFormats[CF_UNICODETEXT-1]; - lpTarget = &ClipFormats[CF_TEXT-1]; - foundData = TRUE; - TRACE("\tUNICODETEXT -> TEXT\n"); - } - else if(ClipFormats[CF_OEMTEXT-1].wDataPresent) - { - /* Convert OEMTEXT -> TEXT */ - lpSource = &ClipFormats[CF_OEMTEXT-1]; - lpTarget = &ClipFormats[CF_TEXT-1]; - foundData = TRUE; - TRACE("\tOEMTEXT -> TEXT\n"); - } - } - /* Asked for CF_OEMTEXT */ - else if( wFormat == CF_OEMTEXT) - { - if(ClipFormats[CF_OEMTEXT-1].wDataPresent) - { - lpSource = &ClipFormats[CF_OEMTEXT-1]; - lpTarget = &ClipFormats[CF_OEMTEXT-1]; - foundData = TRUE; - TRACE("\tOEMTEXT -> OEMTEXT\n"); - } - else if(ClipFormats[CF_UNICODETEXT-1].wDataPresent) - { - /* Convert UNICODETEXT -> OEMTEXT */ - lpSource = &ClipFormats[CF_UNICODETEXT-1]; - lpTarget = &ClipFormats[CF_OEMTEXT-1]; - foundData = TRUE; - TRACE("\tUNICODETEXT -> OEMTEXT\n"); - } - else if(ClipFormats[CF_TEXT-1].wDataPresent) - { - /* Convert TEXT -> OEMTEXT */ - lpSource = &ClipFormats[CF_TEXT-1]; - lpTarget = &ClipFormats[CF_OEMTEXT-1]; - foundData = TRUE; - TRACE("\tTEXT -> OEMTEXT\n"); - } - } - /* Asked for CF_UNICODETEXT */ - else if( wFormat == CF_UNICODETEXT ) - { - if(ClipFormats[CF_UNICODETEXT-1].wDataPresent) - { - lpSource = &ClipFormats[CF_UNICODETEXT-1]; - lpTarget = &ClipFormats[CF_UNICODETEXT-1]; - foundData = TRUE; - TRACE("\tUNICODETEXT -> UNICODETEXT\n"); - } - else if(ClipFormats[CF_TEXT-1].wDataPresent) - { - /* Convert TEXT -> UNICODETEXT */ - lpSource = &ClipFormats[CF_TEXT-1]; - lpTarget = &ClipFormats[CF_UNICODETEXT-1]; - foundData = TRUE; - TRACE("\tTEXT -> UNICODETEXT\n"); - } - else if(ClipFormats[CF_OEMTEXT-1].wDataPresent) - { - /* Convert OEMTEXT -> UNICODETEXT */ - lpSource = &ClipFormats[CF_OEMTEXT-1]; - lpTarget = &ClipFormats[CF_UNICODETEXT-1]; - foundData = TRUE; - TRACE("\tOEMTEXT -> UNICODETEXT\n"); - } - } - if (!foundData) - { - if ((wFormat == CF_TEXT) || (wFormat == CF_OEMTEXT)) - { - lpSource = &ClipFormats[CF_UNICODETEXT-1]; - lpTarget = __lookup_format( ClipFormats, wFormat ); + ERR("Failed to set clipboard.\n"); } else { - lpSource = __lookup_format( ClipFormats, wFormat ); - lpTarget = lpSource; + bRet = TRUE; } } + SERVER_END_REQ; - /* 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 ) - { - INT src_chars, dst_chars, alloc_size; - LPCSTR lpstrS; - LPSTR lpstrT; - - if (lpSource->hData32) - { - lpstrS = (LPSTR)GlobalLock(lpSource->hData32); - } - else - { - lpstrS = (LPSTR)GlobalLock16(lpSource->hData16); - } - - if( !lpstrS ) return NULL; - - /* Text always NULL terminated */ - if(lpSource->wFormatID == CF_UNICODETEXT) - src_chars = strlenW((LPCWSTR)lpstrS)+1; - else - src_chars = strlen(lpstrS)+1; - - /* Calculate number of characters in the destination buffer */ - dst_chars = CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars, - lpTarget->wFormatID, NULL, 0); - if(!dst_chars) return NULL; - - TRACE("\tconverting from '%s' to '%s', %i chars\n", - lpSource->Name, lpTarget->Name, src_chars); - - /* Convert characters to bytes */ - if(lpTarget->wFormatID == CF_UNICODETEXT) - alloc_size = dst_chars * sizeof(WCHAR); - else - alloc_size = dst_chars; - - lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, alloc_size); - lpstrT = (LPSTR)GlobalLock(lpTarget->hData32); - - if( lpstrT ) - { - CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars, - lpTarget->wFormatID, lpstrT, dst_chars); - 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; + return bRet; } + /************************************************************************** - * CLIPBOARD_EnumClipboardFormats (internal) + * CLIPBOARD_GetClipboardInfo */ -static UINT CLIPBOARD_EnumClipboardFormats( UINT wFormat ) +BOOL CLIPBOARD_GetClipboardInfo(LPCLIPBOARDINFO cbInfo) { - LPWINE_CLIPFORMAT lpFormat = ClipFormats; - BOOL bFormatPresent; + BOOL bRet = FALSE; - if (wFormat == 0) /* start from the beginning */ - lpFormat = ClipFormats; - else + SERVER_START_REQ( set_clipboard_info ) { - /* walk up to the specified format record */ + req->flags = 0; - if( !(lpFormat = __lookup_format( lpFormat, wFormat )) ) - return 0; - lpFormat = lpFormat->NextFormat; /* right */ - } - - while(TRUE) - { - if (lpFormat == NULL) return 0; - - if(CLIPBOARD_IsPresent(lpFormat->wFormatID)) - break; - - /* Query the driver if not yet in the cache */ - if (!USER_Driver.pIsSelectionOwner()) + if (wine_server_call_err( req )) { - if(lpFormat->wFormatID == CF_UNICODETEXT || - lpFormat->wFormatID == CF_TEXT || - lpFormat->wFormatID == CF_OEMTEXT) - { - if(USER_Driver.pIsClipboardFormatAvailable(CF_UNICODETEXT) || - USER_Driver.pIsClipboardFormatAvailable(CF_TEXT) || - USER_Driver.pIsClipboardFormatAvailable(CF_OEMTEXT)) - bFormatPresent = TRUE; - else - bFormatPresent = FALSE; - } - else - bFormatPresent = USER_Driver.pIsClipboardFormatAvailable(lpFormat->wFormatID); + ERR("Failed to get clipboard owner.\n"); + } + else + { + cbInfo->hWndOpen = reply->old_clipboard; + cbInfo->hWndOwner = reply->old_owner; + cbInfo->hWndViewer = reply->old_viewer; + cbInfo->seqno = reply->seqno; + cbInfo->flags = reply->flags; - if(bFormatPresent) - break; + bRet = TRUE; + } + } + SERVER_END_REQ; + + return bRet; +} + + +/************************************************************************** + * CLIPBOARD_ReleaseOwner + */ +BOOL CLIPBOARD_ReleaseOwner(void) +{ + BOOL bRet = FALSE; + + SERVER_START_REQ( set_clipboard_info ) + { + req->flags = SET_CB_RELOWNER | SET_CB_SEQNO; + + if (wine_server_call_err( req )) + { + ERR("Failed to set clipboard.\n"); + } + else + { + bRet = TRUE; + } + } + SERVER_END_REQ; + + return bRet; +} + + +/************************************************************************** + * CLIPBOARD_OpenClipboard + */ +static BOOL CLIPBOARD_OpenClipboard(HWND hWnd) +{ + BOOL bRet = FALSE; + + SERVER_START_REQ( set_clipboard_info ) + { + req->flags = SET_CB_OPEN; + req->clipboard = WIN_GetFullHandle( hWnd ); + + if (wine_server_call_err( req )) + { + ERR("Failed to set clipboard.\n"); + } + else + { + bRet = TRUE; + } + } + SERVER_END_REQ; + + return bRet; +} + + +/************************************************************************** + * CLIPBOARD_CloseClipboard + */ +static BOOL CLIPBOARD_CloseClipboard(void) +{ + BOOL bRet = FALSE; + + TRACE(" Changed=%d\n", bCBHasChanged); + + SERVER_START_REQ( set_clipboard_info ) + { + req->flags = SET_CB_CLOSE; + + if (bCBHasChanged) + { + req->flags |= SET_CB_SEQNO; + TRACE("Clipboard data changed\n"); } - lpFormat = lpFormat->NextFormat; + if (wine_server_call_err( req )) + { + ERR("Failed to set clipboard.\n"); + } + else + { + bRet = TRUE; + } } + SERVER_END_REQ; - TRACE("Next available format %d\n", lpFormat->wFormatID); - - return lpFormat->wFormatID; + return bRet; } @@ -742,6 +219,95 @@ static UINT CLIPBOARD_EnumClipboardFormats( UINT wFormat ) * WIN32 Clipboard implementation **************************************************************************/ +/************************************************************************** + * RegisterClipboardFormatA (USER32.@) + */ +UINT WINAPI RegisterClipboardFormatA(LPCSTR FormatName) +{ + UINT wFormatID = 0; + + TRACE("%s\n", debugstr_a(FormatName)); + + if (USER_Driver.pRegisterClipboardFormat) + wFormatID = USER_Driver.pRegisterClipboardFormat(FormatName); + + return wFormatID; +} + + +/************************************************************************** + * RegisterClipboardFormat (USER.145) + */ +UINT16 WINAPI RegisterClipboardFormat16(LPCSTR FormatName) +{ + UINT wFormatID = 0; + + TRACE("%s\n", debugstr_a(FormatName)); + + if (USER_Driver.pRegisterClipboardFormat) + wFormatID = USER_Driver.pRegisterClipboardFormat(FormatName); + + return wFormatID; +} + + +/************************************************************************** + * RegisterClipboardFormatW (USER32.@) + */ +UINT WINAPI RegisterClipboardFormatW(LPCWSTR formatName) +{ + LPSTR aFormat = HEAP_strdupWtoA( GetProcessHeap(), 0, formatName ); + UINT ret = RegisterClipboardFormatA( aFormat ); + HeapFree( GetProcessHeap(), 0, aFormat ); + return ret; +} + + +/************************************************************************** + * GetClipboardFormatName (USER.146) + */ +INT16 WINAPI GetClipboardFormatName16(UINT16 wFormat, LPSTR retStr, INT16 maxlen) +{ + TRACE("%04x,%p,%d\n", wFormat, retStr, maxlen); + + return GetClipboardFormatNameA(wFormat, retStr, maxlen); +} + + +/************************************************************************** + * GetClipboardFormatNameA (USER32.@) + */ +INT WINAPI GetClipboardFormatNameA(UINT wFormat, LPSTR retStr, INT maxlen) +{ + INT len = 0; + + TRACE("%04x,%p,%d\n", wFormat, retStr, maxlen); + + if (USER_Driver.pGetClipboardFormatName) + len = USER_Driver.pGetClipboardFormatName(wFormat, retStr, maxlen); + + return len; +} + + +/************************************************************************** + * GetClipboardFormatNameW (USER32.@) + */ +INT WINAPI GetClipboardFormatNameW(UINT wFormat, LPWSTR retStr, INT maxlen) +{ + INT ret; + LPSTR p = HeapAlloc( GetProcessHeap(), 0, maxlen ); + if(p == NULL) return 0; /* FIXME: is this the correct failure value? */ + + ret = GetClipboardFormatNameA( wFormat, p, maxlen ); + + if (maxlen > 0 && !MultiByteToWideChar( CP_ACP, 0, p, -1, retStr, maxlen )) + retStr[maxlen-1] = 0; + HeapFree( GetProcessHeap(), 0, p ); + return ret; +} + + /************************************************************************** * OpenClipboard (USER32.@) * @@ -753,18 +319,10 @@ BOOL WINAPI OpenClipboard( HWND hWnd ) TRACE("(%p)...\n", hWnd); - if (!ClipLock) - { - ClipLock = GetCurrentThreadId(); + bRet = CLIPBOARD_OpenClipboard(hWnd); - /* Save current user of the clipboard */ - hWndClipWindow = WIN_GetFullHandle( hWnd ); - bCBHasChanged = FALSE; - bRet = TRUE; - } - else bRet = FALSE; + TRACE(" returning %i\n", bRet); - TRACE(" returning %i\n", bRet); return bRet; } @@ -783,16 +341,29 @@ BOOL16 WINAPI CloseClipboard16(void) */ BOOL WINAPI CloseClipboard(void) { - TRACE("()\n"); + BOOL bRet = FALSE; - if (ClipLock == GetCurrentThreadId()) + TRACE("(%d)\n", bCBHasChanged); + + if (CLIPBOARD_CloseClipboard()) { - hWndClipWindow = 0; + if (bCBHasChanged) + { + HWND hWndViewer = GetClipboardViewer(); - if (bCBHasChanged && hWndViewer) SendMessageW( hWndViewer, WM_DRAWCLIPBOARD, 0, 0 ); - ClipLock = 0; + if (USER_Driver.pEndClipboardUpdate) + USER_Driver.pEndClipboardUpdate(); + + if (hWndViewer) + SendMessageW(hWndViewer, WM_DRAWCLIPBOARD, 0, 0); + + bCBHasChanged = FALSE; + } + + bRet = TRUE; } - return TRUE; + + return bRet; } @@ -807,33 +378,50 @@ BOOL16 WINAPI EmptyClipboard16(void) /************************************************************************** * EmptyClipboard (USER32.@) - * Empties and acquires ownership of the clipboard + * Empties and acquires ownership of the clipboard */ BOOL WINAPI EmptyClipboard(void) { + CLIPBOARDINFO cbinfo; + TRACE("()\n"); - if (ClipLock != GetCurrentThreadId()) - { + if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || + ~cbinfo.flags & CB_OPEN) + { WARN("Clipboard not opened by calling task!\n"); + SetLastError(ERROR_CLIPBOARD_NOT_OPEN); return FALSE; } - /* destroy private objects */ + /* Destroy private objects */ + if (cbinfo.hWndOwner) + SendMessageW(cbinfo.hWndOwner, WM_DESTROYCLIPBOARD, 0, 0); - if (hWndClipOwner) SendMessageW( hWndClipOwner, WM_DESTROYCLIPBOARD, 0, 0 ); + /* Tell the driver to acquire the selection. The current owner + * will be signaled to delete it's own cache. */ + if (~cbinfo.flags & CB_OWNER) + { + /* Assign ownership of the clipboard to the current client. We do + * this before acquiring the selection so that when we do acquire the + * selection and the selection loser gets notified, it can check if + * it has lost the Wine clipboard ownership. If it did then it knows + * that a WM_DESTORYCLIPBOARD has already been sent. Otherwise it + * lost the selection to a X app and it should send the + * WM_DESTROYCLIPBOARD itself. */ + CLIPBOARD_SetClipboardOwner(cbinfo.hWndOpen); - /* empty the cache */ - CLIPBOARD_EmptyCache(TRUE); + /* Acquire the selection. This will notify the previous owner + * to clear it's cache. */ + if (USER_Driver.pAcquireClipboard) + USER_Driver.pAcquireClipboard(cbinfo.hWndOpen); + } - /* Assign ownership of the clipboard to the current client */ - hWndClipOwner = hWndClipWindow; - - /* Save the current task */ - ClipOwner = GetCurrentThreadId(); - - /* Tell the driver to acquire the selection */ - USER_Driver.pAcquireClipboard(); + /* Empty the local cache */ + if (USER_Driver.pEmptyClipboard) + USER_Driver.pEmptyClipboard(); + + bCBHasChanged = TRUE; return TRUE; } @@ -841,265 +429,157 @@ BOOL WINAPI EmptyClipboard(void) /************************************************************************** * GetClipboardOwner (USER32.@) - * FIXME: Can't return the owner if the clipbard is owned by an external app + * FIXME: Can't return the owner if the clipboard is owned by an external X-app */ HWND WINAPI GetClipboardOwner(void) { - TRACE("()\n"); - return hWndClipOwner; + HWND hWndOwner = 0; + CLIPBOARDINFO cbinfo; + + if (CLIPBOARD_GetClipboardInfo(&cbinfo)) + hWndOwner = cbinfo.hWndOwner; + + TRACE(" hWndOwner(%p)\n", hWndOwner); + + return hWndOwner; +} + + +/************************************************************************** + * GetOpenClipboardWindow (USER32.@) + */ +HWND WINAPI GetOpenClipboardWindow(void) +{ + HWND hWndOpen = 0; + CLIPBOARDINFO cbinfo; + + if (CLIPBOARD_GetClipboardInfo(&cbinfo)) + hWndOpen = cbinfo.hWndOpen; + + TRACE(" hWndClipWindow(%p)\n", hWndOpen); + + return hWndOpen; +} + + +/************************************************************************** + * SetClipboardViewer (USER32.@) + */ +HWND WINAPI SetClipboardViewer( HWND hWnd ) +{ + HWND hwndPrev = 0; + + SERVER_START_REQ( set_clipboard_info ) + { + req->flags = SET_CB_VIEWER; + req->viewer = WIN_GetFullHandle(hWnd); + + if (wine_server_call_err( req )) + { + ERR("Failed to set clipboard.\n"); + } + else + { + hwndPrev = reply->old_viewer; + } + } + SERVER_END_REQ; + + TRACE("(%p): returning %p\n", hWnd, hwndPrev); + + return hwndPrev; +} + + +/************************************************************************** + * GetClipboardViewer (USER32.@) + */ +HWND WINAPI GetClipboardViewer(void) +{ + HWND hWndViewer = 0; + CLIPBOARDINFO cbinfo; + + if (CLIPBOARD_GetClipboardInfo(&cbinfo)) + hWndViewer = cbinfo.hWndViewer; + + TRACE(" hWndViewer=%p\n", hWndViewer); + + return hWndViewer; +} + + +/************************************************************************** + * ChangeClipboardChain (USER32.@) + */ +BOOL WINAPI ChangeClipboardChain(HWND hWnd, HWND hWndNext) +{ + BOOL bRet = TRUE; + HWND hWndViewer = GetClipboardViewer(); + + if (hWndViewer) + { + if (WIN_GetFullHandle(hWnd) == hWndViewer) + SetClipboardViewer(WIN_GetFullHandle(hWndNext)); + else + bRet = !SendMessageW(hWndViewer, WM_CHANGECBCHAIN, (WPARAM)hWnd, (LPARAM)hWndNext); + } + else + ERR("hWndViewer is lost\n"); + + return bRet; } /************************************************************************** * SetClipboardData (USER.141) */ -HANDLE16 WINAPI SetClipboardData16( UINT16 wFormat, HANDLE16 hData ) +HANDLE16 WINAPI SetClipboardData16(UINT16 wFormat, HANDLE16 hData) { - LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat ); + CLIPBOARDINFO cbinfo; + HANDLE16 hResult = 0; TRACE("(%04X, %04x) !\n", wFormat, 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. - * (to become the owner it must call EmptyClipboard() before - * adding new data). - */ - - if( CLIPBOARD_IsLocked() || !lpFormat || - (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) ) + if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || + (~cbinfo.flags & CB_OPEN) || + (~cbinfo.flags & CB_OWNER)) { - WARN("Invalid hData or clipboard not opened by calling task!\n"); - return 0; + WARN("Clipboard not opened by calling task!\n"); + } + else if (USER_Driver.pSetClipboardData && + USER_Driver.pSetClipboardData(wFormat, hData, 0)) + { + hResult = hData; + bCBHasChanged = TRUE; } - /* Pass on the request to the driver */ - USER_Driver.pSetClipboardData(wFormat); - - if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 ) - { - CLIPBOARD_DeleteRecord(lpFormat, TRUE); - - /* delete existing CF_UNICODETEXT/CF_TEXT/CF_OEMTEXT aliases */ - if(wFormat == CF_UNICODETEXT) - { - CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE); - CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE); - } - else if(wFormat == CF_TEXT) - { - CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE); - CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE); - } - else if(wFormat == CF_OEMTEXT) - { - CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE); - CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE); - } - } - - bCBHasChanged = TRUE; - lpFormat->wDataPresent = 1; - lpFormat->hData16 = hData; /* 0 is legal, see WM_RENDERFORMAT */ - lpFormat->hData32 = 0; - - return lpFormat->hData16; + return hResult; } /************************************************************************** * SetClipboardData (USER32.@) */ -HANDLE WINAPI SetClipboardData( UINT wFormat, HANDLE hData ) +HANDLE WINAPI SetClipboardData(UINT wFormat, HANDLE hData) { - LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat ); + CLIPBOARDINFO cbinfo; + HANDLE hResult = 0; - TRACE("(%08X, %p) !\n", wFormat, hData); + TRACE("(%04X, %p) !\n", wFormat, 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. - * (to become the owner it must call EmptyClipboard() before - * adding new data). - */ - - if( CLIPBOARD_IsLocked() || !lpFormat || - (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) ) + if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || + (~cbinfo.flags & CB_OWNER)) { - WARN("Invalid hData or clipboard not opened by calling task!\n"); - return 0; + WARN("Clipboard not owned by calling task!\n"); + } + else if (USER_Driver.pSetClipboardData && + USER_Driver.pSetClipboardData(wFormat, 0, hData)) + { + hResult = hData; + bCBHasChanged = TRUE; } - /* Tell the driver to acquire the selection */ - USER_Driver.pAcquireClipboard(); - - if ( lpFormat->wDataPresent && - (lpFormat->hData16 || lpFormat->hData32) ) - { - CLIPBOARD_DeleteRecord(lpFormat, TRUE); - - /* delete existing CF_UNICODETEXT/CF_TEXT/CF_OEMTEXT aliases */ - if(wFormat == CF_UNICODETEXT) - { - CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE); - CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE); - } - else if(wFormat == CF_TEXT) - { - CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE); - CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE); - } - else if(wFormat == CF_OEMTEXT) - { - CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE); - CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE); - } - } - - bCBHasChanged = TRUE; - lpFormat->wDataPresent = 1; - lpFormat->hDataSrc32 = hData; /* Save the source handle */ - - /* - * Make a shared duplicate if the memory is not shared - * TODO: What should be done for non-memory objects - */ - 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->hData16 = 0; - - return lpFormat->hData32; /* Should we return lpFormat->hDataSrc32 */ -} - - -/************************************************************************** - * GetClipboardData (USER.142) - */ -HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat ) -{ - LPWINE_CLIPFORMAT lpRender = ClipFormats; - - TRACE("(%04X)\n", wFormat); - - if (CLIPBOARD_IsLocked()) - { - WARN("Clipboard not opened by calling task!\n"); - return 0; - } - - if( wFormat == CF_UNICODETEXT || wFormat == CF_TEXT || wFormat == CF_OEMTEXT ) - { - lpRender = CLIPBOARD_RenderText(wFormat); - if ( !lpRender ) return 0; - } - else - { - lpRender = __lookup_format( ClipFormats, wFormat ); - if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0; - } - - /* Convert between 32 -> 16 bit data, if necessary */ - if( lpRender->hData32 && !lpRender->hData16 - && CLIPBOARD_IsMemoryObject(wFormat) ) - { - int size; - if( lpRender->wFormatID == CF_METAFILEPICT ) - size = sizeof( METAFILEPICT16 ); - else - 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 ); - } - GlobalUnlock16(lpRender->hData16); - GlobalUnlock(lpRender->hData32); - } - } - - TRACE("\treturning %04x (type %i)\n", - lpRender->hData16, lpRender->wFormatID); - return lpRender->hData16; -} - - -/************************************************************************** - * GetClipboardData (USER32.@) - */ -HANDLE WINAPI GetClipboardData( UINT wFormat ) -{ - LPWINE_CLIPFORMAT lpRender = ClipFormats; - - TRACE("(%08X)\n", wFormat); - - if (CLIPBOARD_IsLocked()) - { - WARN("Clipboard not opened by calling task!\n"); - return 0; - } - - if( wFormat == CF_UNICODETEXT || wFormat == CF_TEXT || wFormat == CF_OEMTEXT ) - { - lpRender = CLIPBOARD_RenderText(wFormat); - if ( !lpRender ) return 0; - } - else - { - lpRender = __lookup_format( ClipFormats, wFormat ); - if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0; - } - - /* Convert between 16 -> 32 bit data, if necessary */ - if( lpRender->hData16 && !lpRender->hData32 - && CLIPBOARD_IsMemoryObject(wFormat) ) - { - int size; - if( lpRender->wFormatID == CF_METAFILEPICT ) - size = sizeof( METAFILEPICT ); - else - size = GlobalSize16(lpRender->hData16); - lpRender->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, - size); - if( lpRender->wFormatID == CF_METAFILEPICT ) - { - FIXME("\timplement function CopyMetaFilePict16to32\n"); - FIXME("\tin the appropriate file.\n"); -#ifdef SOMEONE_IMPLEMENTED_ME - CopyMetaFilePict16to32( GlobalLock16(lpRender->hData32), - GlobalLock(lpRender->hData16) ); -#endif - } - else - { - memcpy( GlobalLock(lpRender->hData32), - GlobalLock16(lpRender->hData16), - size ); - } - GlobalUnlock(lpRender->hData32); - GlobalUnlock16(lpRender->hData16); - } - - TRACE("\treturning %p (type %i)\n", lpRender->hData32, lpRender->wFormatID); - return lpRender->hData32; + return hResult; } @@ -1117,310 +597,139 @@ INT16 WINAPI CountClipboardFormats16(void) */ INT WINAPI CountClipboardFormats(void) { - INT FormatCount = 0; - LPWINE_CLIPFORMAT lpFormat = ClipFormats; + INT count = 0; - TRACE("()\n"); + if (USER_Driver.pCountClipboardFormats) + count = USER_Driver.pCountClipboardFormats(); - while(TRUE) - { - if (lpFormat == NULL) break; - - 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 || - ( !USER_Driver.pIsSelectionOwner() - && USER_Driver.pIsClipboardFormatAvailable( lpFormat->wFormatID ) ) ) - { - TRACE("\tdata found for format 0x%04x(%s)\n", - lpFormat->wFormatID, CLIPBOARD_GetFormatName(lpFormat->wFormatID, NULL, 0)); - FormatCount++; - } - } - - lpFormat = lpFormat->NextFormat; - } - - /* these are equivalent, adjust the total */ - FormatCount += (ClipFormats[CF_UNICODETEXT-1].wDataPresent || - ClipFormats[CF_TEXT-1].wDataPresent || - ClipFormats[CF_OEMTEXT-1].wDataPresent) ? 1 : 0; - - TRACE("\ttotal %d\n", FormatCount); - return FormatCount; + TRACE("returning %d\n", count); + return count; } + /************************************************************************** * EnumClipboardFormats (USER.144) */ -UINT16 WINAPI EnumClipboardFormats16( UINT16 wFormat ) +UINT16 WINAPI EnumClipboardFormats16(UINT16 wFormat) { - return EnumClipboardFormats( wFormat ); + return EnumClipboardFormats(wFormat); } /************************************************************************** * EnumClipboardFormats (USER32.@) */ -UINT WINAPI EnumClipboardFormats( UINT wFormat ) +UINT WINAPI EnumClipboardFormats(UINT wFormat) { + UINT wFmt = 0; + CLIPBOARDINFO cbinfo; + TRACE("(%04X)\n", wFormat); - if (CLIPBOARD_IsLocked()) + if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || + (~cbinfo.flags & CB_OPEN)) { - WARN("Clipboard not opened by calling task!\n"); + WARN("Clipboard not opened by calling task.\n"); + SetLastError(ERROR_CLIPBOARD_NOT_OPEN); return 0; } - return CLIPBOARD_EnumClipboardFormats(wFormat); -} + if (USER_Driver.pEnumClipboardFormats) + wFmt = USER_Driver.pEnumClipboardFormats(wFormat); - -/************************************************************************** - * RegisterClipboardFormatA (USER32.@) - */ -UINT WINAPI RegisterClipboardFormatA( LPCSTR FormatName ) -{ - LPWINE_CLIPFORMAT lpNewFormat; - LPWINE_CLIPFORMAT lpFormat = ClipFormats; - - if (FormatName == NULL) return 0; - - TRACE("('%s') !\n", FormatName); - - /* walk format chain to see if it's already registered */ - - while(TRUE) - { - if ( !strcasecmp(lpFormat->Name,FormatName) ) - { - lpFormat->wRefCount++; - return lpFormat->wFormatID; - } - - if ( lpFormat->NextFormat == NULL ) break; - - lpFormat = lpFormat->NextFormat; - } - - /* allocate storage for new format entry */ - - lpNewFormat = (LPWINE_CLIPFORMAT)HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_CLIPFORMAT)); - if(lpNewFormat == NULL) { - WARN("No more memory for a new format!\n"); - return 0; - } - lpFormat->NextFormat = lpNewFormat; - lpNewFormat->wRefCount = 1; - - if (!(lpNewFormat->Name = HeapAlloc(GetProcessHeap(), 0, strlen(FormatName)+1 ))) - { - WARN("No more memory for the new format name!\n"); - HeapFree(GetProcessHeap(), 0, lpNewFormat); - return 0; - } - strcpy( lpNewFormat->Name, FormatName ); - CharLowerA(lpNewFormat->Name); - - lpNewFormat->wDataPresent = 0; - lpNewFormat->hData16 = 0; - lpNewFormat->hDataSrc32 = 0; - lpNewFormat->hData32 = 0; - lpNewFormat->drvData = 0; - lpNewFormat->PrevFormat = lpFormat; - lpNewFormat->NextFormat = NULL; - - /* Pass on the registration request to the driver */ - lpNewFormat->wFormatID = USER_Driver.pRegisterClipboardFormat(lpNewFormat->Name); - - TRACE("Registering format(%d): %s\n", lpNewFormat->wFormatID, FormatName); - return lpNewFormat->wFormatID; -} - - -/************************************************************************** - * RegisterClipboardFormat (USER.145) - */ -UINT16 WINAPI RegisterClipboardFormat16( LPCSTR FormatName ) -{ - return RegisterClipboardFormatA( FormatName ); -} - - -/************************************************************************** - * RegisterClipboardFormatW (USER32.@) - */ -UINT WINAPI RegisterClipboardFormatW( LPCWSTR formatName ) -{ - LPSTR aFormat = HEAP_strdupWtoA( GetProcessHeap(), 0, formatName ); - UINT ret = RegisterClipboardFormatA( aFormat ); - HeapFree( GetProcessHeap(), 0, aFormat ); - return ret; -} - - -/************************************************************************** - * GetClipboardFormatName (USER.146) - */ -INT16 WINAPI GetClipboardFormatName16( UINT16 wFormat, LPSTR retStr, INT16 maxlen ) -{ - return GetClipboardFormatNameA( wFormat, retStr, maxlen ); -} - - -/************************************************************************** - * GetClipboardFormatNameA (USER32.@) - */ -INT WINAPI GetClipboardFormatNameA( UINT wFormat, LPSTR retStr, INT maxlen ) -{ - LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat ); - - TRACE("(%04X, %p, %d) !\n", wFormat, retStr, maxlen); - - if (lpFormat == NULL || lpFormat->Name == NULL) - { - /* Check if another wine process already registered the format */ - if (wFormat && !USER_Driver.pGetClipboardFormatName(wFormat, retStr, maxlen)) - { - RegisterClipboardFormatA(retStr); /* Make a cache entry */ - return strlen(retStr); - } - else - { - TRACE("wFormat=%d not found\n", wFormat); - return 0; - } - } - - TRACE("Name='%s' !\n", lpFormat->Name); - - lstrcpynA( retStr, lpFormat->Name, maxlen ); - return strlen(retStr); -} - - -/************************************************************************** - * GetClipboardFormatNameW (USER32.@) - */ -INT WINAPI GetClipboardFormatNameW( UINT wFormat, LPWSTR retStr, INT maxlen ) -{ - INT ret; - LPSTR p = HeapAlloc( GetProcessHeap(), 0, maxlen ); - if(p == NULL) return 0; /* FIXME: is this the correct failure value? */ - - ret = GetClipboardFormatNameA( wFormat, p, maxlen ); - - if (maxlen > 0 && !MultiByteToWideChar( CP_ACP, 0, p, -1, retStr, maxlen )) - retStr[maxlen-1] = 0; - HeapFree( GetProcessHeap(), 0, p ); - return ret; -} - - -/************************************************************************** - * SetClipboardViewer (USER32.@) - */ -HWND WINAPI SetClipboardViewer( HWND hWnd ) -{ - HWND hwndPrev = hWndViewer; - - TRACE("(%p): returning %p\n", hWnd, hwndPrev); - - hWndViewer = WIN_GetFullHandle( hWnd ); - return hwndPrev; -} - - -/************************************************************************** - * GetClipboardViewer (USER32.@) - */ -HWND WINAPI GetClipboardViewer(void) -{ - TRACE("()\n"); - return hWndViewer; -} - - -/************************************************************************** - * ChangeClipboardChain (USER32.@) - */ -BOOL WINAPI ChangeClipboardChain(HWND hWnd, HWND hWndNext) -{ - BOOL bRet = 0; - - FIXME("(%p, %p): stub?\n", hWnd, hWndNext); - - if( hWndViewer ) - bRet = !SendMessageW( hWndViewer, WM_CHANGECBCHAIN, (WPARAM)hWnd, (LPARAM)hWndNext ); - else - WARN("hWndViewer is lost\n"); - - if( WIN_GetFullHandle(hWnd) == hWndViewer ) hWndViewer = WIN_GetFullHandle( hWndNext ); - - return bRet; + return wFmt; } /************************************************************************** * IsClipboardFormatAvailable (USER.193) */ -BOOL16 WINAPI IsClipboardFormatAvailable16( UINT16 wFormat ) +BOOL16 WINAPI IsClipboardFormatAvailable16(UINT16 wFormat) { - return IsClipboardFormatAvailable( wFormat ); + return IsClipboardFormatAvailable(wFormat); } /************************************************************************** * IsClipboardFormatAvailable (USER32.@) */ -BOOL WINAPI IsClipboardFormatAvailable( UINT wFormat ) +BOOL WINAPI IsClipboardFormatAvailable(UINT wFormat) { - BOOL bRet; + BOOL bret = FALSE; - if (wFormat == 0) /* Reject this case quickly */ - bRet = FALSE; - else - { - UINT iret = CLIPBOARD_EnumClipboardFormats(wFormat - 1); - if ((wFormat == CF_TEXT) || (wFormat == CF_OEMTEXT) || (wFormat == CF_UNICODETEXT)) - bRet = ((iret == CF_TEXT) || (iret == CF_OEMTEXT) || (iret == CF_UNICODETEXT)); - else - bRet = iret == wFormat; - } - TRACE("(%04X)- ret(%d)\n", wFormat, bRet); - return bRet; + if (USER_Driver.pIsClipboardFormatAvailable) + bret = USER_Driver.pIsClipboardFormatAvailable(wFormat); + + TRACE("%04x, returning %d\n", wFormat, bret); + return bret; } /************************************************************************** - * GetOpenClipboardWindow (USER32.@) - * FIXME: This wont work if an external app owns the selection + * GetClipboardData (USER.142) */ -HWND WINAPI GetOpenClipboardWindow(void) +HANDLE16 WINAPI GetClipboardData16(UINT16 wFormat) { - TRACE("()\n"); - return hWndClipWindow; + HANDLE16 hData = 0; + CLIPBOARDINFO cbinfo; + + if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || + (~cbinfo.flags & CB_OPEN)) + { + WARN("Clipboard not opened by calling task.\n"); + SetLastError(ERROR_CLIPBOARD_NOT_OPEN); + return 0; + } + + if (USER_Driver.pGetClipboardData) + USER_Driver.pGetClipboardData(wFormat, &hData, NULL); + + return hData; +} + + +/************************************************************************** + * GetClipboardData (USER32.@) + */ +HANDLE WINAPI GetClipboardData(UINT wFormat) +{ + HANDLE hData = 0; + CLIPBOARDINFO cbinfo; + + TRACE("%04x\n", wFormat); + + if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || + (~cbinfo.flags & CB_OPEN)) + { + WARN("Clipboard not opened by calling task.\n"); + SetLastError(ERROR_CLIPBOARD_NOT_OPEN); + return 0; + } + + if (USER_Driver.pGetClipboardData) + USER_Driver.pGetClipboardData(wFormat, NULL, &hData); + + TRACE("returning %p\n", hData); + return hData; } /************************************************************************** * GetPriorityClipboardFormat (USER32.@) */ -INT WINAPI GetPriorityClipboardFormat( UINT *list, INT nCount ) +INT WINAPI GetPriorityClipboardFormat(UINT *list, INT nCount) { int i; + TRACE("()\n"); - if(CountClipboardFormats() == 0) return 0; + if(CountClipboardFormats() == 0) + return 0; for (i = 0; i < nCount; i++) - if (IsClipboardFormatAvailable( list[i] )) return list[i]; + if (IsClipboardFormatAvailable(list[i])) + return list[i]; + return -1; } @@ -1435,7 +744,14 @@ INT WINAPI GetPriorityClipboardFormat( UINT *list, INT nCount ) */ DWORD WINAPI GetClipboardSequenceNumber(VOID) { - FIXME("Returning 0, see windows/clipboard.c\n"); - /* FIXME: Use serial numbers */ + CLIPBOARDINFO cbinfo; + + if (!CLIPBOARD_GetClipboardInfo(&cbinfo)) + { + ERR("Failed to get clipboard information.\n"); return 0; + } + + TRACE("returning %x\n", cbinfo.seqno); + return cbinfo.seqno; } diff --git a/windows/win.c b/windows/win.c index 8604beb49a7..cb0e871ea05 100644 --- a/windows/win.c +++ b/windows/win.c @@ -1515,6 +1515,9 @@ BOOL WINAPI DestroyWindow( HWND hwnd ) WIN_SendDestroyMsg( hwnd ); if (!IsWindow( hwnd )) return TRUE; + if (GetClipboardOwner() == hwnd) + CLIPBOARD_ReleaseOwner(); + /* Unlink now so we won't bother with the children later on */ WIN_UnlinkWindow( hwnd );