user32: Store clipboard data on the server side.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2016-09-26 11:48:32 +09:00
parent ac005d460d
commit 79f90e4e41
7 changed files with 255 additions and 44 deletions

View File

@ -93,6 +93,76 @@ static const char *debugstr_format( UINT 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, *mfbits;
if (!(mf = GlobalLock( handle ))) return 0;
if (!(size = GetMetaFileBitsEx( mf->hMF, 0, NULL )))
{
GlobalUnlock( handle );
return 0;
}
*ret_size = sizeof(*mf) + size;
if (!(mfbits = GlobalAlloc( GMEM_FIXED, *ret_size )))
{
GlobalUnlock( handle );
return 0;
}
*mfbits = *mf;
GetMetaFileBitsEx( mf->hMF, size, mfbits + 1 );
GlobalUnlock( handle );
return mfbits;
}
default:
if (!(size = GlobalSize( handle ))) return 0;
if ((data_size_t)size != size) return 0;
*ret_size = size;
return handle;
}
}
/* formats that can be synthesized are: CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT,
CF_BITMAP, CF_DIB, CF_DIBV5, CF_ENHMETAFILE, CF_METAFILEPICT */
@ -563,7 +633,6 @@ BOOL WINAPI CloseClipboard(void)
SERVER_START_REQ( close_clipboard )
{
req->changed = bCBHasChanged;
if ((ret = !wine_server_call_err( req )))
{
viewer = wine_server_ptr_handle( reply->viewer );
@ -727,32 +796,38 @@ BOOL WINAPI ChangeClipboardChain( HWND hwnd, HWND next )
*/
HANDLE WINAPI SetClipboardData( UINT format, HANDLE data )
{
HANDLE hResult = 0;
UINT flags;
void *ptr = NULL;
data_size_t size = 0;
HANDLE handle = data, retval = 0;
BOOL ret;
TRACE( "%s %p\n", debugstr_format( format ), data );
if (!format)
if (data)
{
SetLastError( ERROR_CLIPBOARD_NOT_OPEN );
return 0;
if (!(handle = marshal_data( format, data, &size ))) return 0;
if (!(ptr = GlobalLock( handle ))) goto done;
}
flags = get_clipboard_flags();
if (!(flags & CB_OPEN_ANY))
SERVER_START_REQ( set_clipboard_data )
{
SetLastError( ERROR_CLIPBOARD_NOT_OPEN );
return 0;
req->format = format;
wine_server_add_data( req, ptr, size );
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
if (USER_Driver->pSetClipboardData( format, data, flags & CB_OWNER))
if (ret && USER_Driver->pSetClipboardData( format, data, TRUE ))
{
hResult = data;
bCBHasChanged = TRUE;
if (format < CF_MAX) synthesized_formats[format] = 0;
retval = data;
}
return hResult;
done:
if (ptr) GlobalUnlock( ptr );
if (handle != data) GlobalFree( handle );
return retval;
}

View File

@ -207,7 +207,7 @@ static LRESULT CALLBACK winproc_wrapper( HWND hwnd, UINT msg, WPARAM wp, LPARAM
{
case WM_DESTROY:
ok( wm_renderallformats, "didn't receive WM_RENDERALLFORMATS before WM_DESTROY\n" );
todo_wine ok( wm_drawclipboard, "didn't receive WM_DRAWCLIPBOARD before WM_DESTROY\n" );
ok( wm_drawclipboard, "didn't receive WM_DRAWCLIPBOARD before WM_DESTROY\n" );
break;
case WM_DRAWCLIPBOARD:
ok( msg_flags == ISMEX_NOSEND, "WM_DRAWCLIPBOARD wrong flags %x\n", msg_flags );
@ -1092,7 +1092,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1114,7 +1114,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1136,7 +1136,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1165,7 +1165,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 2, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 2, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1222,7 +1222,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1245,7 +1245,7 @@ static DWORD WINAPI clipboard_thread(void *param)
{
/* no synthesized format, so CloseClipboard doesn't change the sequence */
seq = pGetClipboardSequenceNumber();
todo_wine ok( seq == old_seq, "sequence changed\n" );
ok( seq == old_seq, "sequence changed\n" );
old_seq = seq;
}
if (!cross_thread)
@ -1347,7 +1347,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( seq == old_seq, "sequence changed\n" );
ok( seq == old_seq, "sequence changed\n" );
old_seq = seq;
}
if (!cross_thread)
@ -1395,7 +1395,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1420,7 +1420,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( seq == old_seq, "sequence changed\n" );
ok( seq == old_seq, "sequence changed\n" );
old_seq = seq;
}
if (!cross_thread)
@ -1442,7 +1442,6 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine_if (!cross_thread)
ok( (int)(seq - old_seq) == 2, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
@ -1469,7 +1468,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
ok( (int)(seq - old_seq) == 1, "sequence diff %d\n", seq - old_seq );
old_seq = seq;
}
if (!cross_thread)
@ -1494,7 +1493,7 @@ static DWORD WINAPI clipboard_thread(void *param)
if (pGetClipboardSequenceNumber)
{
seq = pGetClipboardSequenceNumber();
todo_wine ok( seq == old_seq, "sequence changed\n" );
ok( seq == old_seq, "sequence changed\n" );
old_seq = seq;
}
if (!cross_thread)
@ -1670,7 +1669,7 @@ static void test_handles( HWND hwnd )
ok( h == htext5, "got %p\n", h );
ok( is_moveable( h ), "expected moveable mem %p\n", h );
h = SetClipboardData( format_id2, empty_moveable );
todo_wine ok( !h, "got %p\n", h );
ok( !h, "got %p\n", h );
GlobalFree( empty_moveable );
if (0) /* crashes on vista64 */

View File

@ -4477,7 +4477,7 @@ struct open_clipboard_reply
struct close_clipboard_request
{
struct request_header __header;
int changed;
char __pad_12[4];
};
struct close_clipboard_reply
{
@ -4527,6 +4527,19 @@ struct empty_clipboard_reply
struct set_clipboard_data_request
{
struct request_header __header;
unsigned int format;
/* VARARG(data,bytes); */
};
struct set_clipboard_data_reply
{
struct reply_header __header;
};
struct release_clipboard_request
{
struct request_header __header;
@ -5729,6 +5742,7 @@ enum request
REQ_close_clipboard,
REQ_set_clipboard_info,
REQ_empty_clipboard,
REQ_set_clipboard_data,
REQ_release_clipboard,
REQ_get_clipboard_info,
REQ_set_clipboard_viewer,
@ -6016,6 +6030,7 @@ union generic_request
struct close_clipboard_request close_clipboard_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 release_clipboard_request release_clipboard_request;
struct get_clipboard_info_request get_clipboard_info_request;
struct set_clipboard_viewer_request set_clipboard_viewer_request;
@ -6301,6 +6316,7 @@ union generic_reply
struct close_clipboard_reply close_clipboard_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 release_clipboard_reply release_clipboard_reply;
struct get_clipboard_info_reply get_clipboard_info_reply;
struct set_clipboard_viewer_reply set_clipboard_viewer_reply;
@ -6365,6 +6381,6 @@ union generic_reply
struct terminate_job_reply terminate_job_reply;
};
#define SERVER_PROTOCOL_VERSION 517
#define SERVER_PROTOCOL_VERSION 518
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */

View File

@ -1,7 +1,8 @@
/*
* Server-side clipboard management
*
* Copyright (C) 2002 Ulrich Czekalla
* Copyright 2002 Ulrich Czekalla
* 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
@ -36,6 +37,14 @@
#include "winuser.h"
#include "winternl.h"
struct clip_format
{
struct list entry; /* entry in format list */
unsigned int id; /* format id */
data_size_t size; /* size of the data block */
void *data; /* data contents, or NULL for delay-rendered */
};
struct clipboard
{
struct object obj; /* object header */
@ -46,6 +55,8 @@ struct clipboard
user_handle_t viewer; /* first window in clipboard viewer list */
unsigned int seqno; /* clipboard change sequence number */
unsigned int open_seqno; /* sequence number at open time */
struct list formats; /* list of data formats */
unsigned int format_count; /* count of data formats */
unsigned int listen_size; /* size of listeners array */
unsigned int listen_count; /* count of listeners */
user_handle_t *listeners; /* array of listener windows */
@ -77,6 +88,45 @@ static const struct object_ops clipboard_ops =
};
/* find a data format in the clipboard */
static struct clip_format *get_format( struct clipboard *clipboard, unsigned int id )
{
struct clip_format *format;
LIST_FOR_EACH_ENTRY( format, &clipboard->formats, struct clip_format, entry )
if (format->id == id) return format;
return NULL;
}
/* add a data format to the clipboard */
static struct clip_format *add_format( struct clipboard *clipboard, unsigned int id )
{
struct clip_format *format;
if (!(format = mem_alloc( sizeof(*format )))) return NULL;
format->id = id;
format->size = 0;
format->data = NULL;
list_add_tail( &clipboard->formats, &format->entry );
clipboard->format_count++;
return format;
}
/* free all clipboard formats */
static void free_clipboard_formats( struct clipboard *clipboard )
{
struct clip_format *format, *next;
LIST_FOR_EACH_ENTRY_SAFE( format, next, &clipboard->formats, struct clip_format, entry )
{
list_remove( &format->entry );
free( format->data );
free( format );
}
clipboard->format_count = 0;
}
/* dump a clipboard object */
static void clipboard_dump( struct object *obj, int verbose )
{
@ -92,6 +142,7 @@ static void clipboard_destroy( struct object *obj )
struct clipboard *clipboard = (struct clipboard *)obj;
free( clipboard->listeners );
free_clipboard_formats( clipboard );
}
/* retrieve the clipboard info for the current process, allocating it if needed */
@ -112,9 +163,11 @@ static struct clipboard *get_process_clipboard(void)
clipboard->owner_win = 0;
clipboard->viewer = 0;
clipboard->seqno = 0;
clipboard->format_count = 0;
clipboard->listen_size = 0;
clipboard->listen_count = 0;
clipboard->listeners = NULL;
list_init( &clipboard->formats );
winstation->clipboard = clipboard;
}
}
@ -164,28 +217,47 @@ static int remove_listener( struct clipboard *clipboard, user_handle_t window )
return 0;
}
/* close the clipboard, and return the viewer window that should be notified if any */
static user_handle_t close_clipboard( struct clipboard *clipboard )
/* notify all listeners, and return the viewer window that should be notified if any */
static user_handle_t notify_listeners( struct clipboard *clipboard )
{
unsigned int i;
clipboard->open_win = 0;
clipboard->open_thread = NULL;
if (clipboard->seqno == clipboard->open_seqno) return 0; /* unchanged */
for (i = 0; i < clipboard->listen_count; i++)
post_message( clipboard->listeners[i], WM_CLIPBOARDUPDATE, 0, 0 );
return clipboard->viewer;
}
/* close the clipboard, and return the viewer window that should be notified if any */
static user_handle_t close_clipboard( struct clipboard *clipboard )
{
clipboard->open_win = 0;
clipboard->open_thread = NULL;
if (clipboard->seqno == clipboard->open_seqno) return 0; /* unchanged */
return notify_listeners( clipboard );
}
/* release the clipboard owner, and return the viewer window that should be notified if any */
static user_handle_t release_clipboard( struct clipboard *clipboard )
{
struct clip_format *format, *next;
int changed = 0;
clipboard->owner_win = 0;
clipboard->owner_thread = NULL;
/* FIXME: free delay-rendered formats if any and notify listeners */
return 0;
/* free the delayed-rendered formats, since we no longer have an owner to render them */
LIST_FOR_EACH_ENTRY_SAFE( format, next, &clipboard->formats, struct clip_format, entry )
{
if (format->data) continue;
list_remove( &format->entry );
clipboard->format_count--;
free( format );
changed = 1;
}
if (!changed) return 0;
clipboard->seqno++;
return notify_listeners( clipboard );
}
/* cleanup clipboard information upon window destruction */
@ -275,8 +347,6 @@ DECL_HANDLER(close_clipboard)
set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
return;
}
if (req->changed) clipboard->seqno++;
reply->viewer = close_clipboard( clipboard );
reply->owner = clipboard->owner_win;
}
@ -309,6 +379,38 @@ DECL_HANDLER(set_clipboard_info)
}
/* add a data format to the clipboard */
DECL_HANDLER(set_clipboard_data)
{
struct clip_format *format;
struct clipboard *clipboard = get_process_clipboard();
void *data = NULL;
if (!clipboard) return;
if (!req->format || !clipboard->open_thread)
{
set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
return;
}
if (get_req_data_size() && !(data = memdup( get_req_data(), get_req_data_size() ))) return;
if (!(format = get_format( clipboard, req->format )))
{
if (!(format = add_format( clipboard, req->format )))
{
free( data );
return;
}
}
clipboard->seqno++;
format->size = get_req_data_size();
format->data = data;
}
/* empty the clipboard and grab ownership */
DECL_HANDLER(empty_clipboard)
{
@ -321,6 +423,8 @@ DECL_HANDLER(empty_clipboard)
set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
return;
}
free_clipboard_formats( clipboard );
clipboard->owner_win = clipboard->open_win;
clipboard->owner_thread = clipboard->open_thread;
clipboard->seqno++;

