mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-15 20:39:29 +00:00
win32u: Move clipboard data management from user32.
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
cad4adedaa
commit
eba0a51002
|
@ -48,17 +48,6 @@
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
|
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 clipboard_cs;
|
||||||
static CRITICAL_SECTION_DEBUG critsect_debug =
|
static CRITICAL_SECTION_DEBUG critsect_debug =
|
||||||
{
|
{
|
||||||
|
@ -119,7 +108,7 @@ static const char *debugstr_format( UINT id )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build the data to send to the server in SetClipboardData */
|
/* build the data to send to the server in SetClipboardData */
|
||||||
static HANDLE marshal_data( UINT format, HANDLE handle, data_size_t *ret_size )
|
static HANDLE marshal_data( UINT format, HANDLE handle, size_t *ret_size )
|
||||||
{
|
{
|
||||||
SIZE_T size;
|
SIZE_T size;
|
||||||
|
|
||||||
|
@ -258,110 +247,35 @@ static HANDLE unmarshal_data( UINT format, void *data, data_size_t size )
|
||||||
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 */
|
/* free a single cached format */
|
||||||
static void free_cached_data( struct cached_format *cache )
|
void free_cached_data( UINT format, HANDLE handle )
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
switch (cache->format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case CF_BITMAP:
|
case CF_BITMAP:
|
||||||
case CF_DSPBITMAP:
|
case CF_DSPBITMAP:
|
||||||
case CF_PALETTE:
|
case CF_PALETTE:
|
||||||
DeleteObject( cache->handle );
|
DeleteObject( handle );
|
||||||
break;
|
break;
|
||||||
case CF_ENHMETAFILE:
|
case CF_ENHMETAFILE:
|
||||||
case CF_DSPENHMETAFILE:
|
case CF_DSPENHMETAFILE:
|
||||||
DeleteEnhMetaFile( cache->handle );
|
DeleteEnhMetaFile( handle );
|
||||||
break;
|
break;
|
||||||
case CF_METAFILEPICT:
|
case CF_METAFILEPICT:
|
||||||
case CF_DSPMETAFILEPICT:
|
case CF_DSPMETAFILEPICT:
|
||||||
if ((ptr = GlobalLock( cache->handle )))
|
if ((ptr = GlobalLock( handle )))
|
||||||
{
|
{
|
||||||
DeleteMetaFile( ((METAFILEPICT *)ptr)->hMF );
|
DeleteMetaFile( ((METAFILEPICT *)ptr)->hMF );
|
||||||
GlobalUnlock( cache->handle );
|
GlobalUnlock( handle );
|
||||||
}
|
}
|
||||||
GlobalFree( cache->handle );
|
GlobalFree( handle );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
GlobalFree( cache->handle );
|
GlobalFree( handle );
|
||||||
break;
|
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 */
|
/* get the clipboard locale stored in the CF_LOCALE format */
|
||||||
|
@ -576,7 +490,7 @@ static HANDLE render_synthesized_enhmetafile( HANDLE data )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* render a synthesized format */
|
/* render a synthesized format */
|
||||||
static HANDLE render_synthesized_format( UINT format, UINT from )
|
HANDLE render_synthesized_format( UINT format, UINT from )
|
||||||
{
|
{
|
||||||
HANDLE data = GetClipboardData( from );
|
HANDLE data = GetClipboardData( from );
|
||||||
|
|
||||||
|
@ -650,55 +564,7 @@ INT WINAPI GetClipboardFormatNameA( UINT format, LPSTR buffer, INT maxlen )
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI OpenClipboard( HWND hwnd )
|
BOOL WINAPI OpenClipboard( HWND hwnd )
|
||||||
{
|
{
|
||||||
BOOL ret;
|
return NtUserOpenClipboard( hwnd, 0 );
|
||||||
HWND owner;
|
|
||||||
|
|
||||||
TRACE( "%p\n", hwnd );
|
|
||||||
|
|
||||||
NtUserCallNoParam( NtUserUpdateClipboard );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
* EmptyClipboard (USER32.@)
|
|
||||||
* Empties and acquires ownership of the clipboard
|
|
||||||
*/
|
|
||||||
BOOL WINAPI EmptyClipboard(void)
|
|
||||||
{
|
|
||||||
BOOL ret;
|
|
||||||
HWND owner = NtUserGetClipboardOwner();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -707,55 +573,28 @@ BOOL WINAPI EmptyClipboard(void)
|
||||||
*/
|
*/
|
||||||
HANDLE WINAPI SetClipboardData( UINT format, HANDLE data )
|
HANDLE WINAPI SetClipboardData( UINT format, HANDLE data )
|
||||||
{
|
{
|
||||||
struct cached_format *cache = NULL;
|
struct set_clipboard_params params = { .size = 0 };
|
||||||
void *ptr = NULL;
|
HANDLE handle = data;
|
||||||
data_size_t size = 0;
|
NTSTATUS status;
|
||||||
HANDLE handle = data, retval = 0;
|
|
||||||
NTSTATUS status = STATUS_SUCCESS;
|
|
||||||
|
|
||||||
TRACE( "%s %p\n", debugstr_format( format ), data );
|
TRACE( "%s %p\n", debugstr_format( format ), data );
|
||||||
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
if (!(handle = marshal_data( format, data, &size ))) return 0;
|
if (!(handle = marshal_data( format, data, ¶ms.size ))) return 0;
|
||||||
if (!(ptr = GlobalLock( handle ))) goto done;
|
if (!(params.data = GlobalLock( handle ))) return 0;
|
||||||
if (!(cache = HeapAlloc( GetProcessHeap(), 0, sizeof(*cache) ))) goto done;
|
|
||||||
cache->format = format;
|
|
||||||
cache->handle = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EnterCriticalSection( &clipboard_cs );
|
status = NtUserSetClipboardData( format, data, ¶ms );
|
||||||
|
|
||||||
SERVER_START_REQ( set_clipboard_data )
|
if (params.data) GlobalUnlock( handle );
|
||||||
{
|
|
||||||
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 (handle != data) GlobalFree( handle );
|
||||||
if (status) SetLastError( RtlNtStatusToDosError( status ));
|
if (status)
|
||||||
return retval;
|
{
|
||||||
|
SetLastError( RtlNtStatusToDosError( status ));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -773,73 +612,34 @@ UINT WINAPI EnumClipboardFormats( UINT format )
|
||||||
*/
|
*/
|
||||||
HANDLE WINAPI GetClipboardData( UINT format )
|
HANDLE WINAPI GetClipboardData( UINT format )
|
||||||
{
|
{
|
||||||
struct cached_format *cache;
|
struct get_clipboard_params params = { .data_size = 1024 };
|
||||||
NTSTATUS status;
|
HANDLE ret = 0;
|
||||||
UINT from, data_seqno;
|
|
||||||
HWND owner;
|
|
||||||
HANDLE data;
|
|
||||||
UINT size = 1024;
|
|
||||||
BOOL render = TRUE;
|
|
||||||
|
|
||||||
for (;;)
|
EnterCriticalSection( &clipboard_cs );
|
||||||
|
|
||||||
|
while (params.data_size)
|
||||||
{
|
{
|
||||||
if (!(data = GlobalAlloc( GMEM_FIXED, size ))) return 0;
|
params.size = params.data_size;
|
||||||
|
params.data_size = 0;
|
||||||
EnterCriticalSection( &clipboard_cs );
|
if (!(params.data = GlobalAlloc( GMEM_FIXED, params.size ))) break;
|
||||||
cache = get_cached_format( format );
|
ret = NtUserGetClipboardData( format, ¶ms );
|
||||||
|
if (ret) break;
|
||||||
SERVER_START_REQ( get_clipboard_data )
|
if (params.data_size == ~0) /* needs unmarshaling */
|
||||||
{
|
{
|
||||||
req->format = format;
|
struct set_clipboard_params set_params = { .cache_only = TRUE, .seqno = params.seqno };
|
||||||
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)
|
ret = unmarshal_data( format, params.data, params.size );
|
||||||
{
|
if (!NtUserSetClipboardData( format, ret, &set_params )) break;
|
||||||
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 */
|
/* data changed, retry */
|
||||||
if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0; /* no such format */
|
free_cached_data( format, ret );
|
||||||
if (status)
|
ret = 0;
|
||||||
{
|
params.data_size = 1024;
|
||||||
SetLastError( RtlNtStatusToDosError( status ));
|
continue;
|
||||||
TRACE( "%s error %08x\n", debugstr_format( format ), status );
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
if (render) /* try rendering it */
|
GlobalFree( params.data );
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection( &clipboard_cs );
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3134,7 +3134,7 @@ static void EDIT_WM_Copy(EDITSTATE *es)
|
||||||
TRACE("%s\n", debugstr_w(dst));
|
TRACE("%s\n", debugstr_w(dst));
|
||||||
GlobalUnlock(hdst);
|
GlobalUnlock(hdst);
|
||||||
OpenClipboard(es->hwndSelf);
|
OpenClipboard(es->hwndSelf);
|
||||||
EmptyClipboard();
|
NtUserEmptyClipboard();
|
||||||
SetClipboardData(CF_UNICODETEXT, hdst);
|
SetClipboardData(CF_UNICODETEXT, hdst);
|
||||||
NtUserCloseClipboard();
|
NtUserCloseClipboard();
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@
|
||||||
@ stdcall DrawTextExW(long wstr long ptr long ptr)
|
@ stdcall DrawTextExW(long wstr long ptr long ptr)
|
||||||
@ stdcall DrawTextW(long wstr long ptr long)
|
@ stdcall DrawTextW(long wstr long ptr long)
|
||||||
@ stdcall EditWndProc(long long long long) EditWndProcA
|
@ stdcall EditWndProc(long long long long) EditWndProcA
|
||||||
@ stdcall EmptyClipboard()
|
@ stdcall EmptyClipboard() NtUserEmptyClipboard
|
||||||
@ stdcall EnableMenuItem(long long long) NtUserEnableMenuItem
|
@ stdcall EnableMenuItem(long long long) NtUserEnableMenuItem
|
||||||
@ stdcall EnableMouseInPointer(long)
|
@ stdcall EnableMouseInPointer(long)
|
||||||
@ stdcall EnableNonClientDpiScaling(long)
|
@ stdcall EnableNonClientDpiScaling(long)
|
||||||
|
|
|
@ -184,6 +184,20 @@ static const struct user_callbacks user_funcs =
|
||||||
unregister_imm,
|
unregister_imm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static NTSTATUS WINAPI User32FreeCachedClipboardData( const struct free_cached_data_params *params,
|
||||||
|
ULONG size )
|
||||||
|
{
|
||||||
|
free_cached_data( params->format, params->handle );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS WINAPI User32RenderSsynthesizedFormat( const struct render_synthesized_format_params *params,
|
||||||
|
ULONG size )
|
||||||
|
{
|
||||||
|
render_synthesized_format( params->format, params->from );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL WINAPI User32LoadDriver( const WCHAR *path, ULONG size )
|
static BOOL WINAPI User32LoadDriver( const WCHAR *path, ULONG size )
|
||||||
{
|
{
|
||||||
return LoadLibraryW( path ) != NULL;
|
return LoadLibraryW( path ) != NULL;
|
||||||
|
@ -196,7 +210,9 @@ static const void *kernel_callback_table[NtUserCallCount] =
|
||||||
User32CallWinEventHook,
|
User32CallWinEventHook,
|
||||||
User32CallWindowProc,
|
User32CallWindowProc,
|
||||||
User32CallWindowsHook,
|
User32CallWindowsHook,
|
||||||
|
User32FreeCachedClipboardData,
|
||||||
User32LoadDriver,
|
User32LoadDriver,
|
||||||
|
User32RenderSsynthesizedFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,8 @@ extern BOOL process_rawinput_message( MSG *msg, UINT hw_id,
|
||||||
const struct hardware_msg_data *msg_data ) DECLSPEC_HIDDEN;
|
const struct hardware_msg_data *msg_data ) DECLSPEC_HIDDEN;
|
||||||
extern BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
|
extern BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
|
||||||
void **buffer, size_t size ) DECLSPEC_HIDDEN;
|
void **buffer, size_t size ) DECLSPEC_HIDDEN;
|
||||||
|
extern void free_cached_data( UINT format, HANDLE handle ) DECLSPEC_HIDDEN;
|
||||||
|
extern HANDLE render_synthesized_format( UINT format, UINT from ) DECLSPEC_HIDDEN;
|
||||||
|
|
||||||
extern void CLIPBOARD_ReleaseOwner( HWND hwnd ) DECLSPEC_HIDDEN;
|
extern void CLIPBOARD_ReleaseOwner( HWND hwnd ) DECLSPEC_HIDDEN;
|
||||||
extern BOOL FOCUS_MouseActivate( HWND hwnd ) DECLSPEC_HIDDEN;
|
extern BOOL FOCUS_MouseActivate( HWND hwnd ) DECLSPEC_HIDDEN;
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
#pragma makedep unix
|
#pragma makedep unix
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "ntstatus.h"
|
||||||
|
#define WIN32_NO_STATUS
|
||||||
#include "win32u_private.h"
|
#include "win32u_private.h"
|
||||||
#include "ntuser_private.h"
|
#include "ntuser_private.h"
|
||||||
#include "wine/server.h"
|
#include "wine/server.h"
|
||||||
|
@ -34,6 +37,19 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
|
WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
|
||||||
|
|
||||||
|
static pthread_mutex_t clipboard_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
|
||||||
/* get a debug string for a format id */
|
/* get a debug string for a format id */
|
||||||
static const char *debugstr_format( UINT id )
|
static const char *debugstr_format( UINT id )
|
||||||
|
@ -76,13 +92,106 @@ static const char *debugstr_format( UINT id )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free a single cached format */
|
||||||
|
static void free_cached_data( struct cached_format *cache )
|
||||||
|
{
|
||||||
|
struct free_cached_data_params params;
|
||||||
|
void *ret_ptr;
|
||||||
|
ULONG ret_len;
|
||||||
|
|
||||||
|
switch (cache->format)
|
||||||
|
{
|
||||||
|
case CF_BITMAP:
|
||||||
|
case CF_DSPBITMAP:
|
||||||
|
case CF_PALETTE:
|
||||||
|
NtGdiDeleteObjectApp( cache->handle );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
params.format = cache->format;
|
||||||
|
params.handle = cache->handle;
|
||||||
|
KeUserModeCallback( NtUserFreeCachedClipboardData, ¶ms, sizeof(params),
|
||||||
|
&ret_ptr, &ret_len );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free( cache );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free all the data in the cache */
|
||||||
|
static void free_cached_formats( struct list *list )
|
||||||
|
{
|
||||||
|
struct list *ptr;
|
||||||
|
|
||||||
|
while ((ptr = list_head( list )))
|
||||||
|
{
|
||||||
|
list_remove( ptr );
|
||||||
|
free_cached_data( LIST_ENTRY( ptr, struct cached_format, entry ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear global memory formats; special types are freed on EmptyClipboard */
|
||||||
|
static void invalidate_memory_formats( struct list *free_list )
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
list_remove( &cache->entry );
|
||||||
|
list_add_tail( free_list, &cache->entry );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* NtUserOpenClipboard (win32u.@)
|
* NtUserOpenClipboard (win32u.@)
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI NtUserOpenClipboard( HWND hwnd, ULONG unk )
|
BOOL WINAPI NtUserOpenClipboard( HWND hwnd, ULONG unk )
|
||||||
{
|
{
|
||||||
FIXME( "\n" );
|
struct list free_list = LIST_INIT( free_list );
|
||||||
return FALSE;
|
BOOL ret;
|
||||||
|
HWND owner;
|
||||||
|
|
||||||
|
TRACE( "%p\n", hwnd );
|
||||||
|
|
||||||
|
user_driver->pUpdateClipboard();
|
||||||
|
|
||||||
|
pthread_mutex_lock( &clipboard_mutex );
|
||||||
|
|
||||||
|
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 && !is_current_process_window( owner )) invalidate_memory_formats( &free_list );
|
||||||
|
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
free_cached_formats( &free_list );
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
@ -115,8 +224,32 @@ BOOL WINAPI NtUserCloseClipboard(void)
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI NtUserEmptyClipboard(void)
|
BOOL WINAPI NtUserEmptyClipboard(void)
|
||||||
{
|
{
|
||||||
FIXME( "\n" );
|
BOOL ret;
|
||||||
return FALSE;
|
HWND owner = NtUserGetClipboardOwner();
|
||||||
|
struct list free_list = LIST_INIT( free_list );
|
||||||
|
|
||||||
|
TRACE( "owner %p\n", owner );
|
||||||
|
|
||||||
|
if (owner) send_message_timeout( owner, WM_DESTROYCLIPBOARD, 0, 0, SMTO_ABORTIFHUNG,
|
||||||
|
5000, NULL, FALSE );
|
||||||
|
|
||||||
|
pthread_mutex_lock( &clipboard_mutex );
|
||||||
|
|
||||||
|
SERVER_START_REQ( empty_clipboard )
|
||||||
|
{
|
||||||
|
ret = !wine_server_call_err( req );
|
||||||
|
}
|
||||||
|
SERVER_END_REQ;
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
list_move_tail( &free_list, &formats_to_free );
|
||||||
|
list_move_tail( &free_list, &cached_formats );
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
free_cached_formats( &free_list );
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
@ -432,10 +565,65 @@ void release_clipboard_owner( HWND hwnd )
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* NtUserSetClipboardData (win32u.@)
|
* NtUserSetClipboardData (win32u.@)
|
||||||
*/
|
*/
|
||||||
NTSTATUS WINAPI NtUserSetClipboardData( UINT format, HANDLE handle, struct set_clipboard_params *params )
|
NTSTATUS WINAPI NtUserSetClipboardData( UINT format, HANDLE data, struct set_clipboard_params *params )
|
||||||
{
|
{
|
||||||
FIXME( "\n" );
|
struct cached_format *cache = NULL, *prev = NULL;
|
||||||
return 0;
|
void *ptr = NULL;
|
||||||
|
data_size_t size = 0;
|
||||||
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
TRACE( "%s %p\n", debugstr_format( format ), data );
|
||||||
|
|
||||||
|
if (params->cache_only)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock( &clipboard_mutex );
|
||||||
|
if ((cache = get_cached_format( format )) && cache->seqno == params->seqno)
|
||||||
|
cache->handle = data;
|
||||||
|
else
|
||||||
|
status = STATUS_UNSUCCESSFUL;
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params->data)
|
||||||
|
{
|
||||||
|
ptr = params->data;
|
||||||
|
size = params->size;
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
if (!(cache = malloc( sizeof(*cache) ))) goto done;
|
||||||
|
cache->format = format;
|
||||||
|
cache->handle = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock( &clipboard_mutex );
|
||||||
|
|
||||||
|
SERVER_START_REQ( set_clipboard_data )
|
||||||
|
{
|
||||||
|
req->format = format;
|
||||||
|
NtQueryDefaultLocale( TRUE, &req->lcid );
|
||||||
|
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 */
|
||||||
|
if ((prev = get_cached_format( format ))) list_remove( &prev->entry );
|
||||||
|
if (cache) list_add_tail( &cached_formats, &cache->entry );
|
||||||
|
}
|
||||||
|
else free( cache );
|
||||||
|
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
if (prev) free_cached_data( prev );
|
||||||
|
|
||||||
|
done:
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
@ -443,6 +631,102 @@ NTSTATUS WINAPI NtUserSetClipboardData( UINT format, HANDLE handle, struct set_c
|
||||||
*/
|
*/
|
||||||
HANDLE WINAPI NtUserGetClipboardData( UINT format, struct get_clipboard_params *params )
|
HANDLE WINAPI NtUserGetClipboardData( UINT format, struct get_clipboard_params *params )
|
||||||
{
|
{
|
||||||
FIXME( "\n" );
|
struct cached_format *cache;
|
||||||
return 0;
|
NTSTATUS status;
|
||||||
|
UINT from, data_seqno;
|
||||||
|
size_t size;
|
||||||
|
HWND owner;
|
||||||
|
BOOL render = TRUE;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock( &clipboard_mutex );
|
||||||
|
|
||||||
|
cache = get_cached_format( format );
|
||||||
|
|
||||||
|
SERVER_START_REQ( get_clipboard_data )
|
||||||
|
{
|
||||||
|
req->format = format;
|
||||||
|
req->render = render;
|
||||||
|
if (cache && cache->handle)
|
||||||
|
{
|
||||||
|
req->cached = 1;
|
||||||
|
req->seqno = cache->seqno;
|
||||||
|
}
|
||||||
|
wine_server_set_reply( req, params->data, params->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)
|
||||||
|
{
|
||||||
|
if (cache)
|
||||||
|
{
|
||||||
|
if (cache->handle && data_seqno == cache->seqno) /* we can reuse the cached data */
|
||||||
|
{
|
||||||
|
HANDLE ret = cache->handle;
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
TRACE( "%s returning %p\n", debugstr_format( format ), ret );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cache entry is stale, remove it */
|
||||||
|
list_remove( &cache->entry );
|
||||||
|
list_add_tail( &formats_to_free, &cache->entry );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate new cache entry */
|
||||||
|
if (!(cache = malloc( sizeof(*cache) ))) return 0;
|
||||||
|
|
||||||
|
cache->format = format;
|
||||||
|
cache->seqno = data_seqno;
|
||||||
|
cache->handle = NULL;
|
||||||
|
params->seqno = cache->seqno;
|
||||||
|
list_add_tail( &cached_formats, &cache->entry );
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
TRACE( "%s needs unmarshaling\n", debugstr_format( format ) );
|
||||||
|
params->data_size = ~0;
|
||||||
|
params->size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock( &clipboard_mutex );
|
||||||
|
|
||||||
|
if (status == STATUS_BUFFER_OVERFLOW)
|
||||||
|
{
|
||||||
|
params->data_size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0; /* no such format */
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct render_synthesized_format_params params = { .format = format, .from = from };
|
||||||
|
ULONG ret_len;
|
||||||
|
void *ret_ptr;
|
||||||
|
KeUserModeCallback( NtUserRenderSynthesizedFormat, ¶ms, sizeof(params),
|
||||||
|
&ret_ptr, &ret_len );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (owner)
|
||||||
|
{
|
||||||
|
TRACE( "%s sending WM_RENDERFORMAT to %p\n", debugstr_format( format ), owner );
|
||||||
|
send_message( owner, WM_RENDERFORMAT, format, 0 );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE( "%s returning 0\n", debugstr_format( format ));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4671,10 +4671,6 @@ ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
|
||||||
thread_detach();
|
thread_detach();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case NtUserUpdateClipboard:
|
|
||||||
user_driver->pUpdateClipboard();
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
FIXME( "invalid code %u\n", code );
|
FIXME( "invalid code %u\n", code );
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -32,7 +32,9 @@ enum
|
||||||
NtUserCallWinEventHook,
|
NtUserCallWinEventHook,
|
||||||
NtUserCallWinProc,
|
NtUserCallWinProc,
|
||||||
NtUserCallWindowsHook,
|
NtUserCallWindowsHook,
|
||||||
|
NtUserFreeCachedClipboardData,
|
||||||
NtUserLoadDriver,
|
NtUserLoadDriver,
|
||||||
|
NtUserRenderSynthesizedFormat,
|
||||||
/* win16 hooks */
|
/* win16 hooks */
|
||||||
NtUserCallFreeIcon,
|
NtUserCallFreeIcon,
|
||||||
NtUserThunkLock,
|
NtUserThunkLock,
|
||||||
|
@ -140,6 +142,20 @@ struct win_hook_params
|
||||||
WCHAR module[MAX_PATH];
|
WCHAR module[MAX_PATH];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* NtUserFreeCachedClipboardData params */
|
||||||
|
struct free_cached_data_params
|
||||||
|
{
|
||||||
|
UINT format;
|
||||||
|
HANDLE handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* NtUserRenderSynthesizedFormat params */
|
||||||
|
struct render_synthesized_format_params
|
||||||
|
{
|
||||||
|
UINT format;
|
||||||
|
UINT from;
|
||||||
|
};
|
||||||
|
|
||||||
/* process DPI awareness contexts */
|
/* process DPI awareness contexts */
|
||||||
#define NTUSER_DPI_UNAWARE 0x00006010
|
#define NTUSER_DPI_UNAWARE 0x00006010
|
||||||
#define NTUSER_DPI_SYSTEM_AWARE 0x00006011
|
#define NTUSER_DPI_SYSTEM_AWARE 0x00006011
|
||||||
|
@ -237,11 +253,19 @@ struct client_menu_name
|
||||||
/* NtUserGetClipboardData params, not compatible with Windows */
|
/* NtUserGetClipboardData params, not compatible with Windows */
|
||||||
struct get_clipboard_params
|
struct get_clipboard_params
|
||||||
{
|
{
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
size_t data_size;
|
||||||
|
UINT seqno;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* NtUserSetClipboardData params, not compatible with Windows */
|
/* NtUserSetClipboardData params, not compatible with Windows */
|
||||||
struct set_clipboard_params
|
struct set_clipboard_params
|
||||||
{
|
{
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
BOOL cache_only;
|
||||||
|
UINT seqno;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* internal messages codes */
|
/* internal messages codes */
|
||||||
|
@ -624,7 +648,6 @@ enum
|
||||||
/* temporary exports */
|
/* temporary exports */
|
||||||
NtUserExitingThread,
|
NtUserExitingThread,
|
||||||
NtUserThreadDetach,
|
NtUserThreadDetach,
|
||||||
NtUserUpdateClipboard,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline HWND NtUserGetDesktopWindow(void)
|
static inline HWND NtUserGetDesktopWindow(void)
|
||||||
|
|
Loading…
Reference in a new issue