diff --git a/dlls/user32/clipboard.c b/dlls/user32/clipboard.c index 33875c56632..c4209899515 100644 --- a/dlls/user32/clipboard.c +++ b/dlls/user32/clipboard.c @@ -36,6 +36,8 @@ #endif #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "wingdi.h" @@ -162,6 +164,46 @@ static HANDLE marshal_data( UINT format, HANDLE handle, data_size_t *ret_size ) } } +/* 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 = handle; + if (size <= sizeof(*mf)) break; + mf->hMF = SetMetaFileBitsEx( size - sizeof(*mf), (BYTE *)(mf + 1) ); + return handle; + } + } + return handle; +} + /* formats that can be synthesized are: CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT, CF_BITMAP, CF_DIB, CF_DIBV5, CF_ENHMETAFILE, CF_METAFILEPICT */ @@ -501,23 +543,6 @@ static HANDLE render_synthesized_format( UINT format, UINT from ) return data; } -/************************************************************************** - * get_clipboard_flags - */ -static UINT get_clipboard_flags(void) -{ - UINT ret = 0; - - SERVER_START_REQ( set_clipboard_info ) - { - if (!wine_server_call_err( req )) ret = reply->flags; - } - SERVER_END_REQ; - - return ret; -} - - /************************************************************************** * CLIPBOARD_ReleaseOwner */ @@ -542,10 +567,6 @@ void CLIPBOARD_ReleaseOwner( HWND hwnd ) } -/************************************************************************** - * WIN32 Clipboard implementation - **************************************************************************/ - /************************************************************************** * RegisterClipboardFormatW (USER32.@) */ @@ -643,11 +664,8 @@ BOOL WINAPI CloseClipboard(void) if (!ret) return FALSE; - if (bCBHasChanged) - { - USER_Driver->pEndClipboardUpdate(); - bCBHasChanged = FALSE; - } + bCBHasChanged = FALSE; + if (viewer) SendNotifyMessageW( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 ); return TRUE; } @@ -674,7 +692,6 @@ BOOL WINAPI EmptyClipboard(void) if (ret) { - USER_Driver->pEmptyClipboard(); bCBHasChanged = TRUE; memset( synthesized_formats, 0, sizeof(synthesized_formats) ); } @@ -817,7 +834,7 @@ HANDLE WINAPI SetClipboardData( UINT format, HANDLE data ) } SERVER_END_REQ; - if (ret && USER_Driver->pSetClipboardData( format, data, TRUE )) + if (ret) { bCBHasChanged = TRUE; if (format < CF_MAX) synthesized_formats[format] = 0; @@ -931,23 +948,57 @@ BOOL WINAPI GetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size */ HANDLE WINAPI GetClipboardData( UINT format ) { - HANDLE data = 0; + NTSTATUS status; + HWND owner; + HANDLE data; + UINT size = 1024; + BOOL render = TRUE; - TRACE( "%s\n", debugstr_format( format )); + if (format < CF_MAX && synthesized_formats[format]) + return render_synthesized_format( format, synthesized_formats[format] ); - if (!(get_clipboard_flags() & CB_OPEN)) + for (;;) { - WARN("Clipboard not opened by calling task.\n"); - SetLastError(ERROR_CLIPBOARD_NOT_OPEN); + if (!(data = GlobalAlloc( GMEM_FIXED, size ))) return 0; + + SERVER_START_REQ( get_clipboard_data ) + { + req->format = format; + wine_server_set_reply( req, data, size ); + status = wine_server_call( req ); + size = reply->total; + owner = wine_server_ptr_handle( reply->owner ); + } + SERVER_END_REQ; + + if (!status && size) + { + data = unmarshal_data( format, data, size ); + TRACE( "%s returning %p\n", debugstr_format( format ), data ); + return data; + } + 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 (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; } - if (format < CF_MAX && synthesized_formats[format]) - data = render_synthesized_format( format, synthesized_formats[format] ); - else - data = USER_Driver->pGetClipboardData( format ); - - TRACE( "returning %p\n", data ); - return data; } diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index a5f97c62f97..ef98575a0a1 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -1762,16 +1762,16 @@ static void test_handles( HWND hwnd ) todo_wine ok( is_freed( hmoveable ), "expected freed mem %p\n", hmoveable ); data = GetClipboardData( CF_TEXT ); - todo_wine ok( is_fixed( data ), "expected fixed mem %p\n", data ); + ok( is_fixed( data ), "expected fixed mem %p\n", data ); data = GetClipboardData( format_id ); - todo_wine ok( is_fixed( data ), "expected fixed mem %p\n", data ); + ok( is_fixed( data ), "expected fixed mem %p\n", data ); data = GetClipboardData( CF_GDIOBJFIRST + 3 ); - todo_wine ok( is_fixed( data ), "expected fixed mem %p\n", data ); + ok( is_fixed( data ), "expected fixed mem %p\n", data ); data = GetClipboardData( CF_PRIVATEFIRST + 7 ); - todo_wine ok( is_fixed( data ), "expected fixed mem %p\n", data ); + ok( is_fixed( data ), "expected fixed mem %p\n", data ); data = GetClipboardData( format_id2 ); ok( is_fixed( data ), "expected fixed mem %p\n", data ); @@ -1782,7 +1782,7 @@ static void test_handles( HWND hwnd ) ok( GlobalSize( data ) == 17, "wrong size %lu\n", GlobalSize( data )); data = GetClipboardData( 0xdeadbabe ); - todo_wine ok( is_fixed( data ), "expected fixed mem %p\n", data ); + ok( is_fixed( data ), "expected fixed mem %p\n", data ); ok( GlobalSize( data ) == 23, "wrong size %lu\n", GlobalSize( data )); data = GetClipboardData( 0xdeadfade ); @@ -1937,69 +1937,69 @@ static void test_handles_process( const char *str ) h = GetClipboardData( CF_TEXT ); todo_wine_if( !h ) ok( is_fixed( h ), "expected fixed mem %p\n", h ); ptr = GlobalLock( h ); - if (ptr) todo_wine ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); + ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); GlobalUnlock( h ); h = GetClipboardData( format_id ); - todo_wine ok( is_fixed( h ), "expected fixed mem %p\n", h ); + ok( is_fixed( h ), "expected fixed mem %p\n", h ); ptr = GlobalLock( h ); if (ptr) ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); GlobalUnlock( h ); h = GetClipboardData( CF_GDIOBJFIRST + 3 ); todo_wine_if( !h ) ok( is_fixed( h ), "expected fixed mem %p\n", h ); ptr = GlobalLock( h ); - if (ptr) todo_wine ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); + ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); GlobalUnlock( h ); trace( "gdiobj %p\n", h ); h = GetClipboardData( CF_PRIVATEFIRST + 7 ); todo_wine_if( !h ) ok( is_fixed( h ), "expected fixed mem %p\n", h ); ptr = GlobalLock( h ); - if (ptr) todo_wine ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); + ok( !strcmp( str, ptr ), "wrong data '%.5s'\n", ptr ); GlobalUnlock( h ); trace( "private %p\n", h ); h = GetClipboardData( CF_BITMAP ); - todo_wine ok( GetObjectType( h ) == OBJ_BITMAP, "expected bitmap %p\n", h ); - todo_wine ok( GetObjectW( h, sizeof(bm), &bm ) == sizeof(bm), "GetObject %p failed\n", h ); - todo_wine ok( bm.bmWidth == 13 && bm.bmHeight == 17, "wrong bitmap %ux%u\n", bm.bmWidth, bm.bmHeight ); + ok( GetObjectType( h ) == OBJ_BITMAP, "expected bitmap %p\n", h ); + ok( GetObjectW( h, sizeof(bm), &bm ) == sizeof(bm), "GetObject %p failed\n", h ); + ok( bm.bmWidth == 13 && bm.bmHeight == 17, "wrong bitmap %ux%u\n", bm.bmWidth, bm.bmHeight ); trace( "bitmap %p\n", h ); h = GetClipboardData( CF_DSPBITMAP ); ok( !GetObjectType( h ), "expected invalid object %p\n", h ); trace( "bitmap2 %p\n", h ); h = GetClipboardData( CF_PALETTE ); - todo_wine ok( GetObjectType( h ) == OBJ_PAL, "expected palette %p\n", h ); - todo_wine ok( GetPaletteEntries( h, 0, 1, &entry ) == 1, "GetPaletteEntries %p failed\n", h ); - todo_wine ok( entry.peRed == 0x12 && entry.peGreen == 0x34 && entry.peBlue == 0x56, + ok( GetObjectType( h ) == OBJ_PAL, "expected palette %p\n", h ); + ok( GetPaletteEntries( h, 0, 1, &entry ) == 1, "GetPaletteEntries %p failed\n", h ); + ok( entry.peRed == 0x12 && entry.peGreen == 0x34 && entry.peBlue == 0x56, "wrong color %02x,%02x,%02x\n", entry.peRed, entry.peGreen, entry.peBlue ); trace( "palette %p\n", h ); h = GetClipboardData( CF_METAFILEPICT ); - todo_wine ok( is_fixed( h ), "expected fixed mem %p\n", h ); + ok( is_fixed( h ), "expected fixed mem %p\n", h ); if (h) ok( GetObjectType( ((METAFILEPICT *)h)->hMF ) == OBJ_METAFILE, "wrong object %p\n", ((METAFILEPICT *)h)->hMF ); trace( "metafile %p\n", h ); h = GetClipboardData( CF_DSPMETAFILEPICT ); - todo_wine ok( is_fixed( h ), "expected fixed mem %p\n", h ); + ok( is_fixed( h ), "expected fixed mem %p\n", h ); if (h) ok( GetObjectType( ((METAFILEPICT *)h)->hMF ) == OBJ_METAFILE, "wrong object %p\n", ((METAFILEPICT *)h)->hMF ); trace( "metafile2 %p\n", h ); h = GetClipboardData( CF_ENHMETAFILE ); - todo_wine ok( GetObjectType( h ) == OBJ_ENHMETAFILE, "expected enhmetafile %p\n", h ); - todo_wine ok( GetEnhMetaFileBits( h, sizeof(buffer), buffer ) > sizeof(ENHMETAHEADER), + ok( GetObjectType( h ) == OBJ_ENHMETAFILE, "expected enhmetafile %p\n", h ); + ok( GetEnhMetaFileBits( h, sizeof(buffer), buffer ) > sizeof(ENHMETAHEADER), "GetEnhMetaFileBits failed on %p\n", h ); - todo_wine ok( ((ENHMETAHEADER *)buffer)->nRecords == 3, + ok( ((ENHMETAHEADER *)buffer)->nRecords == 3, "wrong records %u\n", ((ENHMETAHEADER *)buffer)->nRecords ); trace( "enhmetafile %p\n", h ); h = GetClipboardData( CF_DSPENHMETAFILE ); - todo_wine ok( GetObjectType( h ) == OBJ_ENHMETAFILE, "expected enhmetafile %p\n", h ); - todo_wine ok( GetEnhMetaFileBits( h, sizeof(buffer), buffer ) > sizeof(ENHMETAHEADER), + ok( GetObjectType( h ) == OBJ_ENHMETAFILE, "expected enhmetafile %p\n", h ); + ok( GetEnhMetaFileBits( h, sizeof(buffer), buffer ) > sizeof(ENHMETAHEADER), "GetEnhMetaFileBits failed on %p\n", h ); - todo_wine ok( ((ENHMETAHEADER *)buffer)->nRecords == 3, + ok( ((ENHMETAHEADER *)buffer)->nRecords == 3, "wrong records %u\n", ((ENHMETAHEADER *)buffer)->nRecords ); trace( "enhmetafile2 %p\n", h ); h = GetClipboardData( CF_DIB ); - todo_wine ok( is_fixed( h ), "expected fixed mem %p\n", h ); + ok( is_fixed( h ), "expected fixed mem %p\n", h ); h = GetClipboardData( CF_DIBV5 ); - todo_wine ok( is_fixed( h ), "expected fixed mem %p\n", h ); + ok( is_fixed( h ), "expected fixed mem %p\n", h ); r = CloseClipboard(); ok( r, "gle %d\n", GetLastError() ); } @@ -2109,15 +2109,15 @@ static void test_data_handles(void) ok( is_moveable( text ), "expected moveable mem %p\n", text ); h = GetClipboardData( CF_TEXT ); - todo_wine ok( is_fixed( h ), "expected fixed mem %p\n", h ); + ok( is_fixed( h ), "expected fixed mem %p\n", h ); ok( is_moveable( text ), "expected moveable mem %p\n", text ); ptr = GlobalLock( h ); - if (ptr) todo_wine ok( !strcmp( ptr, "foobar" ), "wrong data '%.8s'\n", ptr ); + ok( !strcmp( ptr, "foobar" ), "wrong data '%.8s'\n", ptr ); GlobalUnlock( h ); r = EmptyClipboard(); ok( r, "gle %d\n", GetLastError() ); - todo_wine ok( is_fixed( h ), "expected free mem %p\n", h ); + ok( is_fixed( h ), "expected free mem %p\n", h ); ok( is_freed( text ) || broken( is_moveable(text) ), /* w2003, w2008 */ "expected free mem %p\n", text ); r = CloseClipboard(); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 44565759d2a..d96bdb6d9c9 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -4540,6 +4540,21 @@ struct set_clipboard_data_reply +struct get_clipboard_data_request +{ + struct request_header __header; + unsigned int format; +}; +struct get_clipboard_data_reply +{ + struct reply_header __header; + user_handle_t owner; + data_size_t total; + /* VARARG(data,bytes); */ +}; + + + struct get_clipboard_formats_request { struct request_header __header; @@ -5772,6 +5787,7 @@ enum request REQ_set_clipboard_info, REQ_empty_clipboard, REQ_set_clipboard_data, + REQ_get_clipboard_data, REQ_get_clipboard_formats, REQ_enum_clipboard_formats, REQ_release_clipboard, @@ -6062,6 +6078,7 @@ union generic_request struct set_clipboard_info_request set_clipboard_info_request; struct empty_clipboard_request empty_clipboard_request; struct set_clipboard_data_request set_clipboard_data_request; + struct get_clipboard_data_request get_clipboard_data_request; struct get_clipboard_formats_request get_clipboard_formats_request; struct enum_clipboard_formats_request enum_clipboard_formats_request; struct release_clipboard_request release_clipboard_request; @@ -6350,6 +6367,7 @@ union generic_reply struct set_clipboard_info_reply set_clipboard_info_reply; struct empty_clipboard_reply empty_clipboard_reply; struct set_clipboard_data_reply set_clipboard_data_reply; + struct get_clipboard_data_reply get_clipboard_data_reply; struct get_clipboard_formats_reply get_clipboard_formats_reply; struct enum_clipboard_formats_reply enum_clipboard_formats_reply; struct release_clipboard_reply release_clipboard_reply; @@ -6416,6 +6434,6 @@ union generic_reply struct terminate_job_reply terminate_job_reply; }; -#define SERVER_PROTOCOL_VERSION 519 +#define SERVER_PROTOCOL_VERSION 520 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/clipboard.c b/server/clipboard.c index ab1fee1b0ed..50975cece37 100644 --- a/server/clipboard.c +++ b/server/clipboard.c @@ -411,6 +411,31 @@ DECL_HANDLER(set_clipboard_data) } +/* fetch a data format from the clipboard */ +DECL_HANDLER(get_clipboard_data) +{ + struct clip_format *format; + struct clipboard *clipboard = get_process_clipboard(); + + if (!clipboard) return; + + if (clipboard->open_thread != current) + { + set_win32_error( ERROR_CLIPBOARD_NOT_OPEN ); + return; + } + if (!(format = get_format( clipboard, req->format ))) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return; + } + reply->total = format->size; + if (!format->data) reply->owner = clipboard->owner_win; + if (format->size <= get_reply_max_size()) set_reply_data( format->data, format->size ); + else set_error( STATUS_BUFFER_OVERFLOW ); +} + + /* retrieve a list of available formats */ DECL_HANDLER(get_clipboard_formats) { diff --git a/server/protocol.def b/server/protocol.def index ddb24b29cf7..922e829bc97 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3206,6 +3206,16 @@ enum caret_state @END +/* Fetch a data format from the clipboard */ +@REQ(get_clipboard_data) + unsigned int format; /* clipboard format of the data */ +@REPLY + user_handle_t owner; /* clipboard owner for delayed-rendered formats */ + data_size_t total; /* total data size */ + VARARG(data,bytes); /* data contents */ +@END + + /* Retrieve a list of available formats */ @REQ(get_clipboard_formats) unsigned int format; /* specific format to query, return all if 0 */ diff --git a/server/request.h b/server/request.h index 4b1b240c60a..1800df96d1e 100644 --- a/server/request.h +++ b/server/request.h @@ -331,6 +331,7 @@ DECL_HANDLER(close_clipboard); DECL_HANDLER(set_clipboard_info); DECL_HANDLER(empty_clipboard); DECL_HANDLER(set_clipboard_data); +DECL_HANDLER(get_clipboard_data); DECL_HANDLER(get_clipboard_formats); DECL_HANDLER(enum_clipboard_formats); DECL_HANDLER(release_clipboard); @@ -620,6 +621,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_clipboard_info, (req_handler)req_empty_clipboard, (req_handler)req_set_clipboard_data, + (req_handler)req_get_clipboard_data, (req_handler)req_get_clipboard_formats, (req_handler)req_enum_clipboard_formats, (req_handler)req_release_clipboard, @@ -2050,6 +2052,11 @@ C_ASSERT( sizeof(struct set_clipboard_info_reply) == 32 ); C_ASSERT( sizeof(struct empty_clipboard_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_clipboard_data_request, format) == 12 ); C_ASSERT( sizeof(struct set_clipboard_data_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_clipboard_data_request, format) == 12 ); +C_ASSERT( sizeof(struct get_clipboard_data_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_clipboard_data_reply, owner) == 8 ); +C_ASSERT( FIELD_OFFSET(struct get_clipboard_data_reply, total) == 12 ); +C_ASSERT( sizeof(struct get_clipboard_data_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_clipboard_formats_request, format) == 12 ); C_ASSERT( sizeof(struct get_clipboard_formats_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_clipboard_formats_reply, count) == 8 ); diff --git a/server/trace.c b/server/trace.c index ef677604fc9..2ecff74a192 100644 --- a/server/trace.c +++ b/server/trace.c @@ -3779,6 +3779,18 @@ static void dump_set_clipboard_data_request( const struct set_clipboard_data_req dump_varargs_bytes( ", data=", cur_size ); } +static void dump_get_clipboard_data_request( const struct get_clipboard_data_request *req ) +{ + fprintf( stderr, " format=%08x", req->format ); +} + +static void dump_get_clipboard_data_reply( const struct get_clipboard_data_reply *req ) +{ + fprintf( stderr, " owner=%08x", req->owner ); + fprintf( stderr, ", total=%u", req->total ); + dump_varargs_bytes( ", data=", cur_size ); +} + static void dump_get_clipboard_formats_request( const struct get_clipboard_formats_request *req ) { fprintf( stderr, " format=%08x", req->format ); @@ -4673,6 +4685,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_clipboard_info_request, (dump_func)dump_empty_clipboard_request, (dump_func)dump_set_clipboard_data_request, + (dump_func)dump_get_clipboard_data_request, (dump_func)dump_get_clipboard_formats_request, (dump_func)dump_enum_clipboard_formats_request, (dump_func)dump_release_clipboard_request, @@ -4959,6 +4972,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_clipboard_info_reply, NULL, NULL, + (dump_func)dump_get_clipboard_data_reply, (dump_func)dump_get_clipboard_formats_reply, (dump_func)dump_enum_clipboard_formats_reply, (dump_func)dump_release_clipboard_reply, @@ -5245,6 +5259,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_clipboard_info", "empty_clipboard", "set_clipboard_data", + "get_clipboard_data", "get_clipboard_formats", "enum_clipboard_formats", "release_clipboard",