View File

@ -3168,7 +3168,6 @@ enum caret_state
/* Close the clipboard */
@REQ(close_clipboard)
int changed; /* did it change since the open? */
@REPLY
user_handle_t viewer; /* first clipboard viewer */
user_handle_t owner; /* current clipboard owner */
@ -3200,6 +3199,13 @@ enum caret_state
@END
/* Add a data format to the clipboard */
@REQ(set_clipboard_data)
unsigned int format; /* clipboard format of the data */
VARARG(data,bytes); /* data contents */
@END
/* Release ownership of the clipboard */
@REQ(release_clipboard)
user_handle_t owner; /* clipboard owner to release */

View File

@ -330,6 +330,7 @@ DECL_HANDLER(open_clipboard);
DECL_HANDLER(close_clipboard);
DECL_HANDLER(set_clipboard_info);
DECL_HANDLER(empty_clipboard);
DECL_HANDLER(set_clipboard_data);
DECL_HANDLER(release_clipboard);
DECL_HANDLER(get_clipboard_info);
DECL_HANDLER(set_clipboard_viewer);
@ -616,6 +617,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_close_clipboard,
(req_handler)req_set_clipboard_info,
(req_handler)req_empty_clipboard,
(req_handler)req_set_clipboard_data,
(req_handler)req_release_clipboard,
(req_handler)req_get_clipboard_info,
(req_handler)req_set_clipboard_viewer,
@ -2028,7 +2030,6 @@ C_ASSERT( FIELD_OFFSET(struct open_clipboard_request, window) == 12 );
C_ASSERT( sizeof(struct open_clipboard_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct open_clipboard_reply, owner) == 8 );
C_ASSERT( sizeof(struct open_clipboard_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct close_clipboard_request, changed) == 12 );
C_ASSERT( sizeof(struct close_clipboard_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct close_clipboard_reply, viewer) == 8 );
C_ASSERT( FIELD_OFFSET(struct close_clipboard_reply, owner) == 12 );
@ -2043,6 +2044,8 @@ C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_reply, old_viewer) == 20 );
C_ASSERT( FIELD_OFFSET(struct set_clipboard_info_reply, seqno) == 24 );
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 release_clipboard_request, owner) == 12 );
C_ASSERT( sizeof(struct release_clipboard_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct release_clipboard_reply, viewer) == 8 );

