CreateFontIndirect() can get NULL pointers.
[wine.git] / windows / clipboard.c
blob93643f61f24373a9c2f3629cedacdbe3b628ff31
1 /*
2 * WIN32 clipboard implementation
4 * Copyright 1994 Martin Ayotte
5 * 1996 Alex Korobka
6 * 1999 Noel Borthwick
8 * NOTES:
9 * This file contains the implementation for the WIN32 Clipboard API
10 * and Wine's internal clipboard cache.
11 * The actual contents of the clipboard are held in the clipboard cache.
12 * The internal implementation talks to a "clipboard driver" to fill or
13 * expose the cache to the native device. (Currently only the X11 and
14 * TTY clipboard driver are available)
16 * TODO:
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <string.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "wine/winuser16.h"
32 #include "wine/winbase16.h"
33 #include "heap.h"
34 #include "message.h"
35 #include "task.h"
36 #include "queue.h"
37 #include "user.h"
38 #include "clipboard.h"
39 #include "debugtools.h"
41 DEFAULT_DEBUG_CHANNEL(clipboard);
43 #define CF_REGFORMATBASE 0xC000
45 /**************************************************************************
46 * Clipboard context global variables
49 static HANDLE hClipLock = 0;
50 static BOOL bCBHasChanged = FALSE;
52 HWND hWndClipWindow = 0; /* window that last opened clipboard */
53 HWND hWndClipOwner = 0; /* current clipboard owner */
54 HANDLE16 hTaskClipOwner = 0; /* clipboard owner's task */
55 static HWND hWndViewer = 0; /* start of viewers chain */
57 static WORD LastRegFormat = CF_REGFORMATBASE;
59 /* Clipboard cache initial data.
60 * WARNING: This data ordering is dependent on the WINE_CLIPFORMAT structure
61 * declared in clipboard.h
63 WINE_CLIPFORMAT ClipFormats[] = {
64 { CF_TEXT, 1, 0, "Text", 0, 0, 0, 0, NULL, &ClipFormats[1]},
65 { CF_BITMAP, 1, 0, "Bitmap", 0, 0, 0, 0, &ClipFormats[0], &ClipFormats[2]},
66 { CF_METAFILEPICT, 1, 0, "MetaFile Picture", 0, 0, 0, 0, &ClipFormats[1], &ClipFormats[3]},
67 { CF_SYLK, 1, 0, "Sylk", 0, 0, 0, 0, &ClipFormats[2], &ClipFormats[4]},
68 { CF_DIF, 1, 0, "DIF", 0, 0, 0, 0, &ClipFormats[3], &ClipFormats[5]},
69 { CF_TIFF, 1, 0, "TIFF", 0, 0, 0, 0, &ClipFormats[4], &ClipFormats[6]},
70 { CF_OEMTEXT, 1, 0, "OEM Text", 0, 0, 0, 0, &ClipFormats[5], &ClipFormats[7]},
71 { CF_DIB, 1, 0, "DIB", 0, 0, 0, 0, &ClipFormats[6], &ClipFormats[8]},
72 { CF_PALETTE, 1, 0, "Palette", 0, 0, 0, 0, &ClipFormats[7], &ClipFormats[9]},
73 { CF_PENDATA, 1, 0, "PenData", 0, 0, 0, 0, &ClipFormats[8], &ClipFormats[10]},
74 { CF_RIFF, 1, 0, "RIFF", 0, 0, 0, 0, &ClipFormats[9], &ClipFormats[11]},
75 { CF_WAVE, 1, 0, "Wave", 0, 0, 0, 0, &ClipFormats[10], &ClipFormats[12]},
76 { CF_UNICODETEXT, 1, 0, "Unicode Text", 0, 0, 0, 0, &ClipFormats[11], &ClipFormats[13]},
77 { CF_OWNERDISPLAY, 1, 0, "Owner Display", 0, 0, 0, 0, &ClipFormats[12], &ClipFormats[14]},
78 { CF_DSPTEXT, 1, 0, "DSPText", 0, 0, 0, 0, &ClipFormats[13], &ClipFormats[15]},
79 { CF_DSPMETAFILEPICT, 1, 0, "DSPMetaFile Picture", 0, 0, 0, 0, &ClipFormats[14], &ClipFormats[16]},
80 { CF_DSPBITMAP, 1, 0, "DSPBitmap", 0, 0, 0, 0, &ClipFormats[15], &ClipFormats[17]},
81 { CF_HDROP, 1, 0, "HDROP", 0, 0, 0, 0, &ClipFormats[16], NULL}
85 /**************************************************************************
86 * Internal Clipboard implementation methods
87 **************************************************************************/
90 /**************************************************************************
91 * CLIPBOARD_LookupFormat
93 static LPWINE_CLIPFORMAT __lookup_format( LPWINE_CLIPFORMAT lpFormat, WORD wID )
95 while(TRUE)
97 if (lpFormat == NULL ||
98 lpFormat->wFormatID == wID) break;
99 lpFormat = lpFormat->NextFormat;
101 return lpFormat;
104 LPWINE_CLIPFORMAT CLIPBOARD_LookupFormat( WORD wID )
106 return __lookup_format( ClipFormats, wID );
109 /**************************************************************************
110 * CLIPBOARD_IsLocked
111 * Check if the clipboard cache is available to the caller
113 BOOL CLIPBOARD_IsLocked()
115 BOOL bIsLocked = TRUE;
116 HANDLE16 hTaskCur = GetCurrentTask();
119 * The clipboard is available:
120 * 1. if the caller's task has opened the clipboard,
121 * or
122 * 2. if the caller is the clipboard owners task, AND is responding to a
123 * WM_RENDERFORMAT message.
125 if ( hClipLock == hTaskCur )
126 bIsLocked = FALSE;
128 else if ( hTaskCur == hTaskClipOwner )
130 /* Check if we're currently executing inside a window procedure
131 * called in response to a WM_RENDERFORMAT message. A WM_RENDERFORMAT
132 * handler is not permitted to open the clipboard since it has been opened
133 * by another client. However the handler must have access to the
134 * clipboard in order to update data in response to this message.
136 MESSAGEQUEUE *queue = QUEUE_Lock( GetFastQueue16() );
138 if ( queue
139 && queue->smWaiting
140 && queue->smWaiting->msg == WM_RENDERFORMAT
141 && queue->smWaiting->hSrcQueue
143 bIsLocked = FALSE;
145 QUEUE_Unlock( queue );
148 return bIsLocked;
151 /**************************************************************************
152 * CLIPBOARD_ReleaseOwner
153 * Gives up ownership of the clipboard
155 void CLIPBOARD_ReleaseOwner()
157 hWndClipOwner = 0;
158 hTaskClipOwner = 0;
161 /**************************************************************************
162 * CLIPBOARD_GlobalFreeProc
164 * This is a callback mechanism to allow HGLOBAL data to be released in
165 * the context of the process which allocated it. We post a WM_TIMER message
166 * to the owner window(in CLIPBOARD_DeleteRecord) and destroy the data(in idEvent)
167 * in this WndProc, which is invoked when the apps message loop calls DispatchMessage.
168 * This technique is discussed in Matt Pietrek's "Under the Hood".
169 * An article describing the same may be found in MSDN by searching for WM_TIMER.
170 * Note that this mechanism will probably stop working when WINE supports
171 * address space separation. When "queue events" are implemented in Wine we
172 * should switch to using that mechanism, since it is more robust and does not
173 * require a procedure address to be passed. See the SetWinEventHook API for
174 * more info on this.
176 VOID CALLBACK CLIPBOARD_GlobalFreeProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
178 /* idEvent is the HGLOBAL to be deleted */
179 GlobalFree( (HGLOBAL)idEvent );
182 /**************************************************************************
183 * CLIPBOARD_DeleteRecord
185 void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange)
187 if( (lpFormat->wFormatID >= CF_GDIOBJFIRST &&
188 lpFormat->wFormatID <= CF_GDIOBJLAST) || lpFormat->wFormatID == CF_BITMAP
189 || lpFormat->wFormatID == CF_PALETTE)
191 if (lpFormat->hData32)
192 DeleteObject(lpFormat->hData32);
193 if (lpFormat->hData16)
194 DeleteObject(lpFormat->hData16);
196 else if( lpFormat->wFormatID == CF_METAFILEPICT )
198 if (lpFormat->hData32)
200 DeleteMetaFile( ((METAFILEPICT *)GlobalLock( lpFormat->hData32 ))->hMF );
201 PostMessageA(hWndClipOwner, WM_TIMER,
202 (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc);
203 if (lpFormat->hDataSrc32)
205 /* Release lpFormat->hData32 in the context of the process which created it.
206 * See CLIPBOARD_GlobalFreeProc for more details about this technique.
207 * GlobalFree(lpFormat->hDataSrc32);
209 PostMessageA(hWndClipOwner, WM_TIMER,
210 (WPARAM)lpFormat->hDataSrc32, (LPARAM)CLIPBOARD_GlobalFreeProc);
213 if (lpFormat->hData16)
214 /* HMETAFILE16 and HMETAFILE32 are apparently the same thing,
215 and a shallow copy is enough to share a METAFILEPICT
216 structure between 16bit and 32bit clipboards. The MetaFile
217 should of course only be deleted once. */
218 GlobalFree16(lpFormat->hData16);
220 if (lpFormat->hData16)
222 DeleteMetaFile16( ((METAFILEPICT16 *)GlobalLock16( lpFormat->hData16 ))->hMF );
223 GlobalFree16(lpFormat->hData16);
226 else
228 if (lpFormat->hData32)
230 /* Release lpFormat->hData32 in the context of the process which created it.
231 * See CLIPBOARD_GlobalFreeProc for more details about this technique.
232 * GlobalFree( lpFormat->hData32 );
234 PostMessageA(hWndClipOwner, WM_TIMER,
235 (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc);
237 if (lpFormat->hDataSrc32)
239 /* Release lpFormat->hData32 in the context of the process which created it.
240 * See CLIPBOARD_GlobalFreeProc for more details about this technique.
241 * GlobalFree(lpFormat->hDataSrc32);
243 PostMessageA(hWndClipOwner, WM_TIMER,
244 (WPARAM)lpFormat->hDataSrc32, (LPARAM)CLIPBOARD_GlobalFreeProc);
246 if (lpFormat->hData16)
247 GlobalFree16(lpFormat->hData16);
250 lpFormat->wDataPresent = 0;
251 lpFormat->hData16 = 0;
252 lpFormat->hData32 = 0;
253 lpFormat->hDataSrc32 = 0;
254 lpFormat->drvData = 0;
256 if( bChange ) bCBHasChanged = TRUE;
259 /**************************************************************************
260 * CLIPBOARD_EmptyCache
262 void CLIPBOARD_EmptyCache( BOOL bChange )
264 LPWINE_CLIPFORMAT lpFormat = ClipFormats;
266 while(lpFormat)
268 if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 )
269 CLIPBOARD_DeleteRecord( lpFormat, bChange );
271 lpFormat = lpFormat->NextFormat;
275 /**************************************************************************
276 * CLIPBOARD_IsPresent
278 BOOL CLIPBOARD_IsPresent(WORD wFormat)
280 /* special case */
282 if( wFormat == CF_TEXT || wFormat == CF_OEMTEXT || wFormat == CF_UNICODETEXT )
283 return ClipFormats[CF_TEXT-1].wDataPresent ||
284 ClipFormats[CF_OEMTEXT-1].wDataPresent ||
285 ClipFormats[CF_UNICODETEXT-1].wDataPresent;
286 else
288 LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
289 if( lpFormat ) return (lpFormat->wDataPresent);
291 return FALSE;
294 /**************************************************************************
295 * CLIPBOARD_IsCacheRendered
296 * Checks if any data needs to be rendered to the clipboard cache
297 * RETURNS:
298 * TRUE - All clipboard data is available in the cache
299 * FALSE - Some data is marked for delayed render and needs rendering
301 BOOL CLIPBOARD_IsCacheRendered()
303 LPWINE_CLIPFORMAT lpFormat = ClipFormats;
305 /* check if all formats were rendered */
306 while(lpFormat)
308 if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
309 return FALSE;
311 lpFormat = lpFormat->NextFormat;
314 return TRUE;
318 /**************************************************************************
319 * CLIPBOARD_IsMemoryObject
320 * Tests if the clipboard format specifies a memory object
322 BOOL CLIPBOARD_IsMemoryObject( WORD wFormat )
324 switch(wFormat)
326 case CF_BITMAP:
327 case CF_METAFILEPICT:
328 case CF_DSPTEXT:
329 case CF_ENHMETAFILE:
330 case CF_HDROP:
331 case CF_PALETTE:
332 case CF_PENDATA:
333 return FALSE;
334 default:
335 return TRUE;
339 /***********************************************************************
340 * CLIPBOARD_GlobalDupMem( HGLOBAL )
341 * Helper method to duplicate an HGLOBAL chunk of memory into shared memory
343 HGLOBAL CLIPBOARD_GlobalDupMem( HGLOBAL hGlobalSrc )
345 HGLOBAL hGlobalDest;
346 PVOID pGlobalSrc, pGlobalDest;
347 DWORD cBytes;
349 if ( !hGlobalSrc )
350 return 0;
352 cBytes = GlobalSize(hGlobalSrc);
353 if ( 0 == cBytes )
354 return 0;
356 /* Turn on the DDESHARE and _MOVEABLE flags explicitly */
357 hGlobalDest = GlobalAlloc( GlobalFlags(hGlobalSrc) | GMEM_DDESHARE | GMEM_MOVEABLE,
358 cBytes );
359 if ( !hGlobalDest )
360 return 0;
362 pGlobalSrc = GlobalLock(hGlobalSrc);
363 pGlobalDest = GlobalLock(hGlobalDest);
364 if ( !pGlobalSrc || !pGlobalDest )
365 return 0;
367 memcpy(pGlobalDest, pGlobalSrc, cBytes);
369 GlobalUnlock(hGlobalSrc);
370 GlobalUnlock(hGlobalDest);
372 return hGlobalDest;
375 /**************************************************************************
376 * CLIPBOARD_GetFormatName
377 * Gets the format name associated with an ID
379 char * CLIPBOARD_GetFormatName(UINT wFormat)
381 LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
382 return (lpFormat) ? lpFormat->Name : NULL;
386 /**************************************************************************
387 * CLIPBOARD_RenderFormat
389 static BOOL CLIPBOARD_RenderFormat(LPWINE_CLIPFORMAT lpFormat)
392 * If WINE is not the selection owner, and the format is available
393 * we must ask the driver to render the data to the clipboard cache.
395 TRACE("enter format=%d\n", lpFormat->wFormatID);
396 if ( !USER_Driver.pIsSelectionOwner()
397 && USER_Driver.pIsClipboardFormatAvailable( lpFormat->wFormatID ) )
399 if ( !USER_Driver.pGetClipboardData( lpFormat->wFormatID ) )
400 return FALSE;
403 * If Wine owns the clipboard, and the data is marked for delayed render,
404 * render it now.
406 else if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
408 if( IsWindow(hWndClipOwner) )
410 /* Send a WM_RENDERFORMAT message to notify the owner to render the
411 * data requested into the clipboard.
413 TRACE("Sending WM_RENDERFORMAT message\n");
414 SendMessage16(hWndClipOwner,WM_RENDERFORMAT,
415 (WPARAM16)lpFormat->wFormatID,0L);
417 else
419 WARN("\thWndClipOwner (%04x) is lost!\n",
420 hWndClipOwner);
421 CLIPBOARD_ReleaseOwner();
422 lpFormat->wDataPresent = 0;
423 return FALSE;
427 return (lpFormat->hData16 || lpFormat->hData32) ? TRUE : FALSE;
430 /**************************************************************************
431 * CLIPBOARD_ConvertText
432 * Returns number of required/converted characters - not bytes!
434 static INT CLIPBOARD_ConvertText(WORD src_fmt, void const *src, INT src_size,
435 WORD dst_fmt, void *dst, INT dst_size)
437 UINT cp;
439 if(src_fmt == CF_UNICODETEXT)
441 switch(dst_fmt)
443 case CF_TEXT:
444 cp = CP_ACP;
445 break;
446 case CF_OEMTEXT:
447 cp = CP_OEMCP;
448 break;
449 default:
450 return 0;
452 return WideCharToMultiByte(cp, 0, src, src_size, dst, dst_size, NULL, NULL);
455 if(dst_fmt == CF_UNICODETEXT)
457 switch(src_fmt)
459 case CF_TEXT:
460 cp = CP_ACP;
461 break;
462 case CF_OEMTEXT:
463 cp = CP_OEMCP;
464 break;
465 default:
466 return 0;
468 return MultiByteToWideChar(cp, 0, src, src_size, dst, dst_size);
471 if(!dst_size) return src_size;
473 if(dst_size > src_size) dst_size = src_size;
475 if(src_fmt == CF_TEXT )
476 CharToOemBuffA(src, dst, dst_size);
477 else
478 OemToCharBuffA(src, dst, dst_size);
480 return dst_size;
483 /**************************************************************************
484 * CLIPBOARD_RenderText
486 * Renders text to the clipboard buffer converting between UNIX and DOS formats.
488 * RETURNS: pointer to the WINE_CLIPFORMAT if successful, NULL otherwise
490 * FIXME: Should be a pair of driver functions that convert between OEM text and Windows.
493 static LPWINE_CLIPFORMAT CLIPBOARD_RenderText( UINT wFormat )
495 LPWINE_CLIPFORMAT lpSource = ClipFormats;
496 LPWINE_CLIPFORMAT lpTarget = NULL;
497 BOOL foundData = FALSE;
499 /* Asked for CF_TEXT but not available - always attempt to convert
500 from CF_UNICODETEXT or CF_OEMTEXT */
501 if( wFormat == CF_TEXT && !ClipFormats[CF_TEXT-1].wDataPresent )
503 if(ClipFormats[CF_UNICODETEXT-1].wDataPresent)
505 /* Convert UNICODETEXT -> TEXT */
506 lpSource = &ClipFormats[CF_UNICODETEXT-1];
507 lpTarget = &ClipFormats[CF_TEXT-1];
508 foundData = TRUE;
509 TRACE("\tUNICODETEXT -> TEXT\n");
511 else if(ClipFormats[CF_OEMTEXT-1].wDataPresent)
513 /* Convert OEMTEXT -> TEXT */
514 lpSource = &ClipFormats[CF_OEMTEXT-1];
515 lpTarget = &ClipFormats[CF_TEXT-1];
516 foundData = TRUE;
517 TRACE("\tOEMTEXT -> TEXT\n");
520 /* Asked for CF_OEMTEXT but not available - always attempt to convert
521 from CF_UNICODETEXT or CF_TEXT */
522 else if( wFormat == CF_OEMTEXT && !ClipFormats[CF_OEMTEXT-1].wDataPresent )
524 if(ClipFormats[CF_UNICODETEXT-1].wDataPresent)
526 /* Convert UNICODETEXT -> OEMTEXT */
527 lpSource = &ClipFormats[CF_UNICODETEXT-1];
528 lpTarget = &ClipFormats[CF_OEMTEXT-1];
529 foundData = TRUE;
530 TRACE("\tUNICODETEXT -> OEMTEXT\n");
532 else if(ClipFormats[CF_TEXT-1].wDataPresent)
534 /* Convert TEXT -> OEMTEXT */
535 lpSource = &ClipFormats[CF_TEXT-1];
536 lpTarget = &ClipFormats[CF_OEMTEXT-1];
537 foundData = TRUE;
538 TRACE("\tTEXT -> OEMTEXT\n");
541 /* Asked for CF_UNICODETEXT but not available - always attempt to convert
542 from CF_TEXT or CF_OEMTEXT */
543 else if( wFormat == CF_UNICODETEXT && !ClipFormats[CF_UNICODETEXT-1].wDataPresent )
545 if(ClipFormats[CF_TEXT-1].wDataPresent)
547 /* Convert TEXT -> UNICODETEXT */
548 lpSource = &ClipFormats[CF_TEXT-1];
549 lpTarget = &ClipFormats[CF_UNICODETEXT-1];
550 foundData = TRUE;
551 TRACE("\tTEXT -> UNICODETEXT\n");
553 else if(ClipFormats[CF_OEMTEXT-1].wDataPresent)
555 /* Convert OEMTEXT -> UNICODETEXT */
556 lpSource = &ClipFormats[CF_OEMTEXT-1];
557 lpTarget = &ClipFormats[CF_UNICODETEXT-1];
558 foundData = TRUE;
559 TRACE("\tOEMTEXT -> UNICODETEXT\n");
562 if (!foundData)
564 if ((wFormat == CF_TEXT) || (wFormat == CF_OEMTEXT))
566 lpSource = &ClipFormats[CF_UNICODETEXT-1];
567 lpTarget = __lookup_format( ClipFormats, wFormat );
569 else
571 lpSource = __lookup_format( ClipFormats, wFormat );
572 lpTarget = lpSource;
576 /* First render the source text format */
577 if ( !lpSource || !CLIPBOARD_RenderFormat(lpSource) ) return NULL;
579 /* Convert to the desired target text format, if necessary */
580 if( lpTarget != lpSource && !lpTarget->hData16 && !lpTarget->hData32 )
582 INT src_chars, dst_chars, alloc_size;
583 LPCSTR lpstrS;
584 LPSTR lpstrT;
586 if (lpSource->hData32)
588 lpstrS = (LPSTR)GlobalLock(lpSource->hData32);
590 else
592 lpstrS = (LPSTR)GlobalLock16(lpSource->hData16);
595 if( !lpstrS ) return NULL;
597 /* Text always NULL terminated */
598 if(lpSource->wFormatID == CF_UNICODETEXT)
599 src_chars = strlenW((LPCWSTR)lpstrS);
600 else
601 src_chars = strlen(lpstrS);
603 /* Calculate number of characters in the destination buffer */
604 dst_chars = CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars,
605 lpTarget->wFormatID, NULL, 0);
606 if(!dst_chars) return NULL;
608 TRACE("\tconverting from '%s' to '%s', %i chars\n",
609 lpSource->Name, lpTarget->Name, src_chars);
611 /* Convert characters to bytes */
612 if(lpTarget->wFormatID == CF_UNICODETEXT)
613 alloc_size = dst_chars * sizeof(WCHAR);
614 else
615 alloc_size = dst_chars;
617 lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, alloc_size);
618 lpstrT = (LPSTR)GlobalLock(lpTarget->hData32);
620 if( lpstrT )
622 CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars,
623 lpTarget->wFormatID, lpstrT, dst_chars);
624 GlobalUnlock(lpTarget->hData32);
626 else
627 lpTarget->hData32 = 0;
629 /* Unlock source */
630 if (lpSource->hData32)
631 GlobalUnlock(lpSource->hData32);
632 else
633 GlobalUnlock16(lpSource->hData16);
636 return (lpTarget->hData16 || lpTarget->hData32) ? lpTarget : NULL;
639 /**************************************************************************
640 * CLIPBOARD_EnumClipboardFormats (internal)
642 static UINT CLIPBOARD_EnumClipboardFormats( UINT wFormat )
644 LPWINE_CLIPFORMAT lpFormat = ClipFormats;
645 BOOL bFormatPresent;
647 if (wFormat == 0) /* start from the beginning */
648 lpFormat = ClipFormats;
649 else
651 /* walk up to the specified format record */
653 if( !(lpFormat = __lookup_format( lpFormat, wFormat )) )
654 return 0;
655 lpFormat = lpFormat->NextFormat; /* right */
658 while(TRUE)
660 if (lpFormat == NULL) return 0;
662 if(CLIPBOARD_IsPresent(lpFormat->wFormatID))
663 break;
665 /* Query the driver if not yet in the cache */
666 if (!USER_Driver.pIsSelectionOwner())
668 if(lpFormat->wFormatID == CF_UNICODETEXT ||
669 lpFormat->wFormatID == CF_TEXT ||
670 lpFormat->wFormatID == CF_OEMTEXT)
672 if(USER_Driver.pIsClipboardFormatAvailable(CF_UNICODETEXT) ||
673 USER_Driver.pIsClipboardFormatAvailable(CF_TEXT) ||
674 USER_Driver.pIsClipboardFormatAvailable(CF_OEMTEXT))
675 bFormatPresent = TRUE;
676 else
677 bFormatPresent = FALSE;
679 else
680 bFormatPresent = USER_Driver.pIsClipboardFormatAvailable(lpFormat->wFormatID);
682 if(bFormatPresent)
683 break;
686 lpFormat = lpFormat->NextFormat;
689 TRACE("Next available format %d\n", lpFormat->wFormatID);
691 return lpFormat->wFormatID;
695 /**************************************************************************
696 * WIN32 Clipboard implementation
697 **************************************************************************/
699 /**************************************************************************
700 * OpenClipboard (USER.137)
702 BOOL16 WINAPI OpenClipboard16( HWND16 hWnd )
704 return OpenClipboard( hWnd );
708 /**************************************************************************
709 * OpenClipboard (USER32.@)
711 * Note: Netscape uses NULL hWnd to open the clipboard.
713 BOOL WINAPI OpenClipboard( HWND hWnd )
715 BOOL bRet;
717 TRACE("(%04x)...\n", hWnd);
719 if (!hClipLock)
721 hClipLock = GetCurrentTask();
723 /* Save current user of the clipboard */
724 hWndClipWindow = hWnd;
725 bCBHasChanged = FALSE;
726 bRet = TRUE;
728 else bRet = FALSE;
730 TRACE(" returning %i\n", bRet);
731 return bRet;
735 /**************************************************************************
736 * CloseClipboard (USER.138)
738 BOOL16 WINAPI CloseClipboard16(void)
740 return CloseClipboard();
744 /**************************************************************************
745 * CloseClipboard (USER32.@)
747 BOOL WINAPI CloseClipboard(void)
749 TRACE("()\n");
751 if (hClipLock == GetCurrentTask())
753 hWndClipWindow = 0;
755 if (bCBHasChanged && hWndViewer)
756 SendMessage16(hWndViewer, WM_DRAWCLIPBOARD, 0, 0L);
757 hClipLock = 0;
759 return TRUE;
763 /**************************************************************************
764 * EmptyClipboard16 (USER.139)
766 BOOL16 WINAPI EmptyClipboard16(void)
768 return EmptyClipboard();
772 /**************************************************************************
773 * EmptyClipboard (USER32.@)
774 * Empties and acquires ownership of the clipboard
776 BOOL WINAPI EmptyClipboard(void)
778 TRACE("()\n");
780 if (hClipLock != GetCurrentTask())
782 WARN("Clipboard not opened by calling task!");
783 return FALSE;
786 /* destroy private objects */
788 if (hWndClipOwner)
789 SendMessage16(hWndClipOwner, WM_DESTROYCLIPBOARD, 0, 0L);
791 /* empty the cache */
792 CLIPBOARD_EmptyCache(TRUE);
794 /* Assign ownership of the clipboard to the current client */
795 hWndClipOwner = hWndClipWindow;
797 /* Save the current task */
798 hTaskClipOwner = GetCurrentTask();
800 /* Tell the driver to acquire the selection */
801 USER_Driver.pAcquireClipboard();
803 return TRUE;
807 /**************************************************************************
808 * GetClipboardOwner (USER.140)
809 * FIXME: Can't return the owner if the clipbard is owned by an external app
811 HWND16 WINAPI GetClipboardOwner16(void)
813 TRACE("()\n");
814 return hWndClipOwner;
818 /**************************************************************************
819 * GetClipboardOwner (USER32.@)
820 * FIXME: Can't return the owner if the clipbard is owned by an external app
822 HWND WINAPI GetClipboardOwner(void)
824 TRACE("()\n");
825 return hWndClipOwner;
829 /**************************************************************************
830 * SetClipboardData (USER.141)
832 HANDLE16 WINAPI SetClipboardData16( UINT16 wFormat, HANDLE16 hData )
834 LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
836 TRACE("(%04X, %04x) !\n", wFormat, hData);
838 /* NOTE: If the hData is zero and current owner doesn't match
839 * the window that opened the clipboard then this application
840 * is screwed because WM_RENDERFORMAT will go to the owner.
841 * (to become the owner it must call EmptyClipboard() before
842 * adding new data).
845 if( CLIPBOARD_IsLocked() || !lpFormat ||
846 (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) )
848 WARN("Invalid hData or clipboard not opened by calling task!\n");
849 return 0;
852 /* Pass on the request to the driver */
853 USER_Driver.pSetClipboardData(wFormat);
855 if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 )
857 CLIPBOARD_DeleteRecord(lpFormat, TRUE);
859 /* delete existing CF_UNICODETEXT/CF_TEXT/CF_OEMTEXT aliases */
860 if(wFormat == CF_UNICODETEXT)
862 CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
863 CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
865 else if(wFormat == CF_TEXT)
867 CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
868 CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
870 else if(wFormat == CF_OEMTEXT)
872 CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
873 CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
877 bCBHasChanged = TRUE;
878 lpFormat->wDataPresent = 1;
879 lpFormat->hData16 = hData; /* 0 is legal, see WM_RENDERFORMAT */
880 lpFormat->hData32 = 0;
882 return lpFormat->hData16;
886 /**************************************************************************
887 * SetClipboardData (USER32.@)
889 HANDLE WINAPI SetClipboardData( UINT wFormat, HANDLE hData )
891 LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
893 TRACE("(%08X, %08x) !\n", wFormat, hData);
895 /* NOTE: If the hData is zero and current owner doesn't match
896 * the window that opened the clipboard then this application
897 * is screwed because WM_RENDERFORMAT will go to the owner.
898 * (to become the owner it must call EmptyClipboard() before
899 * adding new data).
902 if( CLIPBOARD_IsLocked() || !lpFormat ||
903 (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) )
905 WARN("Invalid hData or clipboard not opened by calling task!\n");
906 return 0;
909 /* Tell the driver to acquire the selection */
910 USER_Driver.pAcquireClipboard();
912 if ( lpFormat->wDataPresent &&
913 (lpFormat->hData16 || lpFormat->hData32) )
915 CLIPBOARD_DeleteRecord(lpFormat, TRUE);
917 /* delete existing CF_UNICODETEXT/CF_TEXT/CF_OEMTEXT aliases */
918 if(wFormat == CF_UNICODETEXT)
920 CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
921 CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
923 else if(wFormat == CF_TEXT)
925 CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
926 CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
928 else if(wFormat == CF_OEMTEXT)
930 CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
931 CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
935 bCBHasChanged = TRUE;
936 lpFormat->wDataPresent = 1;
937 lpFormat->hDataSrc32 = hData; /* Save the source handle */
940 * Make a shared duplicate if the memory is not shared
941 * TODO: What should be done for non-memory objects
943 if ( CLIPBOARD_IsMemoryObject(wFormat) && hData && !(GlobalFlags(hData) & GMEM_DDESHARE) )
944 lpFormat->hData32 = CLIPBOARD_GlobalDupMem( hData );
945 else
946 lpFormat->hData32 = hData; /* 0 is legal, see WM_RENDERFORMAT */
948 lpFormat->hData16 = 0;
950 return lpFormat->hData32; /* Should we return lpFormat->hDataSrc32 */
954 /**************************************************************************
955 * GetClipboardData (USER.142)
957 HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat )
959 LPWINE_CLIPFORMAT lpRender = ClipFormats;
961 TRACE("(%04X)\n", wFormat);
963 if (CLIPBOARD_IsLocked())
965 WARN("Clipboard not opened by calling task!\n");
966 return 0;
969 if( wFormat == CF_UNICODETEXT || wFormat == CF_TEXT || wFormat == CF_OEMTEXT )
971 lpRender = CLIPBOARD_RenderText(wFormat);
972 if ( !lpRender ) return 0;
974 else
976 lpRender = __lookup_format( ClipFormats, wFormat );
977 if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0;
980 /* Convert between 32 -> 16 bit data, if necessary */
981 if( lpRender->hData32 && !lpRender->hData16
982 && CLIPBOARD_IsMemoryObject(wFormat) )
984 int size;
985 if( lpRender->wFormatID == CF_METAFILEPICT )
986 size = sizeof( METAFILEPICT16 );
987 else
988 size = GlobalSize(lpRender->hData32);
990 lpRender->hData16 = GlobalAlloc16(GMEM_ZEROINIT, size);
991 if( !lpRender->hData16 )
992 ERR("(%04X) -- not enough memory in 16b heap\n", wFormat);
993 else
995 if( lpRender->wFormatID == CF_METAFILEPICT )
997 FIXME("\timplement function CopyMetaFilePict32to16\n");
998 FIXME("\tin the appropriate file.\n");
999 #ifdef SOMEONE_IMPLEMENTED_ME
1000 CopyMetaFilePict32to16( GlobalLock16(lpRender->hData16),
1001 GlobalLock(lpRender->hData32) );
1002 #endif
1004 else
1006 memcpy( GlobalLock16(lpRender->hData16),
1007 GlobalLock(lpRender->hData32),
1008 size );
1010 GlobalUnlock16(lpRender->hData16);
1011 GlobalUnlock(lpRender->hData32);
1015 TRACE("\treturning %04x (type %i)\n",
1016 lpRender->hData16, lpRender->wFormatID);
1017 return lpRender->hData16;
1021 /**************************************************************************
1022 * GetClipboardData (USER32.@)
1024 HANDLE WINAPI GetClipboardData( UINT wFormat )
1026 LPWINE_CLIPFORMAT lpRender = ClipFormats;
1028 TRACE("(%08X)\n", wFormat);
1030 if (CLIPBOARD_IsLocked())
1032 WARN("Clipboard not opened by calling task!");
1033 return 0;
1036 if( wFormat == CF_UNICODETEXT || wFormat == CF_TEXT || wFormat == CF_OEMTEXT )
1038 lpRender = CLIPBOARD_RenderText(wFormat);
1039 if ( !lpRender ) return 0;
1041 else
1043 lpRender = __lookup_format( ClipFormats, wFormat );
1044 if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0;
1047 /* Convert between 16 -> 32 bit data, if necessary */
1048 if( lpRender->hData16 && !lpRender->hData32
1049 && CLIPBOARD_IsMemoryObject(wFormat) )
1051 int size;
1052 if( lpRender->wFormatID == CF_METAFILEPICT )
1053 size = sizeof( METAFILEPICT );
1054 else
1055 size = GlobalSize16(lpRender->hData16);
1056 lpRender->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE,
1057 size);
1058 if( lpRender->wFormatID == CF_METAFILEPICT )
1060 FIXME("\timplement function CopyMetaFilePict16to32\n");
1061 FIXME("\tin the appropriate file.\n");
1062 #ifdef SOMEONE_IMPLEMENTED_ME
1063 CopyMetaFilePict16to32( GlobalLock16(lpRender->hData32),
1064 GlobalLock(lpRender->hData16) );
1065 #endif
1067 else
1069 memcpy( GlobalLock(lpRender->hData32),
1070 GlobalLock16(lpRender->hData16),
1071 size );
1073 GlobalUnlock(lpRender->hData32);
1074 GlobalUnlock16(lpRender->hData16);
1077 TRACE("\treturning %04x (type %i)\n",
1078 lpRender->hData32, lpRender->wFormatID);
1079 return lpRender->hData32;
1083 /**************************************************************************
1084 * CountClipboardFormats (USER.143)
1086 INT16 WINAPI CountClipboardFormats16(void)
1088 return CountClipboardFormats();
1092 /**************************************************************************
1093 * CountClipboardFormats (USER32.@)
1095 INT WINAPI CountClipboardFormats(void)
1097 INT FormatCount = 0;
1098 LPWINE_CLIPFORMAT lpFormat = ClipFormats;
1100 TRACE("()\n");
1102 while(TRUE)
1104 if (lpFormat == NULL) break;
1106 if( lpFormat->wFormatID != CF_TEXT ) /* Don't count CF_TEXT */
1109 * The format is available if either:
1110 * 1. The data is already in the cache.
1111 * 2. The selection is not owned by us(WINE) and the data is
1112 * available to the clipboard driver.
1114 if ( lpFormat->wDataPresent ||
1115 ( !USER_Driver.pIsSelectionOwner()
1116 && USER_Driver.pIsClipboardFormatAvailable( lpFormat->wFormatID ) ) )
1118 TRACE("\tdata found for format 0x%04x(%s)\n",
1119 lpFormat->wFormatID, CLIPBOARD_GetFormatName(lpFormat->wFormatID));
1120 FormatCount++;
1124 lpFormat = lpFormat->NextFormat;
1127 /* these are equivalent, adjust the total */
1128 FormatCount += (ClipFormats[CF_UNICODETEXT-1].wDataPresent ||
1129 ClipFormats[CF_TEXT-1].wDataPresent ||
1130 ClipFormats[CF_OEMTEXT-1].wDataPresent) ? 1 : 0;
1132 TRACE("\ttotal %d\n", FormatCount);
1133 return FormatCount;
1136 /**************************************************************************
1137 * EnumClipboardFormats (USER.144)
1139 UINT16 WINAPI EnumClipboardFormats16( UINT16 wFormat )
1141 return EnumClipboardFormats( wFormat );
1145 /**************************************************************************
1146 * EnumClipboardFormats (USER32.@)
1148 UINT WINAPI EnumClipboardFormats( UINT wFormat )
1150 TRACE("(%04X)\n", wFormat);
1152 if (CLIPBOARD_IsLocked())
1154 WARN("Clipboard not opened by calling task!");
1155 return 0;
1158 return CLIPBOARD_EnumClipboardFormats(wFormat);
1162 /**************************************************************************
1163 * RegisterClipboardFormat (USER.145)
1165 UINT16 WINAPI RegisterClipboardFormat16( LPCSTR FormatName )
1167 LPWINE_CLIPFORMAT lpNewFormat;
1168 LPWINE_CLIPFORMAT lpFormat = ClipFormats;
1170 if (FormatName == NULL) return 0;
1172 TRACE("('%s') !\n", FormatName);
1174 /* walk format chain to see if it's already registered */
1176 while(TRUE)
1178 if ( !strcmp(lpFormat->Name,FormatName) )
1180 lpFormat->wRefCount++;
1181 return lpFormat->wFormatID;
1184 if ( lpFormat->NextFormat == NULL ) break;
1186 lpFormat = lpFormat->NextFormat;
1189 /* allocate storage for new format entry */
1191 lpNewFormat = (LPWINE_CLIPFORMAT)HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_CLIPFORMAT));
1192 if(lpNewFormat == NULL) {
1193 WARN("No more memory for a new format!");
1194 return 0;
1196 lpFormat->NextFormat = lpNewFormat;
1197 lpNewFormat->wFormatID = LastRegFormat;
1198 lpNewFormat->wRefCount = 1;
1200 lpNewFormat->Name = (LPSTR)HEAP_strdupA(GetProcessHeap(), 0, FormatName);
1201 if(lpNewFormat->Name == NULL) {
1202 WARN("No more memory for the new format name!");
1203 HeapFree(GetProcessHeap(), 0, lpNewFormat);
1204 return 0;
1207 lpNewFormat->wDataPresent = 0;
1208 lpNewFormat->hData16 = 0;
1209 lpNewFormat->hDataSrc32 = 0;
1210 lpNewFormat->hData32 = 0;
1211 lpNewFormat->drvData = 0;
1212 lpNewFormat->PrevFormat = lpFormat;
1213 lpNewFormat->NextFormat = NULL;
1215 /* Pass on the registration request to the driver */
1216 USER_Driver.pRegisterClipboardFormat( FormatName );
1218 return LastRegFormat++;
1222 /**************************************************************************
1223 * RegisterClipboardFormatA (USER32.@)
1225 UINT WINAPI RegisterClipboardFormatA( LPCSTR formatName )
1227 return RegisterClipboardFormat16( formatName );
1231 /**************************************************************************
1232 * RegisterClipboardFormatW (USER32.@)
1234 UINT WINAPI RegisterClipboardFormatW( LPCWSTR formatName )
1236 LPSTR aFormat = HEAP_strdupWtoA( GetProcessHeap(), 0, formatName );
1237 UINT ret = RegisterClipboardFormatA( aFormat );
1238 HeapFree( GetProcessHeap(), 0, aFormat );
1239 return ret;
1243 /**************************************************************************
1244 * GetClipboardFormatName (USER.146)
1246 INT16 WINAPI GetClipboardFormatName16( UINT16 wFormat, LPSTR retStr, INT16 maxlen )
1248 return GetClipboardFormatNameA( wFormat, retStr, maxlen );
1252 /**************************************************************************
1253 * GetClipboardFormatNameA (USER32.@)
1255 INT WINAPI GetClipboardFormatNameA( UINT wFormat, LPSTR retStr, INT maxlen )
1257 LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
1259 TRACE("(%04X, %p, %d) !\n", wFormat, retStr, maxlen);
1261 if (lpFormat == NULL || lpFormat->Name == NULL ||
1262 lpFormat->wFormatID < CF_REGFORMATBASE) return 0;
1264 TRACE("Name='%s' !\n", lpFormat->Name);
1266 lstrcpynA( retStr, lpFormat->Name, maxlen );
1267 return strlen(retStr);
1271 /**************************************************************************
1272 * GetClipboardFormatNameW (USER32.@)
1274 INT WINAPI GetClipboardFormatNameW( UINT wFormat, LPWSTR retStr, INT maxlen )
1276 INT ret;
1277 LPSTR p = HeapAlloc( GetProcessHeap(), 0, maxlen );
1278 if(p == NULL) return 0; /* FIXME: is this the correct failure value? */
1280 ret = GetClipboardFormatNameA( wFormat, p, maxlen );
1282 if (maxlen > 0 && !MultiByteToWideChar( CP_ACP, 0, p, -1, retStr, maxlen ))
1283 retStr[maxlen-1] = 0;
1284 HeapFree( GetProcessHeap(), 0, p );
1285 return ret;
1289 /**************************************************************************
1290 * SetClipboardViewer (USER.147)
1292 HWND16 WINAPI SetClipboardViewer16( HWND16 hWnd )
1294 TRACE("(%04x)\n", hWnd);
1295 return SetClipboardViewer( hWnd );
1299 /**************************************************************************
1300 * SetClipboardViewer (USER32.@)
1302 HWND WINAPI SetClipboardViewer( HWND hWnd )
1304 HWND hwndPrev = hWndViewer;
1306 TRACE("(%04x): returning %04x\n", hWnd, hwndPrev);
1308 hWndViewer = hWnd;
1309 return hwndPrev;
1313 /**************************************************************************
1314 * GetClipboardViewer (USER.148)
1316 HWND16 WINAPI GetClipboardViewer16(void)
1318 TRACE("()\n");
1319 return hWndViewer;
1323 /**************************************************************************
1324 * GetClipboardViewer (USER32.@)
1326 HWND WINAPI GetClipboardViewer(void)
1328 TRACE("()\n");
1329 return hWndViewer;
1333 /**************************************************************************
1334 * ChangeClipboardChain (USER.149)
1336 BOOL16 WINAPI ChangeClipboardChain16(HWND16 hWnd, HWND16 hWndNext)
1338 return ChangeClipboardChain(hWnd, hWndNext);
1342 /**************************************************************************
1343 * ChangeClipboardChain (USER32.@)
1345 BOOL WINAPI ChangeClipboardChain(HWND hWnd, HWND hWndNext)
1347 BOOL bRet = 0;
1349 FIXME("(0x%04x, 0x%04x): stub?\n", hWnd, hWndNext);
1351 if( hWndViewer )
1352 bRet = !SendMessage16( hWndViewer, WM_CHANGECBCHAIN,
1353 (WPARAM16)hWnd, (LPARAM)hWndNext);
1354 else
1355 WARN("hWndViewer is lost\n");
1357 if( hWnd == hWndViewer ) hWndViewer = hWndNext;
1359 return bRet;
1363 /**************************************************************************
1364 * IsClipboardFormatAvailable16 (USER.193)
1366 BOOL16 WINAPI IsClipboardFormatAvailable16( UINT16 wFormat )
1368 return IsClipboardFormatAvailable( wFormat );
1372 /**************************************************************************
1373 * IsClipboardFormatAvailable (USER32.@)
1375 BOOL WINAPI IsClipboardFormatAvailable( UINT wFormat )
1377 BOOL bRet;
1379 if (wFormat == 0) /* Reject this case quickly */
1380 bRet = FALSE;
1381 else
1383 UINT iret = CLIPBOARD_EnumClipboardFormats(wFormat - 1);
1384 if ((wFormat == CF_TEXT) || (wFormat == CF_OEMTEXT) || (wFormat == CF_UNICODETEXT))
1385 bRet = ((iret == CF_TEXT) || (iret == CF_OEMTEXT) || (iret == CF_UNICODETEXT));
1386 else
1387 bRet = iret == wFormat;
1389 TRACE("(%04X)- ret(%d)\n", wFormat, bRet);
1390 return bRet;
1394 /**************************************************************************
1395 * GetOpenClipboardWindow (USER.248)
1396 * FIXME: This wont work if an external app owns the selection
1398 HWND16 WINAPI GetOpenClipboardWindow16(void)
1400 TRACE("()\n");
1401 return hWndClipWindow;
1405 /**************************************************************************
1406 * GetOpenClipboardWindow (USER32.@)
1407 * FIXME: This wont work if an external app owns the selection
1409 HWND WINAPI GetOpenClipboardWindow(void)
1411 TRACE("()\n");
1412 return hWndClipWindow;
1416 /**************************************************************************
1417 * GetPriorityClipboardFormat (USER.402)
1419 INT16 WINAPI GetPriorityClipboardFormat16( UINT16 *lpPriorityList, INT16 nCount)
1421 FIXME("(%p,%d): stub\n", lpPriorityList, nCount );
1422 return 0;
1426 /**************************************************************************
1427 * GetPriorityClipboardFormat (USER32.@)
1429 INT WINAPI GetPriorityClipboardFormat( UINT *lpPriorityList, INT nCount )
1431 int Counter;
1432 TRACE("()\n");
1434 if(CountClipboardFormats() == 0)
1436 return 0;
1439 for(Counter = 0; Counter <= nCount; Counter++)
1441 if(IsClipboardFormatAvailable(*(lpPriorityList+sizeof(INT)*Counter)))
1442 return *(lpPriorityList+sizeof(INT)*Counter);
1445 return -1;
1449 /**************************************************************************
1450 * GetClipboardSequenceNumber (USER32.@)
1451 * Supported on Win2k/Win98
1452 * MSDN: Windows clipboard code keeps a serial number for the clipboard
1453 * for each window station. The number is incremented whenever the
1454 * contents change or are emptied.
1455 * If you do not have WINSTA_ACCESSCLIPBOARD then the function returns 0
1457 DWORD WINAPI GetClipboardSequenceNumber(VOID)
1459 FIXME("Returning 0, see windows/clipboard.c\n");
1460 /* FIXME: Use serial numbers */
1461 return 0;