user32: Add synthesized clipboard formats on the server side.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2016-09-26 15:00:22 +09:00
parent 3044935b0e
commit 9ed8f5e83f
6 changed files with 84 additions and 140 deletions

View File

@ -52,10 +52,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
/*
* Indicates if data has changed since open.
*/
static BOOL bCBHasChanged = FALSE;
/* get a debug string for a format id */
static const char *debugstr_format( UINT id )
@ -205,30 +201,6 @@ static HANDLE unmarshal_data( UINT format, void *data, data_size_t size )
}
/* formats that can be synthesized are: CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT,
CF_BITMAP, CF_DIB, CF_DIBV5, CF_ENHMETAFILE, CF_METAFILEPICT */
static UINT synthesized_formats[CF_MAX];
/* add a synthesized format to the list */
static void add_synthesized_format( UINT format, UINT from )
{
assert( format < CF_MAX );
SetClipboardData( format, 0 );
synthesized_formats[format] = from;
}
/* store the current locale in the CF_LOCALE format */
static void set_clipboard_locale(void)
{
HANDLE data = GlobalAlloc( GMEM_FIXED, sizeof(LCID) );
if (!data) return;
*(LCID *)data = GetUserDefaultLCID();
SetClipboardData( CF_LOCALE, data );
TRACE( "added CF_LOCALE\n" );
}
/* get the clipboard locale stored in the CF_LOCALE format */
static LCID get_clipboard_locale(void)
{
@ -258,71 +230,6 @@ static UINT get_format_codepage( LCID lcid, UINT format )
return ret;
}
/* add synthesized text formats based on what is already in the clipboard */
static void add_synthesized_text(void)
{
BOOL has_text = IsClipboardFormatAvailable( CF_TEXT );
BOOL has_oemtext = IsClipboardFormatAvailable( CF_OEMTEXT );
BOOL has_unicode = IsClipboardFormatAvailable( CF_UNICODETEXT );
if (!has_text && !has_oemtext && !has_unicode) return; /* no text, nothing to do */
if (!IsClipboardFormatAvailable( CF_LOCALE )) set_clipboard_locale();
if (has_unicode)
{
if (!has_text) add_synthesized_format( CF_TEXT, CF_UNICODETEXT );
if (!has_oemtext) add_synthesized_format( CF_OEMTEXT, CF_UNICODETEXT );
}
else if (has_text)
{
if (!has_oemtext) add_synthesized_format( CF_OEMTEXT, CF_TEXT );
if (!has_unicode) add_synthesized_format( CF_UNICODETEXT, CF_TEXT );
}
else
{
if (!has_text) add_synthesized_format( CF_TEXT, CF_OEMTEXT );
if (!has_unicode) add_synthesized_format( CF_UNICODETEXT, CF_OEMTEXT );
}
}
/* add synthesized bitmap formats based on what is already in the clipboard */
static void add_synthesized_bitmap(void)
{
BOOL has_dib = IsClipboardFormatAvailable( CF_DIB );
BOOL has_dibv5 = IsClipboardFormatAvailable( CF_DIBV5 );
BOOL has_bitmap = IsClipboardFormatAvailable( CF_BITMAP );
if (!has_bitmap && !has_dib && !has_dibv5) return; /* nothing to do */
if (has_bitmap && has_dib && has_dibv5) return; /* nothing to synthesize */
if (has_bitmap)
{
if (!has_dib) add_synthesized_format( CF_DIB, CF_BITMAP );
if (!has_dibv5) add_synthesized_format( CF_DIBV5, CF_BITMAP );
}
else if (has_dib)
{
if (!has_bitmap) add_synthesized_format( CF_BITMAP, CF_DIB );
if (!has_dibv5) add_synthesized_format( CF_DIBV5, CF_DIB );
}
else
{
if (!has_bitmap) add_synthesized_format( CF_BITMAP, CF_DIBV5 );
if (!has_dib) add_synthesized_format( CF_DIB, CF_DIBV5 );
}
}
/* add synthesized metafile formats based on what is already in the clipboard */
static void add_synthesized_metafile(void)
{
BOOL has_mf = IsClipboardFormatAvailable( CF_METAFILEPICT );
BOOL has_emf = IsClipboardFormatAvailable( CF_ENHMETAFILE );
if (!has_mf && has_emf) add_synthesized_format( CF_METAFILEPICT, CF_ENHMETAFILE );
else if (!has_emf && has_mf) add_synthesized_format( CF_ENHMETAFILE, CF_METAFILEPICT );
}
/* render synthesized ANSI text based on the contents of the 'from' format */
static HANDLE render_synthesized_textA( HANDLE data, UINT format, UINT from )
{
@ -619,14 +526,7 @@ BOOL WINAPI OpenClipboard( HWND hwnd )
SERVER_START_REQ( open_clipboard )
{
req->window = wine_server_user_handle( hwnd );
if ((ret = !wine_server_call_err( req )))
{
if (!reply->owner)
{
bCBHasChanged = FALSE;
memset( synthesized_formats, 0, sizeof(synthesized_formats) );
}
}
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
@ -642,15 +542,7 @@ BOOL WINAPI CloseClipboard(void)
HWND viewer = 0, owner = 0;
BOOL ret;
TRACE("() Changed=%d\n", bCBHasChanged);
if (bCBHasChanged)
{
memset( synthesized_formats, 0, sizeof(synthesized_formats) );
add_synthesized_text();
add_synthesized_bitmap();
add_synthesized_metafile();
}
TRACE( "\n" );
SERVER_START_REQ( close_clipboard )
{
@ -662,12 +554,8 @@ BOOL WINAPI CloseClipboard(void)
}
SERVER_END_REQ;
if (!ret) return FALSE;
bCBHasChanged = FALSE;
if (viewer) SendNotifyMessageW( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 );
return TRUE;
return ret;
}
@ -690,11 +578,6 @@ BOOL WINAPI EmptyClipboard(void)
}
SERVER_END_REQ;
if (ret)
{
bCBHasChanged = TRUE;
memset( synthesized_formats, 0, sizeof(synthesized_formats) );
}
return ret;
}
@ -829,17 +712,14 @@ HANDLE WINAPI SetClipboardData( UINT format, HANDLE data )
SERVER_START_REQ( set_clipboard_data )
{
req->format = format;
req->lcid = GetUserDefaultLCID();
wine_server_add_data( req, ptr, size );
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
if (ret)
{
bCBHasChanged = TRUE;
if (format < CF_MAX) synthesized_formats[format] = 0;
retval = data;
}
done:
if (ptr) GlobalUnlock( ptr );
@ -949,14 +829,12 @@ BOOL WINAPI GetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size
HANDLE WINAPI GetClipboardData( UINT format )
{
NTSTATUS status;
UINT from;
HWND owner;
HANDLE data;
UINT size = 1024;
BOOL render = TRUE;
if (format < CF_MAX && synthesized_formats[format])
return render_synthesized_format( format, synthesized_formats[format] );
for (;;)
{
if (!(data = GlobalAlloc( GMEM_FIXED, size ))) return 0;
@ -966,6 +844,7 @@ HANDLE WINAPI GetClipboardData( UINT format )
req->format = format;
wine_server_set_reply( req, data, size );
status = wine_server_call( req );
from = reply->from;
size = reply->total;
owner = wine_server_ptr_handle( reply->owner );
}
@ -995,6 +874,7 @@ HANDLE WINAPI GetClipboardData( UINT format )
SendMessageW( owner, WM_RENDERFORMAT, format, 0 );
continue;
}
if (from) return render_synthesized_format( format, from );
}
TRACE( "%s returning 0\n", debugstr_format( format ));
return 0;
@ -1024,11 +904,6 @@ INT WINAPI GetPriorityClipboardFormat(UINT *list, INT nCount)
/**************************************************************************
* GetClipboardSequenceNumber (USER32.@)
* Supported on Win2k/Win98
* MSDN: Windows clipboard code keeps a serial number for the clipboard
* for each window station. The number is incremented whenever the
* contents change or are emptied.
* If you do not have WINSTA_ACCESSCLIPBOARD then the function returns 0
*/
DWORD WINAPI GetClipboardSequenceNumber(VOID)
{

View File

@ -4531,7 +4531,9 @@ struct set_clipboard_data_request
{
struct request_header __header;
unsigned int format;
unsigned int lcid;
/* VARARG(data,bytes); */
char __pad_20[4];
};
struct set_clipboard_data_reply
{
@ -4548,9 +4550,11 @@ struct get_clipboard_data_request
struct get_clipboard_data_reply
{
struct reply_header __header;
unsigned int from;
user_handle_t owner;
data_size_t total;
/* VARARG(data,bytes); */
char __pad_20[4];
};
@ -6434,6 +6438,6 @@ union generic_reply
struct terminate_job_reply terminate_job_reply;
};
#define SERVER_PROTOCOL_VERSION 520
#define SERVER_PROTOCOL_VERSION 521
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */

View File

@ -41,6 +41,7 @@ struct clip_format
{
struct list entry; /* entry in format list */
unsigned int id; /* format id */
unsigned int from; /* for synthesized data, format to generate it from */
data_size_t size; /* size of the data block */
void *data; /* data contents, or NULL for delay-rendered */
};
@ -53,10 +54,12 @@ struct clipboard
struct thread *owner_thread; /* thread id that owns the clipboard */
user_handle_t owner_win; /* window that owns the clipboard data */
user_handle_t viewer; /* first window in clipboard viewer list */
unsigned int lcid; /* locale id to use for synthesizing text formats */
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 format_map; /* existence bitmap for formats < CF_MAX */
unsigned int listen_size; /* size of listeners array */
unsigned int listen_count; /* count of listeners */
user_handle_t *listeners; /* array of listener windows */
@ -88,6 +91,8 @@ static const struct object_ops clipboard_ops =
};
#define HAS_FORMAT(map,id) ((map) & (1 << (id))) /* only for formats < CF_MAX */
/* find a data format in the clipboard */
static struct clip_format *get_format( struct clipboard *clipboard, unsigned int id )
{
@ -106,10 +111,12 @@ static struct clip_format *add_format( struct clipboard *clipboard, unsigned int
if (!(format = mem_alloc( sizeof(*format )))) return NULL;
format->id = id;
format->from = 0;
format->size = 0;
format->data = NULL;
list_add_tail( &clipboard->formats, &format->entry );
clipboard->format_count++;
if (id < CF_MAX) clipboard->format_map |= 1 << id;
return format;
}
@ -125,6 +132,7 @@ static void free_clipboard_formats( struct clipboard *clipboard )
free( format );
}
clipboard->format_count = 0;
clipboard->format_map = 0;
}
/* dump a clipboard object */
@ -164,6 +172,7 @@ static struct clipboard *get_process_clipboard(void)
clipboard->viewer = 0;
clipboard->seqno = 0;
clipboard->format_count = 0;
clipboard->format_map = 0;
clipboard->listen_size = 0;
clipboard->listen_count = 0;
clipboard->listeners = NULL;
@ -175,6 +184,49 @@ static struct clipboard *get_process_clipboard(void)
return clipboard;
}
/* add synthesized formats upon clipboard close */
static int synthesize_formats( struct clipboard *clipboard )
{
static const unsigned int formats[][3] =
{
{ CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT },
{ CF_OEMTEXT, CF_UNICODETEXT, CF_TEXT },
{ CF_UNICODETEXT, CF_TEXT, CF_OEMTEXT },
{ CF_METAFILEPICT, CF_ENHMETAFILE },
{ CF_ENHMETAFILE, CF_METAFILEPICT },
{ CF_BITMAP, CF_DIB, CF_DIBV5 },
{ CF_DIB, CF_BITMAP, CF_DIBV5 },
{ CF_DIBV5, CF_BITMAP, CF_DIB }
};
unsigned int i, from, total = 0, map = clipboard->format_map;
struct clip_format *format;
if (!HAS_FORMAT( map, CF_LOCALE ) &&
(HAS_FORMAT( map, CF_TEXT ) || HAS_FORMAT( map, CF_OEMTEXT ) || HAS_FORMAT( map, CF_UNICODETEXT )))
{
void *data = memdup( &clipboard->lcid, sizeof(clipboard->lcid) );
if ((format = add_format( clipboard, CF_LOCALE )))
{
clipboard->seqno++;
format->data = data;
format->size = sizeof(clipboard->lcid);
}
else free( data );
}
for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++)
{
if (HAS_FORMAT( map, formats[i][0] )) continue;
if (HAS_FORMAT( map, formats[i][1] )) from = formats[i][1];
else if (HAS_FORMAT( map, formats[i][2] )) from = formats[i][2];
else continue;
if (!(format = add_format( clipboard, formats[i][0] ))) continue;
format->from = from;
total++;
}
return total;
}
/* add a clipboard listener */
static void add_listener( struct clipboard *clipboard, user_handle_t window )
{
@ -233,6 +285,7 @@ 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 */
if (synthesize_formats( clipboard )) clipboard->seqno++;
return notify_listeners( clipboard );
}
@ -248,8 +301,9 @@ static user_handle_t release_clipboard( struct clipboard *clipboard )
/* 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;
if (format->data || format->from) continue;
list_remove( &format->entry );
if (format->id < CF_MAX) clipboard->format_map &= ~(1 << format->id);
clipboard->format_count--;
free( format );
changed = 1;
@ -406,8 +460,12 @@ DECL_HANDLER(set_clipboard_data)
}
clipboard->seqno++;
format->from = 0;
format->size = get_req_data_size();
format->data = data;
if (req->format == CF_TEXT || req->format == CF_OEMTEXT || req->format == CF_UNICODETEXT)
clipboard->lcid = req->lcid;
}
@ -429,8 +487,9 @@ DECL_HANDLER(get_clipboard_data)
set_error( STATUS_OBJECT_NAME_NOT_FOUND );
return;
}
reply->from = format->from;
reply->total = format->size;
if (!format->data) reply->owner = clipboard->owner_win;
if (!format->data && !format->from) 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 );
}

View File

@ -3202,6 +3202,7 @@ enum caret_state
/* Add a data format to the clipboard */
@REQ(set_clipboard_data)
unsigned int format; /* clipboard format of the data */
unsigned int lcid; /* locale id to use for synthesizing text formats */
VARARG(data,bytes); /* data contents */
@END
@ -3210,6 +3211,7 @@ enum caret_state
@REQ(get_clipboard_data)
unsigned int format; /* clipboard format of the data */
@REPLY
unsigned int from; /* for synthesized data, format to generate it from */
user_handle_t owner; /* clipboard owner for delayed-rendered formats */
data_size_t total; /* total data size */
VARARG(data,bytes); /* data contents */

View File

@ -2051,12 +2051,14 @@ 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 set_clipboard_data_request, lcid) == 16 );
C_ASSERT( sizeof(struct set_clipboard_data_request) == 24 );
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_data_reply, from) == 8 );
C_ASSERT( FIELD_OFFSET(struct get_clipboard_data_reply, owner) == 12 );
C_ASSERT( FIELD_OFFSET(struct get_clipboard_data_reply, total) == 16 );
C_ASSERT( sizeof(struct get_clipboard_data_reply) == 24 );
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 );

View File

@ -3776,6 +3776,7 @@ 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 );
fprintf( stderr, ", lcid=%08x", req->lcid );
dump_varargs_bytes( ", data=", cur_size );
}
@ -3786,7 +3787,8 @@ static void dump_get_clipboard_data_request( const struct get_clipboard_data_req
static void dump_get_clipboard_data_reply( const struct get_clipboard_data_reply *req )
{
fprintf( stderr, " owner=%08x", req->owner );
fprintf( stderr, " from=%08x", req->from );
fprintf( stderr, ", owner=%08x", req->owner );
fprintf( stderr, ", total=%u", req->total );
dump_varargs_bytes( ", data=", cur_size );
}