/* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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/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. */ static BOOL CLIPBOARD_SetClipboardOwner(HWND hWnd) { BOOL bRet; TRACE(" hWnd(%p)\n", hWnd); SERVER_START_REQ( set_clipboard_info ) { req->flags = SET_CB_OWNER; req->owner = wine_server_user_handle( hWnd ); bRet = !wine_server_call_err( req ); } SERVER_END_REQ; return bRet; } /************************************************************************** * CLIPBOARD_GetClipboardInfo */ static BOOL CLIPBOARD_GetClipboardInfo(LPCLIPBOARDINFO cbInfo) { BOOL bRet; SERVER_START_REQ( set_clipboard_info ) { req->flags = 0; if (((bRet = !wine_server_call_err( req )))) { cbInfo->hWndOpen = wine_server_ptr_handle( reply->old_clipboard ); cbInfo->hWndOwner = wine_server_ptr_handle( reply->old_owner ); cbInfo->hWndViewer = wine_server_ptr_handle( reply->old_viewer ); cbInfo->seqno = reply->seqno; cbInfo->flags = reply->flags; } } 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; SERVER_START_REQ( set_clipboard_info ) { req->flags = SET_CB_OPEN; req->clipboard = wine_server_user_handle( hWnd ); bRet = !wine_server_call( req ); } SERVER_END_REQ; return bRet; } /************************************************************************** * CLIPBOARD_CloseClipboard */ static BOOL CLIPBOARD_CloseClipboard(void) { BOOL bRet; TRACE(" Changed=%d\n", bCBHasChanged); SERVER_START_REQ( set_clipboard_info ) { req->flags = SET_CB_CLOSE; if (bCBHasChanged) req->flags |= SET_CB_SEQNO; bRet = !wine_server_call_err( req ); } SERVER_END_REQ; return bRet; } /************************************************************************** * CLIPBOARD_SetClipboardViewer */ static HWND CLIPBOARD_SetClipboardViewer( HWND hWnd ) { HWND hwndPrev = 0; SERVER_START_REQ( set_clipboard_info ) { req->flags = SET_CB_VIEWER; req->viewer = wine_server_user_handle( hWnd ); if (!wine_server_call_err( req )) hwndPrev = wine_server_ptr_handle( reply->old_viewer ); } SERVER_END_REQ; return hwndPrev; } /************************************************************************** * WIN32 Clipboard implementation **************************************************************************/ /************************************************************************** * RegisterClipboardFormatW (USER32.@) */ UINT WINAPI RegisterClipboardFormatW(LPCWSTR FormatName) { return USER_Driver->pRegisterClipboardFormat(FormatName); } /************************************************************************** * 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) { return USER_Driver->pGetClipboardFormatName(wFormat, retStr, maxlen); } /************************************************************************** * 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 (ret && 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(); USER_Driver->pEndClipboardUpdate(); if (hWndViewer) SendMessageW(hWndViewer, WM_DRAWCLIPBOARD, (WPARAM) GetClipboardOwner(), 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. */ USER_Driver->pAcquireClipboard(cbinfo.hWndOpen); /* Empty the local cache */ 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 = wine_server_ptr_handle( 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 = wine_server_ptr_handle( reply->old_clipboard ); } SERVER_END_REQ; TRACE(" hWndClipWindow(%p)\n", hWndOpen); return hWndOpen; } /************************************************************************** * SetClipboardViewer (USER32.@) */ HWND WINAPI SetClipboardViewer( HWND hWnd ) { HWND hwndPrev = CLIPBOARD_SetClipboardViewer(hWnd); if (hWnd) SendMessageW(hWnd, WM_DRAWCLIPBOARD, (WPARAM) GetClipboardOwner(), 0); 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 = wine_server_ptr_handle( 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) CLIPBOARD_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(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(wFormat, 0, hData, cbinfo.flags & CB_OWNER)) { hResult = hData; bCBHasChanged = TRUE; } return hResult; } /************************************************************************** * CountClipboardFormats (USER32.@) */ INT WINAPI CountClipboardFormats(void) { INT count = USER_Driver->pCountClipboardFormats(); TRACE("returning %d\n", count); return count; } /************************************************************************** * EnumClipboardFormats (USER32.@) */ UINT WINAPI EnumClipboardFormats(UINT wFormat) { 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; } return USER_Driver->pEnumClipboardFormats(wFormat); } /************************************************************************** * IsClipboardFormatAvailable (USER32.@) */ BOOL WINAPI IsClipboardFormatAvailable(UINT wFormat) { BOOL 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(wFormat, &hData, NULL)) hData = 0; 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(wFormat, NULL, &hData)) hData = 0; 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 %x\n", seqno); return seqno; }