/* * WIN32 clipboard implementation * * Copyright 1994 Martin Ayotte * Copyright 1996 Alex Korobka * Copyright 1999 Noel Borthwick * Copyright 2003 Ulrich Czekalla for CodeWeavers * Copyright 2016 Alexandre Julliard * * 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 * */ #include "config.h" #include "wine/port.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "user_private.h" #include "win.h" #include "wine/list.h" #include "wine/unicode.h" #include "wine/server.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(clipboard); struct cached_format { struct list entry; /* entry in cache list */ UINT format; /* format id */ UINT seqno; /* sequence number when the data was set */ HANDLE handle; /* original data handle */ }; static struct list cached_formats = LIST_INIT( cached_formats ); static struct list formats_to_free = LIST_INIT( formats_to_free ); static CRITICAL_SECTION clipboard_cs; static CRITICAL_SECTION_DEBUG critsect_debug = { 0, 0, &clipboard_cs, { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": clipboard_cs") } }; static CRITICAL_SECTION clipboard_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; /* platform-independent version of METAFILEPICT */ struct metafile_pict { LONG mm; LONG xExt; LONG yExt; BYTE bits[1]; }; /* get a debug string for a format id */ static const char *debugstr_format( UINT id ) { WCHAR buffer[256]; if (GetClipboardFormatNameW( id, buffer, 256 )) return wine_dbg_sprintf( "%04x %s", id, debugstr_w(buffer) ); switch (id) { #define BUILTIN(id) case id: return #id; BUILTIN(CF_TEXT) BUILTIN(CF_BITMAP) BUILTIN(CF_METAFILEPICT) BUILTIN(CF_SYLK) BUILTIN(CF_DIF) BUILTIN(CF_TIFF) BUILTIN(CF_OEMTEXT) BUILTIN(CF_DIB) BUILTIN(CF_PALETTE) BUILTIN(CF_PENDATA) BUILTIN(CF_RIFF) BUILTIN(CF_WAVE) BUILTIN(CF_UNICODETEXT) BUILTIN(CF_ENHMETAFILE) BUILTIN(CF_HDROP) BUILTIN(CF_LOCALE) BUILTIN(CF_DIBV5) BUILTIN(CF_OWNERDISPLAY) BUILTIN(CF_DSPTEXT) BUILTIN(CF_DSPBITMAP) BUILTIN(CF_DSPMETAFILEPICT) BUILTIN(CF_DSPENHMETAFILE) #undef BUILTIN default: return wine_dbg_sprintf( "%04x", id ); } } /* build the data to send to the server in SetClipboardData */ static HANDLE marshal_data( UINT format, HANDLE handle, data_size_t *ret_size ) { SIZE_T size; switch (format) { case CF_BITMAP: case CF_DSPBITMAP: { BITMAP bitmap, *bm; if (!GetObjectW( handle, sizeof(bitmap), &bitmap )) return 0; size = abs( bitmap.bmHeight ) * ((((bitmap.bmWidth * bitmap.bmBitsPixel) + 15) >> 3) & ~1); *ret_size = sizeof(bitmap) + size; if (!(bm = GlobalAlloc( GMEM_FIXED, *ret_size ))) return 0; *bm = bitmap; GetBitmapBits( handle, size, bm + 1 ); return bm; } case CF_PALETTE: { LOGPALETTE *pal; if (!(size = GetPaletteEntries( handle, 0, 0, NULL ))) return 0; *ret_size = offsetof( LOGPALETTE, palPalEntry[size] ); if (!(pal = GlobalAlloc( GMEM_FIXED, *ret_size ))) return 0; pal->palVersion = 0x300; pal->palNumEntries = size; GetPaletteEntries( handle, 0, size, pal->palPalEntry ); return pal; } case CF_ENHMETAFILE: case CF_DSPENHMETAFILE: { BYTE *ret; if (!(size = GetEnhMetaFileBits( handle, 0, NULL ))) return 0; if (!(ret = GlobalAlloc( GMEM_FIXED, size ))) return 0; GetEnhMetaFileBits( handle, size, ret ); *ret_size = size; return ret; } case CF_METAFILEPICT: case CF_DSPMETAFILEPICT: { METAFILEPICT *mf; struct metafile_pict *mfbits; if (!(mf = GlobalLock( handle ))) return 0; if (!(size = GetMetaFileBitsEx( mf->hMF, 0, NULL ))) { GlobalUnlock( handle ); return 0; } *ret_size = offsetof( struct metafile_pict, bits[size] ); if (!(mfbits = GlobalAlloc( GMEM_FIXED, *ret_size ))) { GlobalUnlock( handle ); return 0; } mfbits->mm = mf->mm; mfbits->xExt = mf->xExt; mfbits->yExt = mf->yExt; GetMetaFileBitsEx( mf->hMF, size, mfbits->bits ); GlobalUnlock( handle ); return mfbits; } case CF_UNICODETEXT: { WCHAR *ptr; if (!(size = GlobalSize( handle ))) return 0; if ((data_size_t)size != size) return 0; if (!(ptr = GlobalLock( handle ))) return 0; ptr[(size + 1) / sizeof(WCHAR) - 1] = 0; /* enforce null-termination */ GlobalUnlock( handle ); *ret_size = size; return handle; } case CF_TEXT: case CF_OEMTEXT: { char *ptr; if (!(size = GlobalSize( handle ))) return 0; if ((data_size_t)size != size) return 0; if (!(ptr = GlobalLock( handle ))) return 0; ptr[size - 1] = 0; /* enforce null-termination */ GlobalUnlock( handle ); *ret_size = size; return handle; } default: if (!(size = GlobalSize( handle ))) return 0; if ((data_size_t)size != size) return 0; *ret_size = size; return handle; } } /* rebuild the target handle from the data received in GetClipboardData */ static HANDLE unmarshal_data( UINT format, void *data, data_size_t size ) { HANDLE handle = GlobalReAlloc( data, size, 0 ); /* release unused space */ switch (format) { case CF_BITMAP: { BITMAP *bm = handle; if (size < sizeof(*bm)) break; if (size < bm->bmWidthBytes * abs( bm->bmHeight )) break; if (bm->bmBits) break; /* DIB sections are not supported across processes */ bm->bmBits = bm + 1; return CreateBitmapIndirect( bm ); } case CF_DSPBITMAP: /* not supported across processes */ break; case CF_PALETTE: { LOGPALETTE *pal = handle; if (size < sizeof(*pal)) break; if (size < offsetof( LOGPALETTE, palPalEntry[pal->palNumEntries] )) break; return CreatePalette( pal ); } case CF_ENHMETAFILE: case CF_DSPENHMETAFILE: return SetEnhMetaFileBits( size, handle ); case CF_METAFILEPICT: case CF_DSPMETAFILEPICT: { METAFILEPICT mf; struct metafile_pict *mfbits = handle; if (size <= sizeof(*mfbits)) break; mf.mm = mfbits->mm; mf.xExt = mfbits->xExt; mf.yExt = mfbits->yExt; mf.hMF = SetMetaFileBitsEx( size - offsetof( struct metafile_pict, bits ), mfbits->bits ); *(METAFILEPICT *)handle = mf; return handle; } } return handle; } /* retrieve a data format from the cache */ static struct cached_format *get_cached_format( UINT format ) { struct cached_format *cache; LIST_FOR_EACH_ENTRY( cache, &cached_formats, struct cached_format, entry ) if (cache->format == format) return cache; return NULL; } /* store data in the cache, or reuse the existing one if available */ static HANDLE cache_data( UINT format, HANDLE data, data_size_t size, UINT seqno, struct cached_format *cache ) { if (cache) { if (seqno == cache->seqno) /* we can reuse the cached data */ { GlobalFree( data ); return cache->handle; } /* cache entry is stale, remove it */ list_remove( &cache->entry ); list_add_tail( &formats_to_free, &cache->entry ); } /* allocate new cache entry */ if (!(cache = HeapAlloc( GetProcessHeap(), 0, sizeof(*cache) ))) { GlobalFree( data ); return 0; } cache->format = format; cache->seqno = seqno; cache->handle = unmarshal_data( format, data, size ); list_add_tail( &cached_formats, &cache->entry ); return cache->handle; } /* free a single cached format */ static void free_cached_data( struct cached_format *cache ) { void *ptr; switch (cache->format) { case CF_BITMAP: case CF_DSPBITMAP: case CF_PALETTE: DeleteObject( cache->handle ); break; case CF_ENHMETAFILE: case CF_DSPENHMETAFILE: DeleteEnhMetaFile( cache->handle ); break; case CF_METAFILEPICT: case CF_DSPMETAFILEPICT: if ((ptr = GlobalLock( cache->handle ))) { DeleteMetaFile( ((METAFILEPICT *)ptr)->hMF ); GlobalUnlock( cache->handle ); } GlobalFree( cache->handle ); break; default: GlobalFree( cache->handle ); break; } list_remove( &cache->entry ); HeapFree( GetProcessHeap(), 0, cache ); } /* clear global memory formats; special types are freed on EmptyClipboard */ static void invalidate_memory_formats(void) { struct cached_format *cache, *next; LIST_FOR_EACH_ENTRY_SAFE( cache, next, &cached_formats, struct cached_format, entry ) { switch (cache->format) { case CF_BITMAP: case CF_DSPBITMAP: case CF_PALETTE: case CF_ENHMETAFILE: case CF_DSPENHMETAFILE: case CF_METAFILEPICT: case CF_DSPMETAFILEPICT: continue; default: free_cached_data( cache ); break; } } } /* free all the data in the cache */ static void free_cached_formats(void) { struct list *ptr; list_move_tail( &formats_to_free, &cached_formats ); while ((ptr = list_head( &formats_to_free ))) free_cached_data( LIST_ENTRY( ptr, struct cached_format, entry )); } /* get the clipboard locale stored in the CF_LOCALE format */ static LCID get_clipboard_locale(void) { HANDLE data; LCID lcid = GetUserDefaultLCID(); if ((data = GetClipboardData( CF_LOCALE ))) { LCID *ptr = GlobalLock( data ); if (ptr) { if (GlobalSize( data ) >= sizeof(*ptr)) lcid = *ptr; GlobalUnlock( data ); } } return lcid; } /* get the codepage to use for text conversions in the specified format (CF_TEXT or CF_OEMTEXT) */ static UINT get_format_codepage( LCID lcid, UINT format ) { LCTYPE type = (format == CF_TEXT) ? LOCALE_IDEFAULTANSICODEPAGE : LOCALE_IDEFAULTCODEPAGE; UINT ret; if (!GetLocaleInfoW( lcid, type | LOCALE_RETURN_NUMBER, (LPWSTR)&ret, sizeof(ret)/sizeof(WCHAR) )) ret = (format == CF_TEXT) ? CP_ACP : CP_OEMCP; return ret; } /* render synthesized ANSI text based on the contents of the 'from' format */ static HANDLE render_synthesized_textA( HANDLE data, UINT format, UINT from ) { void *src; WCHAR *srcW = NULL; HANDLE ret = 0; LCID lcid = get_clipboard_locale(); UINT codepage = get_format_codepage( lcid, format ); UINT len, size = GlobalSize( data ); if (!(src = GlobalLock( data ))) return 0; if (from != CF_UNICODETEXT) /* first convert incoming format to Unicode */ { UINT from_codepage = get_format_codepage( lcid, from ); len = MultiByteToWideChar( from_codepage, 0, src, size, NULL, 0 ); if (!(srcW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done; MultiByteToWideChar( from_codepage, 0, src, size, srcW, len ); src = srcW; size = len * sizeof(WCHAR); } if ((len = WideCharToMultiByte( codepage, 0, src, size / sizeof(WCHAR), NULL, 0, NULL, NULL ))) { if ((ret = GlobalAlloc( GMEM_FIXED, len ))) WideCharToMultiByte( codepage, 0, src, size / sizeof(WCHAR), ret, len, NULL, NULL ); } done: HeapFree( GetProcessHeap(), 0, srcW ); GlobalUnlock( data ); return ret; } /* render synthesized Unicode text based on the contents of the 'from' format */ static HANDLE render_synthesized_textW( HANDLE data, UINT from ) { char *src; HANDLE ret = 0; UINT len, size = GlobalSize( data ); UINT codepage = get_format_codepage( get_clipboard_locale(), from ); if (!(src = GlobalLock( data ))) return 0; if ((len = MultiByteToWideChar( codepage, 0, src, size, NULL, 0 ))) { if ((ret = GlobalAlloc( GMEM_FIXED, len * sizeof(WCHAR) ))) MultiByteToWideChar( codepage, 0, src, size, ret, len ); } GlobalUnlock( data ); return ret; } /* render a synthesized bitmap based on the DIB clipboard data */ static HANDLE render_synthesized_bitmap( HANDLE data, UINT from ) { BITMAPINFO *bmi; HANDLE ret = 0; HDC hdc = GetDC( 0 ); if ((bmi = GlobalLock( data ))) { /* FIXME: validate data size */ ret = CreateDIBitmap( hdc, &bmi->bmiHeader, CBM_INIT, (char *)bmi + bitmap_info_size( bmi, DIB_RGB_COLORS ), bmi, DIB_RGB_COLORS ); GlobalUnlock( data ); } ReleaseDC( 0, hdc ); return ret; } /* render a synthesized DIB based on the clipboard data */ static HANDLE render_synthesized_dib( HANDLE data, UINT format, UINT from ) { BITMAPINFO *bmi, *src; DWORD src_size, header_size, bits_size; HANDLE ret = 0; HDC hdc = GetDC( 0 ); if (from == CF_BITMAP) { BITMAPV5HEADER header; memset( &header, 0, sizeof(header) ); header.bV5Size = (format == CF_DIBV5) ? sizeof(BITMAPV5HEADER) : sizeof(BITMAPINFOHEADER); if (!GetDIBits( hdc, data, 0, 0, NULL, (BITMAPINFO *)&header, DIB_RGB_COLORS )) goto done; header_size = bitmap_info_size( (BITMAPINFO *)&header, DIB_RGB_COLORS ); if (!(ret = GlobalAlloc( GMEM_FIXED, header_size + header.bV5SizeImage ))) goto done; bmi = (BITMAPINFO *)ret; memset( bmi, 0, header_size ); memcpy( bmi, &header, header.bV5Size ); GetDIBits( hdc, data, 0, abs(header.bV5Height), (char *)bmi + header_size, bmi, DIB_RGB_COLORS ); } else { SIZE_T size = GlobalSize( data ); if (size < sizeof(*bmi)) goto done; if (!(src = GlobalLock( data ))) goto done; src_size = bitmap_info_size( src, DIB_RGB_COLORS ); if (size <= src_size) { GlobalUnlock( data ); goto done; } bits_size = size - src_size; header_size = (format == CF_DIBV5) ? sizeof(BITMAPV5HEADER) : offsetof( BITMAPINFO, bmiColors[src->bmiHeader.biCompression == BI_BITFIELDS ? 3 : 0] ); if ((ret = GlobalAlloc( GMEM_FIXED, header_size + bits_size ))) { bmi = (BITMAPINFO *)ret; memset( bmi, 0, header_size ); memcpy( bmi, src, min( header_size, src_size )); bmi->bmiHeader.biSize = header_size; /* FIXME: convert colors according to DIBv5 color profile */ memcpy( (char *)bmi + header_size, (char *)src + src_size, bits_size ); } GlobalUnlock( data ); } done: ReleaseDC( 0, hdc ); return ret; } /* render a synthesized metafile based on the enhmetafile clipboard data */ static HANDLE render_synthesized_metafile( HANDLE data ) { HANDLE ret = 0; UINT size; void *bits; METAFILEPICT *pict; ENHMETAHEADER header; HDC hdc = GetDC( 0 ); size = GetWinMetaFileBits( data, 0, NULL, MM_ISOTROPIC, hdc ); if ((bits = HeapAlloc( GetProcessHeap(), 0, size ))) { if (GetEnhMetaFileHeader( data, sizeof(header), &header ) && GetWinMetaFileBits( data, size, bits, MM_ISOTROPIC, hdc )) { if ((ret = GlobalAlloc( GMEM_FIXED, sizeof(*pict) ))) { pict = (METAFILEPICT *)ret; pict->mm = MM_ISOTROPIC; pict->xExt = header.rclFrame.right - header.rclFrame.left; pict->yExt = header.rclFrame.bottom - header.rclFrame.top; pict->hMF = SetMetaFileBitsEx( size, bits ); } } HeapFree( GetProcessHeap(), 0, bits ); } ReleaseDC( 0, hdc ); return ret; } /* render a synthesized enhmetafile based on the metafile clipboard data */ static HANDLE render_synthesized_enhmetafile( HANDLE data ) { METAFILEPICT *pict; HANDLE ret = 0; UINT size; void *bits; if (!(pict = GlobalLock( data ))) return 0; size = GetMetaFileBitsEx( pict->hMF, 0, NULL ); if ((bits = HeapAlloc( GetProcessHeap(), 0, size ))) { GetMetaFileBitsEx( pict->hMF, size, bits ); ret = SetWinMetaFileBits( size, bits, NULL, pict ); HeapFree( GetProcessHeap(), 0, bits ); } GlobalUnlock( data ); return ret; } /* render a synthesized format */ static HANDLE render_synthesized_format( UINT format, UINT from ) { HANDLE data = GetClipboardData( from ); if (!data) return 0; TRACE( "rendering %s from %s\n", debugstr_format( format ), debugstr_format( from )); switch (format) { case CF_TEXT: case CF_OEMTEXT: data = render_synthesized_textA( data, format, from ); break; case CF_UNICODETEXT: data = render_synthesized_textW( data, from ); break; case CF_BITMAP: data = render_synthesized_bitmap( data, from ); break; case CF_DIB: case CF_DIBV5: data = render_synthesized_dib( data, format, from ); break; case CF_METAFILEPICT: data = render_synthesized_metafile( data ); break; case CF_ENHMETAFILE: data = render_synthesized_enhmetafile( data ); break; default: assert( 0 ); } if (data) { TRACE( "adding %s %p\n", debugstr_format( format ), data ); SetClipboardData( format, data ); } return data; } /************************************************************************** * CLIPBOARD_ReleaseOwner */ void CLIPBOARD_ReleaseOwner( HWND hwnd ) { HWND viewer = 0, owner = 0; SendMessageW( hwnd, WM_RENDERALLFORMATS, 0, 0 ); SERVER_START_REQ( release_clipboard ) { req->owner = wine_server_user_handle( hwnd ); if (!wine_server_call( req )) { viewer = wine_server_ptr_handle( reply->viewer ); owner = wine_server_ptr_handle( reply->owner ); } } SERVER_END_REQ; if (viewer) SendNotifyMessageW( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 ); } /************************************************************************** * RegisterClipboardFormatW (USER32.@) */ UINT WINAPI RegisterClipboardFormatW( LPCWSTR name ) { return GlobalAddAtomW( name ); } /************************************************************************** * RegisterClipboardFormatA (USER32.@) */ UINT WINAPI RegisterClipboardFormatA( LPCSTR name ) { return GlobalAddAtomA( name ); } /************************************************************************** * GetClipboardFormatNameW (USER32.@) */ INT WINAPI GetClipboardFormatNameW( UINT format, LPWSTR buffer, INT maxlen ) { if (format < MAXINTATOM || format > 0xffff) return 0; return GlobalGetAtomNameW( format, buffer, maxlen ); } /************************************************************************** * GetClipboardFormatNameA (USER32.@) */ INT WINAPI GetClipboardFormatNameA( UINT format, LPSTR buffer, INT maxlen ) { if (format < MAXINTATOM || format > 0xffff) return 0; return GlobalGetAtomNameA( format, buffer, maxlen ); } /************************************************************************** * OpenClipboard (USER32.@) */ BOOL WINAPI OpenClipboard( HWND hwnd ) { BOOL ret; HWND owner; TRACE( "%p\n", hwnd ); USER_Driver->pUpdateClipboard(); EnterCriticalSection( &clipboard_cs ); SERVER_START_REQ( open_clipboard ) { req->window = wine_server_user_handle( hwnd ); ret = !wine_server_call_err( req ); owner = wine_server_ptr_handle( reply->owner ); } SERVER_END_REQ; if (ret && !WIN_IsCurrentProcess( owner )) invalidate_memory_formats(); LeaveCriticalSection( &clipboard_cs ); return ret; } /************************************************************************** * CloseClipboard (USER32.@) */ BOOL WINAPI CloseClipboard(void) { HWND viewer = 0, owner = 0; BOOL ret; TRACE( "\n" ); SERVER_START_REQ( close_clipboard ) { if ((ret = !wine_server_call_err( req ))) { viewer = wine_server_ptr_handle( reply->viewer ); owner = wine_server_ptr_handle( reply->owner ); } } SERVER_END_REQ; if (viewer) SendNotifyMessageW( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 ); return ret; } /************************************************************************** * EmptyClipboard (USER32.@) * Empties and acquires ownership of the clipboard */ BOOL WINAPI EmptyClipboard(void) { BOOL ret; HWND owner = GetClipboardOwner(); TRACE( "owner %p\n", owner ); if (owner) SendMessageTimeoutW( owner, WM_DESTROYCLIPBOARD, 0, 0, SMTO_ABORTIFHUNG, 5000, NULL ); EnterCriticalSection( &clipboard_cs ); SERVER_START_REQ( empty_clipboard ) { ret = !wine_server_call_err( req ); } SERVER_END_REQ; if (ret) free_cached_formats(); LeaveCriticalSection( &clipboard_cs ); return ret; } /************************************************************************** * GetClipboardOwner (USER32.@) */ HWND WINAPI GetClipboardOwner(void) { HWND hWndOwner = 0; SERVER_START_REQ( get_clipboard_info ) { if (!wine_server_call_err( req )) hWndOwner = wine_server_ptr_handle( reply->owner ); } SERVER_END_REQ; TRACE( "returning %p\n", hWndOwner ); return hWndOwner; } /************************************************************************** * GetOpenClipboardWindow (USER32.@) */ HWND WINAPI GetOpenClipboardWindow(void) { HWND hWndOpen = 0; SERVER_START_REQ( get_clipboard_info ) { if (!wine_server_call_err( req )) hWndOpen = wine_server_ptr_handle( reply->window ); } SERVER_END_REQ; TRACE( "returning %p\n", hWndOpen ); return hWndOpen; } /************************************************************************** * SetClipboardViewer (USER32.@) */ HWND WINAPI SetClipboardViewer( HWND hwnd ) { HWND prev = 0, owner = 0; SERVER_START_REQ( set_clipboard_viewer ) { req->viewer = wine_server_user_handle( hwnd ); if (!wine_server_call_err( req )) { prev = wine_server_ptr_handle( reply->old_viewer ); owner = wine_server_ptr_handle( reply->owner ); } } SERVER_END_REQ; if (hwnd) SendNotifyMessageW( hwnd, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 ); TRACE( "%p returning %p\n", hwnd, prev ); return prev; } /************************************************************************** * GetClipboardViewer (USER32.@) */ HWND WINAPI GetClipboardViewer(void) { HWND hWndViewer = 0; SERVER_START_REQ( get_clipboard_info ) { if (!wine_server_call_err( req )) hWndViewer = wine_server_ptr_handle( reply->viewer ); } SERVER_END_REQ; TRACE( "returning %p\n", hWndViewer ); return hWndViewer; } /************************************************************************** * ChangeClipboardChain (USER32.@) */ BOOL WINAPI ChangeClipboardChain( HWND hwnd, HWND next ) { NTSTATUS status; HWND viewer; if (!hwnd) return FALSE; SERVER_START_REQ( set_clipboard_viewer ) { req->viewer = wine_server_user_handle( next ); req->previous = wine_server_user_handle( hwnd ); status = wine_server_call( req ); viewer = wine_server_ptr_handle( reply->old_viewer ); } SERVER_END_REQ; if (status == STATUS_PENDING) return !SendMessageW( viewer, WM_CHANGECBCHAIN, (WPARAM)hwnd, (LPARAM)next ); if (status) SetLastError( RtlNtStatusToDosError( status )); return !status; } /************************************************************************** * SetClipboardData (USER32.@) */ HANDLE WINAPI SetClipboardData( UINT format, HANDLE data ) { struct cached_format *cache = NULL; void *ptr = NULL; data_size_t size = 0; HANDLE handle = data, retval = 0; NTSTATUS status = STATUS_SUCCESS; TRACE( "%s %p\n", debugstr_format( format ), data ); if (data) { if (!(handle = marshal_data( format, data, &size ))) return 0; if (!(ptr = GlobalLock( handle ))) goto done; if (!(cache = HeapAlloc( GetProcessHeap(), 0, sizeof(*cache) ))) goto done; cache->format = format; cache->handle = data; } EnterCriticalSection( &clipboard_cs ); SERVER_START_REQ( set_clipboard_data ) { req->format = format; req->lcid = GetUserDefaultLCID(); wine_server_add_data( req, ptr, size ); if (!(status = wine_server_call( req ))) { if (cache) cache->seqno = reply->seqno; } } SERVER_END_REQ; if (!status) { /* free the previous entry if any */ struct cached_format *prev; if ((prev = get_cached_format( format ))) free_cached_data( prev ); if (cache) list_add_tail( &cached_formats, &cache->entry ); retval = data; } else HeapFree( GetProcessHeap(), 0, cache ); LeaveCriticalSection( &clipboard_cs ); done: if (ptr) GlobalUnlock( handle ); if (handle != data) GlobalFree( handle ); if (status) SetLastError( RtlNtStatusToDosError( status )); return retval; } /************************************************************************** * CountClipboardFormats (USER32.@) */ INT WINAPI CountClipboardFormats(void) { INT count = 0; USER_Driver->pUpdateClipboard(); SERVER_START_REQ( get_clipboard_formats ) { wine_server_call( req ); count = reply->count; } SERVER_END_REQ; TRACE("returning %d\n", count); return count; } /************************************************************************** * EnumClipboardFormats (USER32.@) */ UINT WINAPI EnumClipboardFormats( UINT format ) { UINT ret = 0; SERVER_START_REQ( enum_clipboard_formats ) { req->previous = format; if (!wine_server_call_err( req )) { ret = reply->format; SetLastError( ERROR_SUCCESS ); } } SERVER_END_REQ; TRACE( "%s -> %s\n", debugstr_format( format ), debugstr_format( ret )); return ret; } /************************************************************************** * IsClipboardFormatAvailable (USER32.@) */ BOOL WINAPI IsClipboardFormatAvailable( UINT format ) { BOOL ret = FALSE; if (!format) return FALSE; USER_Driver->pUpdateClipboard(); SERVER_START_REQ( get_clipboard_formats ) { req->format = format; if (!wine_server_call_err( req )) ret = (reply->count > 0); } SERVER_END_REQ; TRACE( "%s -> %u\n", debugstr_format( format ), ret ); return ret; } /************************************************************************** * GetUpdatedClipboardFormats (USER32.@) */ BOOL WINAPI GetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size ) { BOOL ret; if (!out_size) { SetLastError( ERROR_NOACCESS ); return FALSE; } USER_Driver->pUpdateClipboard(); SERVER_START_REQ( get_clipboard_formats ) { if (formats) wine_server_set_reply( req, formats, size * sizeof(*formats) ); ret = !wine_server_call_err( req ); *out_size = reply->count; } SERVER_END_REQ; TRACE( "%p %u returning %u formats, ret %u\n", formats, size, *out_size, ret ); if (!ret && !formats && *out_size) SetLastError( ERROR_NOACCESS ); return ret; } /************************************************************************** * GetClipboardData (USER32.@) */ HANDLE WINAPI GetClipboardData( UINT format ) { struct cached_format *cache; NTSTATUS status; UINT from, data_seqno; HWND owner; HANDLE data; UINT size = 1024; BOOL render = TRUE; for (;;) { if (!(data = GlobalAlloc( GMEM_FIXED, size ))) return 0; EnterCriticalSection( &clipboard_cs ); cache = get_cached_format( format ); SERVER_START_REQ( get_clipboard_data ) { req->format = format; req->render = render; if (cache) { req->cached = 1; req->seqno = cache->seqno; } wine_server_set_reply( req, data, size ); status = wine_server_call( req ); from = reply->from; size = reply->total; data_seqno = reply->seqno; owner = wine_server_ptr_handle( reply->owner ); } SERVER_END_REQ; if (!status && size) { data = cache_data( format, data, size, data_seqno, cache ); LeaveCriticalSection( &clipboard_cs ); TRACE( "%s returning %p\n", debugstr_format( format ), data ); return data; } LeaveCriticalSection( &clipboard_cs ); GlobalFree( data ); if (status == STATUS_BUFFER_OVERFLOW) continue; /* retry with the new size */ if (status) { SetLastError( RtlNtStatusToDosError( status )); TRACE( "%s error %08x\n", debugstr_format( format ), status ); return 0; } if (render) /* try rendering it */ { render = FALSE; if (from) { render_synthesized_format( format, from ); continue; } else if (owner) { TRACE( "%s sending WM_RENDERFORMAT to %p\n", debugstr_format( format ), owner ); SendMessageW( owner, WM_RENDERFORMAT, format, 0 ); continue; } } TRACE( "%s returning 0\n", debugstr_format( format )); return 0; } } /************************************************************************** * GetPriorityClipboardFormat (USER32.@) */ INT WINAPI GetPriorityClipboardFormat(UINT *list, INT nCount) { int i; TRACE( "%p %u\n", list, nCount ); if(CountClipboardFormats() == 0) return 0; for (i = 0; i < nCount; i++) if (IsClipboardFormatAvailable(list[i])) return list[i]; return -1; } /************************************************************************** * GetClipboardSequenceNumber (USER32.@) */ DWORD WINAPI GetClipboardSequenceNumber(VOID) { DWORD seqno = 0; SERVER_START_REQ( get_clipboard_info ) { if (!wine_server_call_err( req )) seqno = reply->seqno; } SERVER_END_REQ; TRACE( "returning %u\n", seqno ); return seqno; } /************************************************************************** * AddClipboardFormatListener (USER32.@) */ BOOL WINAPI AddClipboardFormatListener(HWND hwnd) { BOOL ret; SERVER_START_REQ( add_clipboard_listener ) { req->window = wine_server_user_handle( hwnd ); ret = !wine_server_call_err( req ); } SERVER_END_REQ; return ret; } /************************************************************************** * RemoveClipboardFormatListener (USER32.@) */ BOOL WINAPI RemoveClipboardFormatListener(HWND hwnd) { BOOL ret; SERVER_START_REQ( remove_clipboard_listener ) { req->window = wine_server_user_handle( hwnd ); ret = !wine_server_call_err( req ); } SERVER_END_REQ; return ret; }