| /* | |
| * 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 <assert.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 "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; | |
| } |