mfplat/sample: Optimize copying to 2d buffer.
[wine.git] / dlls / win32u / clipboard.c
blob6cf484a56cadc0ef6b56ddd314bdd1dcf591cecd
1 /*
2 * WIN32 clipboard implementation
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1996 Alex Korobka
6 * Copyright 1999 Noel Borthwick
7 * Copyright 2003 Ulrich Czekalla for CodeWeavers
8 * Copyright 2016 Alexandre Julliard
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #if 0
27 #pragma makedep unix
28 #endif
30 #include <pthread.h>
31 #include "ntstatus.h"
32 #define WIN32_NO_STATUS
33 #include "win32u_private.h"
34 #include "ntgdi_private.h"
35 #include "ntuser_private.h"
36 #include "wine/server.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
41 static pthread_mutex_t clipboard_mutex = PTHREAD_MUTEX_INITIALIZER;
43 struct cached_format
45 struct list entry; /* entry in cache list */
46 UINT format; /* format id */
47 UINT seqno; /* sequence number when the data was set */
48 HANDLE handle; /* original data handle */
51 static struct list cached_formats = LIST_INIT( cached_formats );
52 static struct list formats_to_free = LIST_INIT( formats_to_free );
55 /* get a debug string for a format id */
56 static const char *debugstr_format( UINT id )
58 WCHAR buffer[256];
59 DWORD le = RtlGetLastWin32Error();
60 BOOL r = NtUserGetClipboardFormatName( id, buffer, ARRAYSIZE(buffer) );
61 RtlSetLastWin32Error(le);
63 if (r)
64 return wine_dbg_sprintf( "%04x %s", id, debugstr_w(buffer) );
66 switch (id)
68 #define BUILTIN(id) case id: return #id;
69 BUILTIN(CF_TEXT)
70 BUILTIN(CF_BITMAP)
71 BUILTIN(CF_METAFILEPICT)
72 BUILTIN(CF_SYLK)
73 BUILTIN(CF_DIF)
74 BUILTIN(CF_TIFF)
75 BUILTIN(CF_OEMTEXT)
76 BUILTIN(CF_DIB)
77 BUILTIN(CF_PALETTE)
78 BUILTIN(CF_PENDATA)
79 BUILTIN(CF_RIFF)
80 BUILTIN(CF_WAVE)
81 BUILTIN(CF_UNICODETEXT)
82 BUILTIN(CF_ENHMETAFILE)
83 BUILTIN(CF_HDROP)
84 BUILTIN(CF_LOCALE)
85 BUILTIN(CF_DIBV5)
86 BUILTIN(CF_OWNERDISPLAY)
87 BUILTIN(CF_DSPTEXT)
88 BUILTIN(CF_DSPBITMAP)
89 BUILTIN(CF_DSPMETAFILEPICT)
90 BUILTIN(CF_DSPENHMETAFILE)
91 #undef BUILTIN
92 default: return wine_dbg_sprintf( "%04x", id );
96 /* retrieve a data format from the cache */
97 static struct cached_format *get_cached_format( UINT format )
99 struct cached_format *cache;
101 LIST_FOR_EACH_ENTRY( cache, &cached_formats, struct cached_format, entry )
102 if (cache->format == format) return cache;
103 return NULL;
106 /* free a single cached format */
107 static void free_cached_data( struct cached_format *cache )
109 struct free_cached_data_params params;
110 void *ret_ptr;
111 ULONG ret_len;
113 switch (cache->format)
115 case CF_BITMAP:
116 case CF_PALETTE:
117 make_gdi_object_system( cache->handle, FALSE );
118 case CF_DSPBITMAP:
119 NtGdiDeleteObjectApp( cache->handle );
120 break;
122 default:
123 params.format = cache->format;
124 params.handle = cache->handle;
125 KeUserModeCallback( NtUserFreeCachedClipboardData, &params, sizeof(params),
126 &ret_ptr, &ret_len );
127 break;
129 free( cache );
132 /* free all the data in the cache */
133 static void free_cached_formats( struct list *list )
135 struct list *ptr;
137 while ((ptr = list_head( list )))
139 list_remove( ptr );
140 free_cached_data( LIST_ENTRY( ptr, struct cached_format, entry ));
144 /* clear global memory formats; special types are freed on EmptyClipboard */
145 static void invalidate_memory_formats( struct list *free_list )
147 struct cached_format *cache, *next;
149 LIST_FOR_EACH_ENTRY_SAFE( cache, next, &cached_formats, struct cached_format, entry )
151 switch (cache->format)
153 case CF_BITMAP:
154 case CF_DSPBITMAP:
155 case CF_PALETTE:
156 case CF_ENHMETAFILE:
157 case CF_DSPENHMETAFILE:
158 case CF_METAFILEPICT:
159 case CF_DSPMETAFILEPICT:
160 continue;
161 default:
162 list_remove( &cache->entry );
163 list_add_tail( free_list, &cache->entry );
164 break;
169 /**************************************************************************
170 * NtUserOpenClipboard (win32u.@)
172 BOOL WINAPI NtUserOpenClipboard( HWND hwnd, ULONG unk )
174 struct list free_list = LIST_INIT( free_list );
175 BOOL ret;
176 HWND owner;
178 TRACE( "%p\n", hwnd );
180 user_driver->pUpdateClipboard();
182 pthread_mutex_lock( &clipboard_mutex );
184 SERVER_START_REQ( open_clipboard )
186 req->window = wine_server_user_handle( hwnd );
187 ret = !wine_server_call_err( req );
188 owner = wine_server_ptr_handle( reply->owner );
190 SERVER_END_REQ;
192 if (ret && !is_current_process_window( owner )) invalidate_memory_formats( &free_list );
194 pthread_mutex_unlock( &clipboard_mutex );
195 free_cached_formats( &free_list );
196 return ret;
199 /**************************************************************************
200 * NtUserCloseClipboard (win32u.@)
202 BOOL WINAPI NtUserCloseClipboard(void)
204 HWND viewer = 0, owner = 0;
205 BOOL ret;
207 TRACE( "\n" );
209 SERVER_START_REQ( close_clipboard )
211 if ((ret = !wine_server_call_err( req )))
213 viewer = wine_server_ptr_handle( reply->viewer );
214 owner = wine_server_ptr_handle( reply->owner );
217 SERVER_END_REQ;
219 if (viewer) NtUserMessageCall( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0,
220 0, NtUserSendNotifyMessage, FALSE );
221 return ret;
224 /**************************************************************************
225 * NtUserEmptyClipboard (win32u.@)
227 BOOL WINAPI NtUserEmptyClipboard(void)
229 BOOL ret;
230 HWND owner = NtUserGetClipboardOwner();
231 struct list free_list = LIST_INIT( free_list );
233 TRACE( "owner %p\n", owner );
235 if (owner)
236 send_message_timeout( owner, WM_DESTROYCLIPBOARD, 0, 0, SMTO_ABORTIFHUNG, 5000, FALSE );
238 pthread_mutex_lock( &clipboard_mutex );
240 SERVER_START_REQ( empty_clipboard )
242 ret = !wine_server_call_err( req );
244 SERVER_END_REQ;
246 if (ret)
248 list_move_tail( &free_list, &formats_to_free );
249 list_move_tail( &free_list, &cached_formats );
252 pthread_mutex_unlock( &clipboard_mutex );
253 free_cached_formats( &free_list );
254 return ret;
257 /**************************************************************************
258 * NtUserCountClipboardFormats (win32u.@)
260 INT WINAPI NtUserCountClipboardFormats(void)
262 INT count = 0;
264 user_driver->pUpdateClipboard();
266 SERVER_START_REQ( get_clipboard_formats )
268 wine_server_call( req );
269 count = reply->count;
271 SERVER_END_REQ;
273 TRACE( "returning %d\n", count );
274 return count;
277 /**************************************************************************
278 * NtUserIsClipboardFormatAvailable (win32u.@)
280 BOOL WINAPI NtUserIsClipboardFormatAvailable( UINT format )
282 BOOL ret = FALSE;
284 if (!format) return FALSE;
286 user_driver->pUpdateClipboard();
288 SERVER_START_REQ( get_clipboard_formats )
290 req->format = format;
291 if (!wine_server_call_err( req )) ret = (reply->count > 0);
293 SERVER_END_REQ;
294 TRACE( "%s -> %u\n", debugstr_format( format ), ret );
295 return ret;
298 /**************************************************************************
299 * NtUserGetUpdatedClipboardFormats (win32u.@)
301 BOOL WINAPI NtUserGetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size )
303 BOOL ret;
305 if (!out_size)
307 RtlSetLastWin32Error( ERROR_NOACCESS );
308 return FALSE;
311 user_driver->pUpdateClipboard();
313 SERVER_START_REQ( get_clipboard_formats )
315 if (formats) wine_server_set_reply( req, formats, size * sizeof(*formats) );
316 ret = !wine_server_call_err( req );
317 *out_size = reply->count;
319 SERVER_END_REQ;
321 TRACE( "%p %u returning %u formats, ret %u\n", formats, size, *out_size, ret );
322 if (!ret && !formats && *out_size) RtlSetLastWin32Error( ERROR_NOACCESS );
323 return ret;
326 /**************************************************************************
327 * NtUserGetPriorityClipboardFormat (win32u.@)
329 INT WINAPI NtUserGetPriorityClipboardFormat( UINT *list, INT count )
331 int i;
333 TRACE( "%p %u\n", list, count );
335 if (NtUserCountClipboardFormats() == 0)
336 return 0;
338 for (i = 0; i < count; i++)
339 if (NtUserIsClipboardFormatAvailable( list[i] ))
340 return list[i];
342 return -1;
345 /**************************************************************************
346 * NtUserGetClipboardFormatName (win32u.@)
348 INT WINAPI NtUserGetClipboardFormatName( UINT format, WCHAR *buffer, INT maxlen )
350 char buf[sizeof(ATOM_BASIC_INFORMATION) + MAX_ATOM_LEN * sizeof(WCHAR)];
351 ATOM_BASIC_INFORMATION *abi = (ATOM_BASIC_INFORMATION *)buf;
352 UINT length = 0;
354 if (format < MAXINTATOM || format > 0xffff) return 0;
355 if (maxlen <= 0)
357 RtlSetLastWin32Error( ERROR_MORE_DATA );
358 return 0;
360 if (!set_ntstatus( NtQueryInformationAtom( format, AtomBasicInformation,
361 buf, sizeof(buf), NULL )))
362 return 0;
364 length = min( abi->NameLength / sizeof(WCHAR), maxlen - 1 );
365 if (length) memcpy( buffer, abi->Name, length * sizeof(WCHAR) );
366 buffer[length] = 0;
367 return length;
370 /**************************************************************************
371 * NtUserGetClipboardOwner (win32u.@)
373 HWND WINAPI NtUserGetClipboardOwner(void)
375 HWND owner = 0;
377 SERVER_START_REQ( get_clipboard_info )
379 if (!wine_server_call_err( req )) owner = wine_server_ptr_handle( reply->owner );
381 SERVER_END_REQ;
383 TRACE( "returning %p\n", owner );
384 return owner;
387 /**************************************************************************
388 * NtUserSetClipboardViewer (win32u.@)
390 HWND WINAPI NtUserSetClipboardViewer( HWND hwnd )
392 HWND prev = 0, owner = 0;
394 SERVER_START_REQ( set_clipboard_viewer )
396 req->viewer = wine_server_user_handle( hwnd );
397 if (!wine_server_call_err( req ))
399 prev = wine_server_ptr_handle( reply->old_viewer );
400 owner = wine_server_ptr_handle( reply->owner );
403 SERVER_END_REQ;
405 if (hwnd)
406 NtUserMessageCall( hwnd, WM_DRAWCLIPBOARD, (WPARAM)owner, 0,
407 NULL, NtUserSendNotifyMessage, FALSE );
409 TRACE( "%p returning %p\n", hwnd, prev );
410 return prev;
413 /**************************************************************************
414 * NtUserGetClipboardViewer (win32u.@)
416 HWND WINAPI NtUserGetClipboardViewer(void)
418 HWND viewer = 0;
420 SERVER_START_REQ( get_clipboard_info )
422 if (!wine_server_call_err( req )) viewer = wine_server_ptr_handle( reply->viewer );
424 SERVER_END_REQ;
426 TRACE( "returning %p\n", viewer );
427 return viewer;
430 /**************************************************************************
431 * NtUserChangeClipboardChain (win32u.@)
433 BOOL WINAPI NtUserChangeClipboardChain( HWND hwnd, HWND next )
435 NTSTATUS status;
436 HWND viewer;
438 if (!hwnd) return FALSE;
440 SERVER_START_REQ( set_clipboard_viewer )
442 req->viewer = wine_server_user_handle( next );
443 req->previous = wine_server_user_handle( hwnd );
444 status = wine_server_call( req );
445 viewer = wine_server_ptr_handle( reply->old_viewer );
447 SERVER_END_REQ;
449 if (status == STATUS_PENDING)
450 return !send_message( viewer, WM_CHANGECBCHAIN, (WPARAM)hwnd, (LPARAM)next );
452 if (status) RtlSetLastWin32Error( RtlNtStatusToDosError( status ));
453 return !status;
456 /**************************************************************************
457 * NtUserGetOpenClipboardWindow (win32u.@)
459 HWND WINAPI NtUserGetOpenClipboardWindow(void)
461 HWND window = 0;
463 SERVER_START_REQ( get_clipboard_info )
465 if (!wine_server_call_err( req )) window = wine_server_ptr_handle( reply->window );
467 SERVER_END_REQ;
469 TRACE( "returning %p\n", window );
470 return window;
473 /**************************************************************************
474 * NtUserGetClipboardSequenceNumber (win32u.@)
476 DWORD WINAPI NtUserGetClipboardSequenceNumber(void)
478 unsigned int seqno = 0;
480 SERVER_START_REQ( get_clipboard_info )
482 if (!wine_server_call_err( req )) seqno = reply->seqno;
484 SERVER_END_REQ;
486 TRACE( "returning %u\n", seqno );
487 return seqno;
490 /* see EnumClipboardFormats */
491 UINT enum_clipboard_formats( UINT format )
493 UINT ret = 0;
495 SERVER_START_REQ( enum_clipboard_formats )
497 req->previous = format;
498 if (!wine_server_call_err( req ))
500 ret = reply->format;
501 RtlSetLastWin32Error( ERROR_SUCCESS );
504 SERVER_END_REQ;
506 TRACE( "%s -> %s\n", debugstr_format( format ), debugstr_format( ret ));
507 return ret;
510 /**************************************************************************
511 * NtUserAddClipboardFormatListener (win32u.@)
513 BOOL WINAPI NtUserAddClipboardFormatListener( HWND hwnd )
515 BOOL ret;
517 SERVER_START_REQ( add_clipboard_listener )
519 req->window = wine_server_user_handle( hwnd );
520 ret = !wine_server_call_err( req );
522 SERVER_END_REQ;
523 return ret;
526 /**************************************************************************
527 * NtUserRemoveClipboardFormatListener (win32u.@)
529 BOOL WINAPI NtUserRemoveClipboardFormatListener( HWND hwnd )
531 BOOL ret;
533 SERVER_START_REQ( remove_clipboard_listener )
535 req->window = wine_server_user_handle( hwnd );
536 ret = !wine_server_call_err( req );
538 SERVER_END_REQ;
539 return ret;
542 /**************************************************************************
543 * release_clipboard_owner
545 void release_clipboard_owner( HWND hwnd )
547 HWND viewer = 0, owner = 0;
549 send_message( hwnd, WM_RENDERALLFORMATS, 0, 0 );
551 SERVER_START_REQ( release_clipboard )
553 req->owner = wine_server_user_handle( hwnd );
554 if (!wine_server_call( req ))
556 viewer = wine_server_ptr_handle( reply->viewer );
557 owner = wine_server_ptr_handle( reply->owner );
560 SERVER_END_REQ;
562 if (viewer)
563 NtUserMessageCall( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0,
564 0, NtUserSendNotifyMessage, FALSE );
567 /**************************************************************************
568 * NtUserSetClipboardData (win32u.@)
570 NTSTATUS WINAPI NtUserSetClipboardData( UINT format, HANDLE data, struct set_clipboard_params *params )
572 struct cached_format *cache = NULL, *prev = NULL;
573 LCID lcid;
574 void *ptr = NULL;
575 data_size_t size = 0;
576 NTSTATUS status = STATUS_SUCCESS;
578 TRACE( "%s %p\n", debugstr_format( format ), data );
580 if (params->cache_only)
582 pthread_mutex_lock( &clipboard_mutex );
583 if ((cache = get_cached_format( format )) && cache->seqno == params->seqno)
584 cache->handle = data;
585 else
586 status = STATUS_UNSUCCESSFUL;
587 pthread_mutex_unlock( &clipboard_mutex );
588 return status;
591 if (params->data)
593 ptr = params->data;
594 size = params->size;
595 if (data)
597 if (!(cache = malloc( sizeof(*cache) ))) goto done;
598 cache->format = format;
599 cache->handle = data;
602 if (format == CF_BITMAP || format == CF_PALETTE)
604 make_gdi_object_system( cache->handle, TRUE );
607 NtQueryDefaultLocale( TRUE, &lcid );
609 pthread_mutex_lock( &clipboard_mutex );
611 SERVER_START_REQ( set_clipboard_data )
613 req->format = format;
614 req->lcid = lcid;
615 wine_server_add_data( req, ptr, size );
616 if (!(status = wine_server_call( req )))
618 if (cache) cache->seqno = reply->seqno;
621 SERVER_END_REQ;
623 if (!status)
625 /* free the previous entry if any */
626 if ((prev = get_cached_format( format ))) list_remove( &prev->entry );
627 if (cache) list_add_tail( &cached_formats, &cache->entry );
629 else free( cache );
631 pthread_mutex_unlock( &clipboard_mutex );
632 if (prev) free_cached_data( prev );
634 done:
635 return status;
638 /**************************************************************************
639 * NtUserGetClipboardData (win32u.@)
641 HANDLE WINAPI NtUserGetClipboardData( UINT format, struct get_clipboard_params *params )
643 struct cached_format *cache = NULL;
644 unsigned int status;
645 UINT from, data_seqno;
646 size_t size;
647 HWND owner;
648 BOOL render = TRUE;
650 for (;;)
652 pthread_mutex_lock( &clipboard_mutex );
654 if (!params->data_only) cache = get_cached_format( format );
656 SERVER_START_REQ( get_clipboard_data )
658 req->format = format;
659 req->render = render;
660 if (cache && cache->handle)
662 req->cached = 1;
663 req->seqno = cache->seqno;
665 wine_server_set_reply( req, params->data, params->size );
666 status = wine_server_call( req );
667 from = reply->from;
668 size = reply->total;
669 data_seqno = reply->seqno;
670 owner = wine_server_ptr_handle( reply->owner );
672 SERVER_END_REQ;
674 params->size = size;
676 if (!status && size)
678 if (cache)
680 if (cache->handle && data_seqno == cache->seqno) /* we can reuse the cached data */
682 HANDLE ret = cache->handle;
683 pthread_mutex_unlock( &clipboard_mutex );
684 TRACE( "%s returning %p\n", debugstr_format( format ), ret );
685 return ret;
688 /* cache entry is stale, remove it */
689 list_remove( &cache->entry );
690 list_add_tail( &formats_to_free, &cache->entry );
693 if (params->data_only)
695 pthread_mutex_unlock( &clipboard_mutex );
696 return params->data;
699 /* allocate new cache entry */
700 if (!(cache = malloc( sizeof(*cache) )))
702 pthread_mutex_unlock( &clipboard_mutex );
703 return 0;
706 cache->format = format;
707 cache->seqno = data_seqno;
708 cache->handle = NULL;
709 params->seqno = cache->seqno;
710 list_add_tail( &cached_formats, &cache->entry );
711 pthread_mutex_unlock( &clipboard_mutex );
712 TRACE( "%s needs unmarshaling\n", debugstr_format( format ) );
713 params->data_size = ~0;
714 return 0;
716 pthread_mutex_unlock( &clipboard_mutex );
718 if (status == STATUS_BUFFER_OVERFLOW)
720 params->data_size = size;
721 return 0;
723 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
725 RtlSetLastWin32Error( ERROR_NOT_FOUND ); /* no such format */
726 return 0;
728 if (status)
730 RtlSetLastWin32Error( RtlNtStatusToDosError( status ));
731 TRACE( "%s error %08x\n", debugstr_format( format ), status );
732 return 0;
734 if (render) /* try rendering it */
736 render = FALSE;
737 if (from)
739 struct render_synthesized_format_params params = { .format = format, .from = from };
740 ULONG ret_len;
741 void *ret_ptr;
742 KeUserModeCallback( NtUserRenderSynthesizedFormat, &params, sizeof(params),
743 &ret_ptr, &ret_len );
744 continue;
746 if (owner)
748 TRACE( "%s sending WM_RENDERFORMAT to %p\n", debugstr_format( format ), owner );
749 send_message( owner, WM_RENDERFORMAT, format, 0 );
750 continue;
753 TRACE( "%s returning 0\n", debugstr_format( format ));
754 return 0;