2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Clipboard routines
5 * FILE: subsys/win32k/ntuser/clipboard.c
6 * PROGRAMER: Filip Navara <xnavara@volny.cz>
7 * Pablo Borobia <pborobia@gmail.com>
15 #define DATA_DELAYED_RENDER 0
16 #define DATA_SYNTHESIZED_RENDER -1
18 PTHREADINFO ClipboardThread
;
19 PTHREADINFO ClipboardOwnerThread
;
20 PWINDOW_OBJECT ClipboardWindow
;
21 PWINDOW_OBJECT ClipboardViewerWindow
;
22 PWINDOW_OBJECT ClipboardOwnerWindow
;
23 BOOL sendDrawClipboardMsg
;
24 BOOL recentlySetClipboard
;
26 UINT lastEnumClipboardFormats
;
27 DWORD ClipboardSequenceNumber
= 0;
29 PCLIPBOARDCHAINELEMENT WindowsChain
= NULL
;
30 PCLIPBOARDELEMENT ClipboardData
= NULL
;
32 PCHAR synthesizedData
;
33 DWORD synthesizedDataSize
;
36 /*==============================================================*/
38 /* return the pointer to the prev window of the finded window,
39 if NULL does not exists in the chain */
40 PCLIPBOARDCHAINELEMENT FASTCALL
41 IntIsWindowInChain(PWINDOW_OBJECT window
)
43 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
47 if (wce
->window
== window
)
57 VOID FASTCALL
printChain()
60 PCLIPBOARDCHAINELEMENT wce2
= WindowsChain
;
63 DPRINT1("chain: %p\n", wce2
->window
->hSelf
);
68 /* the new window always have to be the first in the chain */
69 PCLIPBOARDCHAINELEMENT FASTCALL
70 IntAddWindowToChain(PWINDOW_OBJECT window
)
72 PCLIPBOARDCHAINELEMENT wce
= NULL
;
74 if (!IntIsWindowInChain(window
))
78 wce
= ExAllocatePool(PagedPool
, sizeof(CLIPBOARDCHAINELEMENT
));
81 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
86 wce
->next
= WindowsChain
;
94 /* return the next window to beremoved later */
98 PCLIPBOARDCHAINELEMENT FASTCALL
99 IntRemoveWindowFromChain(PWINDOW_OBJECT window
)
101 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
102 PCLIPBOARDCHAINELEMENT
*link
= &WindowsChain
;
104 if (IntIsWindowInChain(window
))
108 if (wce
->window
== window
)
129 /*==============================================================*/
130 /* if format exists, returns a non zero value (pointing to format object) */
131 PCLIPBOARDELEMENT FASTCALL
132 intIsFormatAvailable(format
)
134 PCLIPBOARDELEMENT ret
= NULL
;
135 PCLIPBOARDELEMENT ce
= ClipboardData
;
139 if (ce
->format
== format
)
149 /* counts how many distinct format were are in the clipboard */
151 IntCountClipboardFormats()
154 PCLIPBOARDELEMENT ce
= ClipboardData
;
164 /* adds a new format and data to the clipboard */
165 PCLIPBOARDELEMENT FASTCALL
166 intAddFormatedData(UINT format
, HANDLE hData
, DWORD size
)
168 PCLIPBOARDELEMENT ce
= NULL
;
170 ce
= ExAllocatePool(PagedPool
, sizeof(CLIPBOARDELEMENT
));
173 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
180 ce
->next
= ClipboardData
;
184 IntIncrementSequenceNumber();
190 /* removes a format and its data from the clipboard */
192 intRemoveFormatedData(UINT format
)
195 PCLIPBOARDELEMENT ce
= ClipboardData
;
196 PCLIPBOARDELEMENT
*link
= &ClipboardData
;
198 if (intIsFormatAvailable(format
))
202 if (ce
->format
== format
)
214 ExFreePool(ce
->hData
);
224 IntEmptyClipboardData()
226 PCLIPBOARDELEMENT ce
= ClipboardData
;
227 PCLIPBOARDELEMENT tmp
;
232 ExFreePool(ce
->hData
);
237 ClipboardData
= NULL
;
240 /*==============================================================*/
243 renderBITMAPfromDIB(LPBYTE hDIB
)
248 BITMAPINFOHEADER
*ih
;
250 //hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
251 hdc
= UserGetDCEx(ClipboardWindow
, NULL
, DCX_USESTYLE
);
253 ih
= (BITMAPINFOHEADER
*)hDIB
;
255 offset
= sizeof(BITMAPINFOHEADER
) + ((ih
->biBitCount
<= 8) ? (sizeof(RGBQUAD
) * (1 << ih
->biBitCount
)) : 0);
257 hbitmap
= NtGdiCreateDIBitmapInternal(hdc
,
268 //UserReleaseDC(NULL, hdc, FALSE);
269 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
275 canSinthesize(UINT format
)
282 case CF_METAFILEPICT
:
289 /* returns the size of the sinthesized data */
291 synthesizeData(UINT format
)
295 synthesizedData
= NULL
;
296 synthesizedDataSize
= 0;
298 if (!canSinthesize(format
))
310 case CF_METAFILEPICT
:
322 freeSynthesizedData()
324 ExFreePool(synthesizedData
);
327 /*==============================================================*/
330 intIsClipboardOpenByMe()
332 /* check if we open the clipboard */
333 if (ClipboardThread
&& ClipboardThread
== PsGetCurrentThreadWin32Thread())
335 /* yes, we got a thread and its the same that opens the clipboard */
339 /* will fail if not thread (closed) or not open by me*/
343 /* IntClipboardFreeWindow it's called when a window was destroyed */
345 IntClipboardFreeWindow(PWINDOW_OBJECT window
)
347 /* called from co_UserFreeWindow in window.c */
348 /* check if clipboard is not locked by this window, if yes, unlock it */
349 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
351 /* the window that opens the clipboard was destroyed */
352 ClipboardThread
= NULL
;
353 ClipboardWindow
= NULL
;
354 //TODO: free clipboard
356 if (window
== ClipboardOwnerWindow
)
358 /* the owner window was destroyed */
359 ClipboardOwnerWindow
= NULL
;
360 ClipboardOwnerThread
= NULL
;
362 /* remove window from window chain */
363 if (IntIsWindowInChain(window
))
365 PCLIPBOARDCHAINELEMENT w
= IntRemoveWindowFromChain(window
);
374 NtUserOpenClipboard(HWND hWnd
, DWORD Unknown1
)
377 PWINDOW_OBJECT Window
;
380 UserEnterExclusive();
382 sendDrawClipboardMsg
= FALSE
;
383 recentlySetClipboard
= FALSE
;
387 /* clipboard is already open */
388 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
390 if (ClipboardOwnerWindow
)
392 if (ClipboardOwnerWindow
->hSelf
== hWnd
)
411 Window
= UserGetWindowObject(hWnd
);
415 ClipboardWindow
= Window
;
416 ClipboardThread
= PsGetCurrentThreadWin32Thread();
421 ClipboardWindow
= NULL
;
422 ClipboardThread
= NULL
;
423 ClipboardOwnerWindow
= NULL
;
424 ClipboardOwnerThread
= NULL
;
429 ClipboardWindow
= NULL
;
430 ClipboardThread
= PsGetCurrentThreadWin32Thread();
441 NtUserCloseClipboard(VOID
)
445 UserEnterExclusive();
447 if (intIsClipboardOpenByMe())
449 ClipboardWindow
= NULL
;
450 ClipboardThread
= NULL
;
455 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
458 recentlySetClipboard
= FALSE
;
462 if (sendDrawClipboardMsg
&& WindowsChain
)
464 /* only send message to the first window in the chain, then they'll do the chain */
465 /* commented because it makes a crash in co_MsqSendMessage
466 ASSERT(WindowsChain->window);
467 ASSERT(WindowsChain->window->hSelf);
468 DPRINT1("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", WindowsChain->window->hSelf);
469 co_IntSendMessage(WindowsChain->window->hSelf, WM_DRAWCLIPBOARD, 0, 0);
477 NtUserGetOpenClipboardWindow(VOID
)
485 ret
= ClipboardWindow
->hSelf
;
494 NtUserChangeClipboardChain(HWND hWndRemove
, HWND hWndNewNext
)
497 PCLIPBOARDCHAINELEMENT w
= NULL
;
498 PWINDOW_OBJECT removeWindow
;
499 UserEnterExclusive();
501 removeWindow
= UserGetWindowObject(hWndRemove
);
505 if ((ret
= !!IntIsWindowInChain(removeWindow
)))
507 w
= IntRemoveWindowFromChain(removeWindow
);
515 if (ret
&& WindowsChain
)
517 // only send message to the first window in the chain,
518 // then they do the chain
520 /* WindowsChain->window may be NULL */
521 LPARAM lparam
= WindowsChain
->window
== NULL
? 0 : (LPARAM
)WindowsChain
->window
->hSelf
;
522 DPRINT1("Message: WM_CHANGECBCHAIN to %p", WindowsChain
->window
->hSelf
);
523 co_IntSendMessage(WindowsChain
->window
->hSelf
, WM_CHANGECBCHAIN
, (WPARAM
)hWndRemove
, lparam
);
532 NtUserCountClipboardFormats(VOID
)
538 ret
= IntCountClipboardFormats();
545 NtUserEmptyClipboard(VOID
)
549 UserEnterExclusive();
551 if (intIsClipboardOpenByMe())
555 IntEmptyClipboardData();
558 ClipboardOwnerWindow
= ClipboardWindow
;
559 ClipboardOwnerThread
= ClipboardThread
;
561 IntIncrementSequenceNumber();
567 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
570 if (ret
&& ClipboardOwnerWindow
)
572 DPRINT("Clipboard: WM_DESTROYCLIPBOARD to %p", ClipboardOwnerWindow
->hSelf
);
573 co_IntSendMessage( ClipboardOwnerWindow
->hSelf
, WM_DESTROYCLIPBOARD
, 0, 0);
582 NtUserGetClipboardData(UINT uFormat
, PVOID pBuffer
)
588 if (intIsClipboardOpenByMe())
590 /* when Unknown1 is zero, we returns to user32 the data size */
593 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
597 /* format exists in clipboard */
598 if (data
->size
== DATA_DELAYED_RENDER
)
600 /* tell owner what data needs to be rendered */
601 if (ClipboardOwnerWindow
)
603 ASSERT(ClipboardOwnerWindow
->hSelf
);
604 co_IntSendMessage(ClipboardOwnerWindow
->hSelf
, WM_RENDERFORMAT
, (WPARAM
)uFormat
, 0);
605 data
= intIsFormatAvailable(uFormat
);
607 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
612 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
614 data
->size
= synthesizeData(uFormat
);
618 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
622 /* there is no data in this format */
623 //ret = (HANDLE)FALSE;
628 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
632 if (data
->size
== DATA_DELAYED_RENDER
)
634 // we rendered it in 1st call of getclipboard data
638 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
640 if (uFormat
== CF_BITMAP
)
642 /* BITMAP & METAFILEs returns a GDI handle */
643 PCLIPBOARDELEMENT data
= intIsFormatAvailable(CF_DIB
);
646 ret
= renderBITMAPfromDIB(data
->hData
);
651 ret
= (HANDLE
)pBuffer
;
655 ProbeForWrite(pBuffer
, synthesizedDataSize
, 1);
656 memcpy(pBuffer
, (PCHAR
)synthesizedData
, synthesizedDataSize
);
664 freeSynthesizedData();
669 ret
= (HANDLE
)pBuffer
;
673 ProbeForWrite(pBuffer
, data
->size
, 1);
674 memcpy(pBuffer
, (PCHAR
)data
->hData
, data
->size
);
690 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
699 NtUserGetClipboardFormatName(UINT format
, PUNICODE_STRING FormatName
,
702 UNICODE_STRING sFormatName
;
705 /* if the format is built-in we fail */
708 /* registetrated formats are >= 0xc000 */
712 if((cchMaxCount
< 1) || !FormatName
)
714 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
720 ProbeForWriteUnicodeString(FormatName
);
721 sFormatName
= *(volatile UNICODE_STRING
*)FormatName
;
722 ProbeForWrite(sFormatName
.Buffer
, sFormatName
.MaximumLength
, 1);
724 ret
= IntGetAtomName((RTL_ATOM
)format
, sFormatName
.Buffer
, cchMaxCount
* sizeof(WCHAR
));
728 ret
= ret
/ sizeof(WCHAR
);
729 sFormatName
.Length
= ret
;
738 SetLastNtError(_SEH_GetExceptionCode());
746 NtUserGetClipboardOwner(VOID
)
752 if (ClipboardOwnerWindow
)
754 ret
= ClipboardOwnerWindow
->hSelf
;
763 NtUserGetClipboardViewer(VOID
)
771 ret
= WindowsChain
->window
->hSelf
;
780 NtUserGetPriorityClipboardFormat(UINT
*paFormatPriorityList
, INT cFormats
)
786 UserEnterExclusive();
790 if (IntCountClipboardFormats() == 0)
796 ProbeForRead(paFormatPriorityList
, cFormats
, sizeof(UINT
));
798 priorityList
= paFormatPriorityList
;
802 for (i
= 0; i
< cFormats
; i
++)
804 if (intIsFormatAvailable(priorityList
[i
]))
806 ret
= priorityList
[i
];
815 SetLastNtError(_SEH_GetExceptionCode());
826 NtUserIsClipboardFormatAvailable(UINT format
)
832 ret
= (intIsFormatAvailable(format
) != NULL
);
842 NtUserSetClipboardData(UINT uFormat
, HANDLE hMem
, DWORD size
)
844 HANDLE hCBData
= NULL
;
845 UNICODE_STRING unicodeString
;
846 OEM_STRING oemString
;
847 ANSI_STRING ansiString
;
849 UserEnterExclusive();
851 /* to place data here the we need to be the owner */
852 if (ClipboardOwnerThread
== PsGetCurrentThreadWin32Thread())
854 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
858 if (data
->size
== DATA_DELAYED_RENDER
)
860 intRemoveFormatedData(uFormat
);
864 // we already have this format on clipboard
873 ProbeForRead(hMem
, size
, 1);
877 SetLastNtError(_SEH_GetExceptionCode());
878 _SEH_YIELD(goto exit_setCB
);
882 if (intIsClipboardOpenByMe())
884 delayedRender
= FALSE
;
887 if (!canSinthesize(uFormat
))
889 hCBData
= ExAllocatePool(PagedPool
, size
);
890 memcpy(hCBData
, hMem
, size
);
891 intAddFormatedData(uFormat
, hCBData
, size
);
892 DPRINT1("Data stored\n");
895 sendDrawClipboardMsg
= TRUE
;
896 recentlySetClipboard
= TRUE
;
897 lastEnumClipboardFormats
= uFormat
;
904 //TODO : sinthesize CF_UNICODETEXT & CF_OEMTEXT
905 // CF_TEXT -> CF_UNICODETEXT
906 ansiString
.Buffer
= hCBData
;
907 ansiString
.Length
= size
;
908 RtlAnsiStringToUnicodeString(&unicodeString
, &ansiString
, TRUE
);
909 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
910 // CF_TEXT -> CF_OEMTEXT
911 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
912 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
913 //HKCU\Control Panel\International\Locale
914 //intAddFormatedData(CF_LOCALE, oemString.Buffer, oemString.Length);
919 //TODO : sinthesize CF_TEXT & CF_OEMTEXT
920 //CF_UNICODETEXT -> CF_TEXT
921 unicodeString
.Buffer
= hCBData
;
922 unicodeString
.Length
= size
;
923 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
924 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
925 //CF_UNICODETEXT -> CF_OEMTEXT
926 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
927 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
932 //TODO : sinthesize CF_TEXT & CF_UNICODETEXT
933 //CF_OEMTEXT -> CF_UNICODETEXT
934 oemString
.Buffer
= hCBData
;
935 oemString
.Length
= size
;
936 RtlOemStringToUnicodeString(&unicodeString
, &oemString
, TRUE
);
937 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
938 //CF_OEMTEXT -> CF_TEXT
939 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
940 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
945 // we need to render the DIB or DIBV5 format as soon as possible
946 // because pallette information may change
952 BITMAPOBJ
*BitmapObj
;
954 hdc
= UserGetDCEx(NULL
, NULL
, DCX_USESTYLE
);
957 BitmapObj
= BITMAPOBJ_LockBitmap(hMem
);
958 BITMAP_GetObject(BitmapObj
, sizeof(BITMAP
), (LPSTR
)&bm
);
961 BITMAPOBJ_UnlockBitmap(BitmapObj
);
964 bi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
965 bi
.bmiHeader
.biWidth
= bm
.bmWidth
;
966 bi
.bmiHeader
.biHeight
= bm
.bmHeight
;
967 bi
.bmiHeader
.biPlanes
= 1;
968 bi
.bmiHeader
.biBitCount
= bm
.bmPlanes
* bm
.bmBitsPixel
;
969 bi
.bmiHeader
.biCompression
= BI_RGB
;
970 bi
.bmiHeader
.biSizeImage
= 0;
971 bi
.bmiHeader
.biXPelsPerMeter
= 0;
972 bi
.bmiHeader
.biYPelsPerMeter
= 0;
973 bi
.bmiHeader
.biClrUsed
= 0;
975 ret
= NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, NULL
, &bi
, DIB_RGB_COLORS
, 0, 0);
977 size
= bi
.bmiHeader
.biSizeImage
+ sizeof(BITMAPINFOHEADER
);
979 hCBData
= ExAllocatePool(PagedPool
, size
);
980 memcpy(hCBData
, &bi
, sizeof(BITMAPINFOHEADER
));
982 ret
= NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, (LPBYTE
)hCBData
+ sizeof(BITMAPINFOHEADER
), &bi
, DIB_RGB_COLORS
, 0, 0);
984 UserReleaseDC(NULL
, hdc
, FALSE
);
986 intAddFormatedData(CF_DIB
, hCBData
, size
);
987 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
988 // intAddFormatedData(CF_DIBV5, hCBData, size);
994 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
995 // intAddFormatedData(CF_DIBV5, hCBData, size);
997 // intAddFormatedData(CF_PALETTE, hCBData, size);
1001 // intAddFormatedData(CF_BITMAP, hCBData, size);
1002 // intAddFormatedData(CF_PALETTE, hCBData, size);
1003 // intAddFormatedData(CF_DIB, hCBData, size);
1005 case CF_ENHMETAFILE
:
1006 // intAddFormatedData(CF_METAFILEPICT, hCBData, size);
1008 case CF_METAFILEPICT
:
1009 // intAddFormatedData(CF_ENHMETAFILE, hCBData, size);
1016 // the window provides data in the specified format
1017 delayedRender
= TRUE
;
1018 sendDrawClipboardMsg
= TRUE
;
1019 intAddFormatedData(uFormat
, NULL
, 0);
1020 DPRINT1("SetClipboardData delayed format: %d\n", uFormat
);
1034 NtUserSetClipboardViewer(HWND hWndNewViewer
)
1037 PCLIPBOARDCHAINELEMENT newWC
= NULL
;
1038 PWINDOW_OBJECT window
;
1040 UserEnterExclusive();
1042 window
= UserGetWindowObject(hWndNewViewer
);
1046 if ((newWC
= IntAddWindowToChain(window
)))
1050 // newWC->next may be NULL if we are the first window in the chain
1053 // return the next HWND available window in the chain
1054 ret
= newWC
->next
->window
->hSelf
;
1066 IntEnumClipboardFormats(UINT uFormat
)
1070 if (intIsClipboardOpenByMe())
1074 if (recentlySetClipboard
)
1076 ret
= lastEnumClipboardFormats
;
1080 /* return the first available format */
1083 ret
= ClipboardData
->format
;
1089 if (recentlySetClipboard
)
1095 /* querying nextt available format */
1096 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
1102 ret
= data
->next
->format
;
1106 /* reached the end */
1116 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
1122 // This number is incremented whenever the contents of the clipboard change
1123 // or the clipboard is emptied.
1124 // If clipboard rendering is delayed,
1125 // the sequence number is not incremented until the changes are rendered.
1127 IntIncrementSequenceNumber(VOID
)
1130 PWINSTATION_OBJECT WinStaObj
;
1132 pti
= PsGetCurrentThreadWin32Thread();
1133 WinStaObj
= pti
->Desktop
->WindowStation
;
1135 WinStaObj
->Clipboard
->ClipboardSequenceNumber
++;
1139 NtUserGetClipboardSequenceNumber(VOID
)
1141 //windowstation sequence number
1142 //if no WINSTA_ACCESSCLIPBOARD access to the window station,
1143 //the function returns zero.
1147 PWINSTATION_OBJECT WinStaObj
;
1150 WinSta
= UserGetProcessWindowStation();
1152 Status
= IntValidateWindowStationHandle(WinSta
, UserMode
, WINSTA_ACCESSCLIPBOARD
, &WinStaObj
);
1154 if (!NT_SUCCESS(Status
))
1156 DPRINT1("No WINSTA_ACCESSCLIPBOARD access\n");
1157 SetLastNtError(Status
);
1161 sn
= WinStaObj
->ClipboardSequenceNumber
;
1163 ObDereferenceObject(WinStaObj
);
1166 //sn = ClipboardSequenceNumber;
1172 /**************** VISTA FUNCTIONS******************/
1174 BOOL STDCALL
NtUserAddClipboardFormatListener(
1182 BOOL STDCALL
NtUserRemoveClipboardFormatListener(
1190 BOOL STDCALL
NtUserGetUpdatedClipboardFormats(