View File

@ -3746,7 +3746,6 @@ static void dump_open_clipboard_reply( const struct open_clipboard_reply *req )
static void dump_close_clipboard_request( const struct close_clipboard_request *req )
{
fprintf( stderr, " changed=%d", req->changed );
}
static void dump_close_clipboard_reply( const struct close_clipboard_reply *req )
@ -3774,6 +3773,12 @@ static void dump_empty_clipboard_request( const struct empty_clipboard_request *
{
}
static void dump_set_clipboard_data_request( const struct set_clipboard_data_request *req )
{
fprintf( stderr, " format=%08x", req->format );
dump_varargs_bytes( ", data=", cur_size );
}
static void dump_release_clipboard_request( const struct release_clipboard_request *req )
{
fprintf( stderr, " owner=%08x", req->owner );
@ -4646,6 +4651,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_close_clipboard_request,
(dump_func)dump_set_clipboard_info_request,
(dump_func)dump_empty_clipboard_request,
(dump_func)dump_set_clipboard_data_request,
(dump_func)dump_release_clipboard_request,
(dump_func)dump_get_clipboard_info_request,
(dump_func)dump_set_clipboard_viewer_request,
@ -4929,6 +4935,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_close_clipboard_reply,
(dump_func)dump_set_clipboard_info_reply,
NULL,
NULL,
(dump_func)dump_release_clipboard_reply,
(dump_func)dump_get_clipboard_info_reply,
(dump_func)dump_set_clipboard_viewer_reply,
@ -5212,6 +5219,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"close_clipboard",
"set_clipboard_info",
"empty_clipboard",
"set_clipboard_data",
"release_clipboard",
"get_clipboard_info",
"set_clipboard_viewer",