| /* | |
| * WIN32 clipboard implementation | |
| * | |
| * 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 | |
| * 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 | |
| * | |
| * NOTES: | |
| * This file contains the implementation for the WIN32 Clipboard API | |
| * and Wine's internal clipboard cache. | |
| * The actual contents of the clipboard are held in the clipboard cache. | |
| * The internal implementation talks to a "clipboard driver" to fill or | |
| * expose the cache to the native device. (Currently only the X11 and | |
| * TTY clipboard driver are available) | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdarg.h> | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <fcntl.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #include <string.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winerror.h" | |
| #include "wine/winuser16.h" | |
| #include "wine/winbase16.h" | |
| #include "user_private.h" | |
| #include "win.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| #include "wine/server.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(clipboard); | |
| #define CF_REGFORMATBASE 0xC000 | |
| typedef struct | |
| { | |
| HWND hWndOpen; | |
| HWND hWndOwner; | |
| HWND hWndViewer; | |
| UINT seqno; | |
| UINT flags; | |
| } CLIPBOARDINFO, *LPCLIPBOARDINFO; | |
| /* | |
| * Indicates if data has changed since open. | |
| */ | |
| static BOOL bCBHasChanged = FALSE; | |
| /************************************************************************** | |
| * CLIPBOARD_SetClipboardOwner | |
| * | |
| * Set the global wineserver clipboard owner. The current process will | |
| * be the owner and <hWnd> will get the render notifications. | |
| */ | |
| BOOL CLIPBOARD_SetClipboardOwner(HWND hWnd) | |
| { | |
| BOOL bRet = FALSE; | |
| TRACE(" hWnd(%p)\n", hWnd); | |
| SERVER_START_REQ( set_clipboard_info ) | |
| { | |
| req->flags = SET_CB_OWNER; | |
| req->owner = WIN_GetFullHandle( hWnd ); | |
| if (wine_server_call_err( req )) | |
| { | |
| ERR("Failed to set clipboard owner to %p\n", hWnd); | |
| } | |
| else | |
| { | |
| bRet = TRUE; | |
| } | |
| } | |
| SERVER_END_REQ; | |
| return bRet; | |
| } | |
| /************************************************************************** | |
| * CLIPBOARD_GetClipboardInfo | |
| */ | |
| static BOOL 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 info\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; | |
| } | |
| /************************************************************************** | |
| * 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( req )) | |
| 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"); | |
| } | |
| if (wine_server_call_err( req )) | |
| { | |
| ERR("Failed to set clipboard.\n"); | |
| } | |
| else | |
| { | |
| bRet = TRUE; | |
| } | |
| } | |
| SERVER_END_REQ; | |
| return bRet; | |
| } | |
| /************************************************************************** | |
| * WIN32 Clipboard implementation | |
| **************************************************************************/ | |
| /************************************************************************** | |
| * RegisterClipboardFormatW (USER32.@) | |
| */ | |
| UINT WINAPI RegisterClipboardFormatW(LPCWSTR FormatName) | |
| { | |
| UINT wFormatID = 0; | |
| TRACE("%s\n", debugstr_w(FormatName)); | |
| if (USER_Driver.pRegisterClipboardFormat) | |
| wFormatID = USER_Driver.pRegisterClipboardFormat(FormatName); | |
| return wFormatID; | |
| } | |
| /************************************************************************** | |
| * RegisterClipboardFormatA (USER32.@) | |
| */ | |
| UINT WINAPI RegisterClipboardFormatA(LPCSTR formatName) | |
| { | |
| int len; | |
| LPWSTR wFormat; | |
| UINT ret; | |
| len = MultiByteToWideChar(CP_ACP, 0, formatName, -1, NULL, 0); | |
| wFormat = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); | |
| MultiByteToWideChar(CP_ACP, 0, formatName, -1, wFormat, len); | |
| ret = RegisterClipboardFormatW(wFormat); | |
| HeapFree(GetProcessHeap(), 0, wFormat); | |
| return ret; | |
| } | |
| /************************************************************************** | |
| * GetClipboardFormatNameW (USER32.@) | |
| */ | |
| INT WINAPI GetClipboardFormatNameW(UINT wFormat, LPWSTR 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; | |
| } | |
| /************************************************************************** | |
| * GetClipboardFormatNameA (USER32.@) | |
| */ | |
| INT WINAPI GetClipboardFormatNameA(UINT wFormat, LPSTR retStr, INT maxlen) | |
| { | |
| INT ret; | |
| LPWSTR p = HeapAlloc( GetProcessHeap(), 0, maxlen*sizeof(WCHAR) ); | |
| if(p == NULL) return 0; /* FIXME: is this the correct failure value? */ | |
| ret = GetClipboardFormatNameW( wFormat, p, maxlen ); | |
| if (maxlen > 0 && !WideCharToMultiByte( CP_ACP, 0, p, -1, retStr, maxlen, 0, 0)) | |
| retStr[maxlen-1] = 0; | |
| HeapFree( GetProcessHeap(), 0, p ); | |
| return ret; | |
| } | |
| /************************************************************************** | |
| * OpenClipboard (USER32.@) | |
| * | |
| * Note: Netscape uses NULL hWnd to open the clipboard. | |
| */ | |
| BOOL WINAPI OpenClipboard( HWND hWnd ) | |
| { | |
| BOOL bRet; | |
| TRACE("(%p)...\n", hWnd); | |
| bRet = CLIPBOARD_OpenClipboard(hWnd); | |
| TRACE(" returning %i\n", bRet); | |
| return bRet; | |
| } | |
| /************************************************************************** | |
| * CloseClipboard (USER32.@) | |
| */ | |
| BOOL WINAPI CloseClipboard(void) | |
| { | |
| BOOL bRet = FALSE; | |
| TRACE("(%d)\n", bCBHasChanged); | |
| if (CLIPBOARD_CloseClipboard()) | |
| { | |
| if (bCBHasChanged) | |
| { | |
| HWND hWndViewer = GetClipboardViewer(); | |
| if (USER_Driver.pEndClipboardUpdate) | |
| USER_Driver.pEndClipboardUpdate(); | |
| if (hWndViewer) | |
| SendMessageW(hWndViewer, WM_DRAWCLIPBOARD, 0, 0); | |
| bCBHasChanged = FALSE; | |
| } | |
| bRet = TRUE; | |
| } | |
| return bRet; | |
| } | |
| /************************************************************************** | |
| * EmptyClipboard (USER32.@) | |
| * Empties and acquires ownership of the clipboard | |
| */ | |
| BOOL WINAPI EmptyClipboard(void) | |
| { | |
| CLIPBOARDINFO cbinfo; | |
| TRACE("()\n"); | |
| 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 */ | |
| if (cbinfo.hWndOwner) | |
| SendMessageW(cbinfo.hWndOwner, WM_DESTROYCLIPBOARD, 0, 0); | |
| /* Tell the driver to acquire the selection. The current owner | |
| * will be signaled to delete it's own cache. */ | |
| /* 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); | |
| /* Acquire the selection. This will notify the previous owner | |
| * to clear it's cache. */ | |
| if (USER_Driver.pAcquireClipboard) | |
| USER_Driver.pAcquireClipboard(cbinfo.hWndOpen); | |
| /* Empty the local cache */ | |
| if (USER_Driver.pEmptyClipboard) | |
| USER_Driver.pEmptyClipboard(FALSE); | |
| bCBHasChanged = TRUE; | |
| return TRUE; | |
| } | |
| /************************************************************************** | |
| * GetClipboardOwner (USER32.@) | |
| * FIXME: Can't return the owner if the clipboard is owned by an external X-app | |
| */ | |
| HWND WINAPI GetClipboardOwner(void) | |
| { | |
| HWND hWndOwner = 0; | |
| SERVER_START_REQ( set_clipboard_info ) | |
| { | |
| req->flags = 0; | |
| if (!wine_server_call_err( req )) hWndOwner = reply->old_owner; | |
| } | |
| SERVER_END_REQ; | |
| TRACE(" hWndOwner(%p)\n", hWndOwner); | |
| return hWndOwner; | |
| } | |
| /************************************************************************** | |
| * GetOpenClipboardWindow (USER32.@) | |
| */ | |
| HWND WINAPI GetOpenClipboardWindow(void) | |
| { | |
| HWND hWndOpen = 0; | |
| SERVER_START_REQ( set_clipboard_info ) | |
| { | |
| req->flags = 0; | |
| if (!wine_server_call_err( req )) hWndOpen = reply->old_clipboard; | |
| } | |
| SERVER_END_REQ; | |
| 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; | |
| SERVER_START_REQ( set_clipboard_info ) | |
| { | |
| req->flags = 0; | |
| if (!wine_server_call_err( req )) hWndViewer = reply->old_viewer; | |
| } | |
| SERVER_END_REQ; | |
| 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) | |
| { | |
| CLIPBOARDINFO cbinfo; | |
| HANDLE16 hResult = 0; | |
| TRACE("(%04X, %04x) !\n", wFormat, hData); | |
| /* If it's not owned, data can only be set if the format doesn't exists | |
| and its rendering is not delayed */ | |
| if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || | |
| (!(cbinfo.flags & CB_OWNER) && !hData)) | |
| { | |
| WARN("Clipboard not owned by calling task. Operation failed.\n"); | |
| return 0; | |
| } | |
| if (USER_Driver.pSetClipboardData && | |
| USER_Driver.pSetClipboardData(wFormat, hData, 0, cbinfo.flags & CB_OWNER)) | |
| { | |
| hResult = hData; | |
| bCBHasChanged = TRUE; | |
| } | |
| return hResult; | |
| } | |
| /************************************************************************** | |
| * SetClipboardData (USER32.@) | |
| */ | |
| HANDLE WINAPI SetClipboardData(UINT wFormat, HANDLE hData) | |
| { | |
| CLIPBOARDINFO cbinfo; | |
| HANDLE hResult = 0; | |
| TRACE("(%04X, %p) !\n", wFormat, hData); | |
| /* If it's not owned, data can only be set if the format isn't | |
| available and its rendering is not delayed */ | |
| if (!CLIPBOARD_GetClipboardInfo(&cbinfo) || | |
| (!(cbinfo.flags & CB_OWNER) && !hData)) | |
| { | |
| WARN("Clipboard not owned by calling task. Operation failed.\n"); | |
| return 0; | |
| } | |
| if (USER_Driver.pSetClipboardData && | |
| USER_Driver.pSetClipboardData(wFormat, 0, hData, cbinfo.flags & CB_OWNER)) | |
| { | |
| hResult = hData; | |
| bCBHasChanged = TRUE; | |
| } | |
| return hResult; | |
| } | |
| /************************************************************************** | |
| * CountClipboardFormats (USER32.@) | |
| */ | |
| INT WINAPI CountClipboardFormats(void) | |
| { | |
| INT count = 0; | |
| if (USER_Driver.pCountClipboardFormats) | |
| count = USER_Driver.pCountClipboardFormats(); | |
| TRACE("returning %d\n", count); | |
| return count; | |
| } | |
| /************************************************************************** | |
| * EnumClipboardFormats (USER32.@) | |
| */ | |
| UINT WINAPI EnumClipboardFormats(UINT wFormat) | |
| { | |
| UINT wFmt = 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.pEnumClipboardFormats) | |
| wFmt = USER_Driver.pEnumClipboardFormats(wFormat); | |
| return wFmt; | |
| } | |
| /************************************************************************** | |
| * IsClipboardFormatAvailable (USER32.@) | |
| */ | |
| BOOL WINAPI IsClipboardFormatAvailable(UINT wFormat) | |
| { | |
| BOOL bret = FALSE; | |
| if (USER_Driver.pIsClipboardFormatAvailable) | |
| bret = USER_Driver.pIsClipboardFormatAvailable(wFormat); | |
| TRACE("%04x, returning %d\n", wFormat, bret); | |
| return bret; | |
| } | |
| /************************************************************************** | |
| * GetClipboardData (USER.142) | |
| */ | |
| HANDLE16 WINAPI GetClipboardData16(UINT16 wFormat) | |
| { | |
| 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 i; | |
| TRACE("()\n"); | |
| if(CountClipboardFormats() == 0) | |
| return 0; | |
| for (i = 0; i < nCount; i++) | |
| if (IsClipboardFormatAvailable(list[i])) | |
| return list[i]; | |
| return -1; | |
| } | |
| /************************************************************************** | |
| * GetClipboardSequenceNumber (USER32.@) | |
| * Supported on Win2k/Win98 | |
| * MSDN: Windows clipboard code keeps a serial number for the clipboard | |
| * for each window station. The number is incremented whenever the | |
| * contents change or are emptied. | |
| * If you do not have WINSTA_ACCESSCLIPBOARD then the function returns 0 | |
| */ | |
| DWORD WINAPI GetClipboardSequenceNumber(VOID) | |
| { | |
| DWORD seqno = 0; | |
| SERVER_START_REQ( set_clipboard_info ) | |
| { | |
| req->flags = 0; | |
| if (!wine_server_call_err( req )) seqno = reply->seqno; | |
| } | |
| SERVER_END_REQ; | |
| TRACE("returning %lx\n", seqno); | |
| return seqno; | |
| } |