/****************************** Module Header ******************************\ * Module Name: clipbrd.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Clipboard code. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 18-Nov-1990 ScottLu Added revalidation code * 11-Feb-1991 JimA Added access checks * 20-Jun-1995 ChrisWil Merged Chicago functionality. \***************************************************************************/ #include "precomp.h" #pragma hdrstop #undef DUMMY_TEXT_HANDLE #define DUMMY_TEXT_HANDLE (HANDLE)0x0001 // must be first dummy #define DUMMY_DIB_HANDLE (HANDLE)0x0002 #define DUMMY_METARENDER_HANDLE (HANDLE)0x0003 #define DUMMY_METACLONE_HANDLE (HANDLE)0x0004 #define DUMMY_MAX_HANDLE (HANDLE)0x0004 // must be last dummy #define PRIVATEFORMAT 0 #define GDIFORMAT 1 #define HANDLEFORMAT 2 #define METAFILEFORMAT 3 #define CCHFORMATNAME 256 #define IsTextHandle(fmt, hdata) \ (((hdata) != DUMMY_TEXT_HANDLE) && \ (((fmt) == CF_TEXT) || ((fmt) == CF_OEMTEXT) || ((fmt) == CF_UNICODETEXT))) #define IsDibHandle(fmt, hdata) \ (((fmt) == CF_DIB) && ((hdata) != DUMMY_DIB_HANDLE)) #define IsMetaDummyHandle(hdata) \ ((hdata == DUMMY_METACLONE_HANDLE) || (hdata == DUMMY_METARENDER_HANDLE)) /**************************************************************************\ * CheckClipboardAccess * * Perform access check on the clipboard. Special case CSRSS threads * so that console windows on multiple windowstations will have * the correct access. * * 04-Jul-1995 JimA Created \**************************************************************************/ PWINDOWSTATION CheckClipboardAccess( VOID) { NTSTATUS Status; PWINDOWSTATION pwinsta; BOOL fUseDesktop; PTHREADINFO pti; pti = PtiCurrentShared(); /* * CSR process use to have NULL pwinsta. Now that it's assigned to * the services windowstation we have to explicitly use the desktop * for checking the access. */ fUseDesktop = (pti->TIF_flags & TIF_CSRSSTHREAD) ? TRUE : FALSE; Status = ReferenceWindowStation(PsGetCurrentThread(), NULL, WINSTA_ACCESSCLIPBOARD, &pwinsta, fUseDesktop); if (!NT_SUCCESS(Status)) { RIPNTERR0(Status, RIP_WARNING,"Access to clipboard denied."); return NULL; } return pwinsta; } /**************************************************************************\ * ConvertMemHandle * * Converts data to a clipboard-memory-handle. This special handle * contains the size-of-data in the first DWORD. The second DWORD points * back to the block. * * History: \**************************************************************************/ HANDLE _ConvertMemHandle( LPBYTE ccxlpData, UINT cbData) { PCLIPDATA pClipData; UINT cbObject; /* * Round up size to account for CLIPDATA structure padding on Win64. */ cbObject = max(sizeof(CLIPDATA), FIELD_OFFSET(CLIPDATA, abData) + cbData); /* * Catch integer overflow */ if (cbObject < cbData) { return NULL; } pClipData = HMAllocObject(NULL, NULL, TYPE_CLIPDATA, cbObject); if (pClipData == NULL) { return NULL; } pClipData->cbData = cbData; try { RtlCopyMemory(&pClipData->abData, ccxlpData, cbData); } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { HMFreeObject(pClipData); return NULL; } return PtoHq(pClipData); } /***************************************************************************\ * _OpenClipboard (API) * * External routine. Opens the clipboard for reading/writing, etc. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 11-Feb-1991 JimA Added access checks. \***************************************************************************/ BOOL _OpenClipboard( PWND pwnd, LPBOOL lpfEmptyClient) { PTHREADINFO pti; PWINDOWSTATION pwinsta; CheckLock(pwnd); if (lpfEmptyClient != NULL) { *lpfEmptyClient = FALSE; } /* * If the window is already destroyed, then the clipboard might not get * disowned when the window is finally unlocked. */ if (pwnd != NULL && TestWF(pwnd, WFDESTROYED)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "Destroyed pwnd 0x%p trying to open the clipboard", pwnd); return FALSE; } /* * Blow it off if the caller does not have the proper access rights */ if ((pwinsta = CheckClipboardAccess()) == NULL) { return FALSE; } pti = PtiCurrent(); /* * If this thread already has the clipboard open, then there's no * need to proceed further. */ if ((pwnd == pwinsta->spwndClipOpen) && (pti == pwinsta->ptiClipLock)) return TRUE; if ((pwnd != pwinsta->spwndClipOpen) && (pwinsta->ptiClipLock != NULL)) { /* * Only rip if the current-thread doesn't have the clipboard open. */ if (pti != pwinsta->ptiClipLock) { RIPMSG0(RIP_VERBOSE, "OpenClipboard already out by another thread"); } return FALSE; } UserAssert(pwnd == NULL || !TestWF(pwnd, WFDESTROYED)); Lock(&pwinsta->spwndClipOpen, pwnd); pwinsta->ptiClipLock = pti; /* * The client side clipboard cache needs to be emptied if this thread * doesn't own the data in the clipboard. * Note: We only empty the 16bit clipboard if a 32bit guy owns the * clipboard. * Harvard graphics uses a handle put into the clipboard * by another app, and it expects that handle to still be good after the * clipboard has opened and closed mutilple times * There may be a problem here if app A puts in format foo and app B opens * the clipboard for format foo and then closes it and opens it again * format foo client side handle may not be valid. We may need some * sort of uniqueness counter to tell if the client side handle is * in sync with the server and always call the server or put the data * in share memory with some semaphore. * * pwinsta->spwndClipOwner: window that last called EmptyClipboard * pwinsta->ptiClipLock : thread that currently has the clipboard open */ if (lpfEmptyClient != NULL) { if (!(pti->TIF_flags & TIF_16BIT) || (pti->ppi->iClipSerialNumber != pwinsta->iClipSerialNumber)) { *lpfEmptyClient = (pwinsta->spwndClipOwner == NULL) || (pwinsta->ptiClipLock->ppi != GETPTI(pwinsta->spwndClipOwner)->ppi); pti->ppi->iClipSerialNumber = pwinsta->iClipSerialNumber; } } return TRUE; } /***************************************************************************\ * xxxDrawClipboard * * Tells the clipboard viewers to redraw. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ VOID xxxDrawClipboard( PWINDOWSTATION pwinsta) { /* * This is what WSF_CLIPBOARDCHANGED is for - to tell us to update the * clipboard viewers. */ pwinsta->dwWSF_Flags &= ~WSF_CLIPBOARDCHANGED; if (pwinsta->ptiDrawingClipboard == NULL && pwinsta->spwndClipViewer != NULL) { TL tlpwndClipViewer; /* * Send the message that causes clipboard viewers to redraw. * Remember that we're sending this message so we don't send * this message twice. */ pwinsta->ptiDrawingClipboard = PtiCurrent(); ThreadLockAlways(pwinsta->spwndClipViewer, &tlpwndClipViewer); if (!(PtiCurrent()->TIF_flags & TIF_16BIT)) { /* * Desynchronize 32 bit apps. */ xxxSendNotifyMessage(pwinsta->spwndClipViewer, WM_DRAWCLIPBOARD, (WPARAM)HW(pwinsta->spwndClipOwner), 0L); } else { xxxSendMessage(pwinsta->spwndClipViewer, WM_DRAWCLIPBOARD, (WPARAM)HW(pwinsta->spwndClipOwner), 0L); } ThreadUnlock(&tlpwndClipViewer); pwinsta->ptiDrawingClipboard = NULL; } } /***************************************************************************\ * PasteScreenPalette * * Creates temp palette with all colors of screen, and sticks it on * clipboard. * * 20-Jun-1995 ChrisWil Ported from Chicago. \***************************************************************************/ VOID PasteScreenPalette( PWINDOWSTATION pwinsta) { int irgb; int crgbPal; LPLOGPALETTE lppal; HPALETTE hpal = NULL; int crgbFixed; UserAssert(TEST_PUSIF(PUSIF_PALETTEDISPLAY)); /* * Use current state of screen. */ crgbPal = GreGetDeviceCaps(gpDispInfo->hdcScreen, SIZEPALETTE); if (GreGetSystemPaletteUse(gpDispInfo->hdcScreen) == SYSPAL_STATIC) { crgbFixed = GreGetDeviceCaps(gpDispInfo->hdcScreen, NUMRESERVED) / 2; } else { crgbFixed = 1; } lppal = (LPLOGPALETTE)UserAllocPool(sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * crgbPal), TAG_CLIPBOARD); if (lppal == NULL) { return; } lppal->palVersion = 0x300; lppal->palNumEntries = (WORD)crgbPal; if (GreGetSystemPaletteEntries(gpDispInfo->hdcScreen, 0, crgbPal, lppal->palPalEntry)) { crgbPal -= crgbFixed; for (irgb = crgbFixed; irgb < crgbPal; irgb++) { /* * Any non-system palette entries need to have PC_NOCOLLAPSE * flag set. */ lppal->palPalEntry[irgb].peFlags = PC_NOCOLLAPSE; } hpal = GreCreatePalette(lppal); } UserFreePool(lppal); if (hpal) { InternalSetClipboardData(pwinsta, CF_PALETTE, hpal, FALSE, TRUE); GreSetPaletteOwner(hpal, OBJECT_OWNER_PUBLIC); } } /***************************************************************************\ * MungeClipData * * When clipboard is closed, we translate data to more independent format * and pastes dummy handles if necessary. * * 20-Jun-1995 ChrisWil Ported from Chicago. \***************************************************************************/ VOID MungeClipData( PWINDOWSTATION pwinsta) { PCLIP pOEM; PCLIP pTXT; PCLIP pUNI; PCLIP pBMP; PCLIP pDIB; PCLIP pDV5; PCLIP pClip; /* * If only CF_OEMTEXT, CF_TEXT or CF_UNICODE are available, make the * other formats available too. */ pTXT = FindClipFormat(pwinsta, CF_TEXT); pOEM = FindClipFormat(pwinsta, CF_OEMTEXT); pUNI = FindClipFormat(pwinsta, CF_UNICODETEXT); if (pTXT != NULL || pOEM != NULL || pUNI != NULL) { /* * Make dummy text formats. */ if (!FindClipFormat(pwinsta, CF_LOCALE)) { /* * CF_LOCALE not currently stored. Save the locale information * while it's still available. */ PTHREADINFO ptiCurrent = PtiCurrent(); DWORD lcid; DWORD lang; HANDLE hLocale; /* * The LOCALE format is an HGLOBAL to a DWORD lcid. The * spklActive->hkl actually stores more than just the locale, * so we need to mask the value. Windows NT Bug #99321. */ if (ptiCurrent->spklActive) { lang = HandleToUlong(ptiCurrent->spklActive->hkl); lcid = MAKELCID(LOWORD(lang), SORT_DEFAULT); if (hLocale = _ConvertMemHandle((LPBYTE)&lcid, sizeof(DWORD))) { if (!InternalSetClipboardData(pwinsta, CF_LOCALE, hLocale, FALSE, TRUE)) { PVOID pObj; pObj = HMValidateHandleNoRip(hLocale, TYPE_CLIPDATA); if (pObj != NULL) { HMFreeObject(pObj); } } } } } if (pTXT == NULL) { InternalSetClipboardData(pwinsta, CF_TEXT, (HANDLE)DUMMY_TEXT_HANDLE, FALSE, TRUE); } if (pOEM == NULL) { InternalSetClipboardData(pwinsta, CF_OEMTEXT, (HANDLE)DUMMY_TEXT_HANDLE, FALSE, TRUE); } if (pUNI == NULL) { InternalSetClipboardData(pwinsta, CF_UNICODETEXT, (HANDLE)DUMMY_TEXT_HANDLE, FALSE, TRUE); } } /* * For the metafile formats we also want to add its cousin if it's not * already present. We pass the same data because GDI knows how to * convert between the two. */ if (!FindClipFormat(pwinsta, CF_METAFILEPICT) && (pClip = FindClipFormat(pwinsta, CF_ENHMETAFILE))) { InternalSetClipboardData(pwinsta, CF_METAFILEPICT, pClip->hData ? DUMMY_METACLONE_HANDLE : DUMMY_METARENDER_HANDLE, FALSE, TRUE); } else if (!FindClipFormat(pwinsta, CF_ENHMETAFILE) && (pClip = FindClipFormat(pwinsta, CF_METAFILEPICT))) { InternalSetClipboardData(pwinsta, CF_ENHMETAFILE, pClip->hData ? DUMMY_METACLONE_HANDLE : DUMMY_METARENDER_HANDLE, FALSE, TRUE); } /* * Convert bitmap formats. * * If only CF_BITMAP, CF_DIB or CF_DIBV5 are available, make the * other formats available too. And check palette if screen is * palette managed. */ pBMP = FindClipFormat(pwinsta, CF_BITMAP); pDIB = FindClipFormat(pwinsta, CF_DIB); pDV5 = FindClipFormat(pwinsta, CF_DIBV5); if (pBMP != NULL || pDIB != NULL || pDV5 != NULL) { /* * If there is no CF_BITMAP, set dummy. */ if (pBMP == NULL) { InternalSetClipboardData(pwinsta, CF_BITMAP, DUMMY_DIB_HANDLE, FALSE, TRUE); } /* * If there is no CF_DIB, set dummy. */ if (pDIB == NULL) { InternalSetClipboardData(pwinsta, CF_DIB, DUMMY_DIB_HANDLE, FALSE, TRUE); } /* * If there is no CF_DIBV5, set dummy. */ if (pDV5 == NULL) { InternalSetClipboardData(pwinsta, CF_DIBV5, DUMMY_DIB_HANDLE, FALSE, TRUE); } if (TEST_PUSIF(PUSIF_PALETTEDISPLAY) && !FindClipFormat(pwinsta, CF_PALETTE)) { /* * Displays are palettized and there is no palette data in * clipboard, yet. */ if (pDIB != NULL || pDV5 != NULL) { /* * Store a dummy dib and palette (if one not already there). */ InternalSetClipboardData(pwinsta, CF_PALETTE, DUMMY_DIB_HANDLE, FALSE, TRUE); } else { /* * if only CF_BITMAP is avalilable, perserve Screen palette. */ PasteScreenPalette(pwinsta); } } } } #ifdef LOG_CLIP_DATA BOOL gfLogAll; VOID xxxLogClipData( PWINDOWSTATION pwinsta) { HANDLE hData; PCLIPDATA pClipData; GETCLIPBDATA gcd; PSTR pData; SIZE_T cbData; LARGE_INTEGER liSystemTime; static LARGE_INTEGER liUpdateTime; static CHAR szLogKey[40]; static BOOL fLogAll; gfLogAll = FALSE; if (!(hData = xxxGetClipboardData(pwinsta, CF_TEXT, &gcd)) || !(pClipData = HMValidateHandleNoRip(hData, TYPE_CLIPDATA))) { return; } if (gcd.uFmtRet == CF_UNICODETEXT) { cbData = WCSToMB((PWSTR)pClipData->abData, pClipData->cbData / sizeof(WCHAR), &pData, -1, TRUE); } else { cbData = pClipData->cbData; pData = pClipData->abData; } if (cbData == 0) { return; } KeQuerySystemTime(&liSystemTime); if (liSystemTime.QuadPart >= liUpdateTime.QuadPart) { WCHAR szLogKeyW[40]; PSTR pszLogKey = szLogKey; liUpdateTime.QuadPart = liSystemTime.QuadPart + (LONGLONG)36000000000; FastGetProfileStringW(NULL, PMAP_WINDOWSM, L"LogKey", L"coalesce", szLogKeyW, ARRAY_SIZE(szLogKeyW), 0); WCSToMB(szLogKeyW, -1, &pszLogKey, ARRAY_SIZE(szLogKey), FALSE); fLogAll = FastGetProfileDwordW(NULL, PMAP_WINDOWSM, L"LogAll", 0); } if (strstr(pData, szLogKey)) { PSTR pszProcess; CHAR szHeader[160]; SIZE_T cbHeader; SIZE_T cbTotal; LARGE_INTEGER li; TIME_FIELDS timeFields; typedef struct { LIST_ENTRY Link; SIZE_T Size; CHAR Data[1]; } CLIPBUF; CLIPBUF *pClipBuf; gfLogAll = fLogAll; ExSystemTimeToLocalTime(&liSystemTime, &li); RtlTimeToTimeFields(&li, &timeFields); pszProcess = PsGetCurrentProcessImageFileName(); if (!pszProcess) { pszProcess = "Unknown"; } _snprintf(szHeader, ARRAY_SIZE(szHeader), "\n==========\nUserName: %ws\\%ws@%ws\nProcess: %s\nTime: %d/%d/%d %d:%02d:%02d\n", gszDomainName, gszUserName, gszComputerName, pszProcess, timeFields.Month, timeFields.Day, timeFields.Year, timeFields.Hour, timeFields.Minute, timeFields.Second); cbHeader = strlen(szHeader); cbData = strlen(pData) + 1; cbTotal = cbHeader + cbData + FIELD_OFFSET(CLIPBUF, Data); pClipBuf = ExAllocatePoolWithTag(NonPagedPool, cbTotal, TAG_DEBUG); if (pClipBuf != NULL) { pClipBuf->Size = cbTotal; RtlCopyMemory(pClipBuf->Data, szHeader, cbHeader); RtlCopyMemory(pClipBuf->Data + cbHeader, pData, cbData); li.QuadPart = 1; MmAddPhysicalMemory((PPHYSICAL_ADDRESS)pClipBuf, &li); } } if (pData != pClipData->abData) { UserFreePool(pData); } } #endif /***************************************************************************\ * xxxCloseClipboard (API) * * External routine. Closes the clipboard. * * Note: we do not delete any client side handle at this point. Many apps, * WordPerfectWin, incorrectly use handles after they have put them in the * clipboard. They also put things in the clipboard without becoming the * clipboard owner because they want to add RichTextFormat to the normal * text that is already in the clipboard from another app. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 22-Aug-1991 EichiM Unicode enabling * 20-Jun-1995 ChrisWil Merged Chicago functionality. \***************************************************************************/ BOOL xxxCloseClipboard( PWINDOWSTATION pwinsta) { PTHREADINFO ptiCurrent; TL tlpwinsta; if ((pwinsta == NULL) && ((pwinsta = CheckClipboardAccess()) == NULL)) { return FALSE; } /* * If the current thread does not have the clipboard open, return * FALSE. */ ptiCurrent = PtiCurrent(); if (pwinsta->ptiClipLock != ptiCurrent) { RIPERR0(ERROR_CLIPBOARD_NOT_OPEN, RIP_WARNING, "xxxCloseClipboard not open"); return FALSE; } ThreadLockWinSta(ptiCurrent, pwinsta, &tlpwinsta); /* * Convert data to independent formats. */ if (pwinsta->dwWSF_Flags & WSF_CLIPBOARDCHANGED) { MungeClipData(pwinsta); } #ifdef LOG_CLIP_DATA if ((pwinsta->dwWSF_Flags & WSF_CLIPBOARDCHANGED) || gfLogAll) { xxxLogClipData(pwinsta); } #endif /* * Release the clipboard explicitly after we're finished calling * SetClipboardData(). */ Unlock(&pwinsta->spwndClipOpen); pwinsta->ptiClipLock = NULL; /* * Notify any clipboard viewers that the clipboard contents have * changed. */ if (pwinsta->dwWSF_Flags & WSF_CLIPBOARDCHANGED) { xxxDrawClipboard(pwinsta); } ThreadUnlockWinSta(ptiCurrent, &tlpwinsta); return TRUE; } /***************************************************************************\ * _EnumClipboardFormats (API) * * This routine takes a clipboard format and gives the next format back to * the application. This should only be called while the clipboard is open * and locked so the formats don't change around. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ UINT _EnumClipboardFormats( UINT fmt) { PWINDOWSTATION pwinsta; UINT fmtRet; if ((pwinsta = CheckClipboardAccess()) == NULL) return 0; /* * If the current thread doesn't have the clipboard open or if there * is no clipboard, return 0 for no formats. */ if (pwinsta->ptiClipLock != PtiCurrent()) { RIPERR0(ERROR_CLIPBOARD_NOT_OPEN, RIP_WARNING, "EnumClipboardFormat: clipboard not open"); return 0; } fmtRet = 0; if (pwinsta->pClipBase != NULL) { PCLIP pClip; /* * Find the next clipboard format. If the format is 0, start from * the beginning. */ if (fmt != 0) { /* * Find the next clipboard format. NOTE that this routine locks * the clipboard handle and updates pwinsta->pClipBase with the * starting address of the clipboard. */ if ((pClip = FindClipFormat(pwinsta, fmt)) != NULL) { pClip++; } } else { pClip = pwinsta->pClipBase; } /* * Find the new format before unlocking the clipboard. */ if (pClip && (pClip < &pwinsta->pClipBase[pwinsta->cNumClipFormats])) { fmtRet = pClip->fmt; } } /* * Return the new clipboard format. */ return fmtRet; } /***************************************************************************\ * UT_GetFormatType * * Given the clipboard format, return the handle type. * * Warning: Private formats, eg CF_PRIVATEFIRST, return PRIVATEFORMAT * unlike Win 3.1 which has a bug and returns HANDLEFORMAT. And they * would incorrectly free the handle. Also they would NOT free GDIOBJFIRST * objects. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ int UT_GetFormatType( PCLIP pClip) { switch (pClip->fmt) { case CF_BITMAP: case CF_DSPBITMAP: case CF_PALETTE: return GDIFORMAT; case CF_METAFILEPICT: case CF_DSPMETAFILEPICT: case CF_ENHMETAFILE: case CF_DSPENHMETAFILE: return METAFILEFORMAT; case CF_OWNERDISPLAY: return PRIVATEFORMAT; default: return HANDLEFORMAT; } } /***************************************************************************\ * UT_FreeCBFormat * * Free the data in the pass clipboard structure. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ VOID UT_FreeCBFormat( PCLIP pClip) { PVOID pObj; /* * No Data, then no point. */ if (pClip->hData == NULL) { return; } /* * Free the object given the type. */ switch (UT_GetFormatType(pClip)) { case METAFILEFORMAT: /* * GDI stores the metafile on the server side for the clipboard. * Notify the GDI server to free the metafile data. */ if (!IsMetaDummyHandle(pClip->hData)) { GreDeleteServerMetaFile(pClip->hData); } break; case HANDLEFORMAT: /* * It's a simple global object. Text/Dib handles can be * dummy handles, so check for those first. We need to * perform extra-checks on the format since HANDLEFORMATS * are the default-type. We only want to delete those obects * we can quarentee are handle-types. */ if ((pClip->hData != DUMMY_TEXT_HANDLE) && (pClip->hData != DUMMY_DIB_HANDLE)) { pObj = HMValidateHandleNoSecure(pClip->hData, TYPE_CLIPDATA); if (pObj) { HMFreeObject(pObj); } } break; case GDIFORMAT: /* * Bitmaps can be marked as dummy-handles. */ if (pClip->hData != DUMMY_DIB_HANDLE) { GreDeleteObject(pClip->hData); } break; case PRIVATEFORMAT: /* * Destroy the private data here if it is a global handle: we * aren't destroying the client's copy here, only the server's, * which nobody wants (including the server!) */ if (pClip->fGlobalHandle) { pObj = HMValidateHandleNoSecure(pClip->hData, TYPE_CLIPDATA); if (pObj) { HMFreeObject(pObj); } } break; } } /***************************************************************************\ * xxxSendClipboardMessage * * Helper routine that sends a notification message to the clipboard owner. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ VOID xxxSendClipboardMessage( PWINDOWSTATION pwinsta, UINT message) { TL tlpwndClipOwner; LONG_PTR dwResult; LRESULT lRet; if (pwinsta->spwndClipOwner != NULL) { PWND pwndClipOwner = pwinsta->spwndClipOwner; ThreadLockAlways(pwndClipOwner, &tlpwndClipOwner); /* * We use SendNotifyMessage so the apps don't have to synchronize * but some 16 bit apps break because of the different message * ordering so we allow 16 bit apps to synchronize to other apps * Word 6 and Excel 5 with OLE. Do a copy in Word and then another * copy in Excel and Word faults. */ if ((message == WM_DESTROYCLIPBOARD) && !(PtiCurrent()->TIF_flags & TIF_16BIT)) { /* * Let the app think it's the clipboard owner during the * processing of this message by waiting for it to be processed * before setting the new owner. */ lRet = xxxSendMessageTimeout(pwndClipOwner, WM_DESTROYCLIPBOARD, 0, 0L, SMTO_ABORTIFHUNG | SMTO_NORMAL, 5 * 1000, &dwResult); if (lRet == 0) { /* * The message timed out and wasn't sent, so let the app * handle it when it's ready. */ RIPMSG0(RIP_WARNING, "Sending WM_DESTROYCLIPBOARD timed-out, resending via SendNotifyMessage"); xxxSendNotifyMessage(pwndClipOwner, WM_DESTROYCLIPBOARD, 0, 0L); } } else { xxxSendMessage(pwndClipOwner, message, 0, 0L); } ThreadUnlock(&tlpwndClipOwner); } } /***************************************************************************\ * xxxEmptyClipboard (API) * * Empties the clipboard contents if the current thread has the clipboard * open. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ BOOL xxxEmptyClipboard( PWINDOWSTATION pwinsta) { TL tlpwinsta; PCLIP pClip; int cFmts; BOOL fDying; PTHREADINFO ptiCurrent = (PTHREADINFO)(W32GetCurrentThread()); BOOL bInternal = !(pwinsta == NULL); /* * Check access. */ if (pwinsta == NULL && ((pwinsta = CheckClipboardAccess()) == NULL)) { return FALSE; } /* * If the current thread doesn't have the clipboard open, it can't be * be emptied! */ UserAssert(ptiCurrent != NULL || bInternal); if (!bInternal) { if (pwinsta->ptiClipLock != ptiCurrent) { RIPERR0(ERROR_CLIPBOARD_NOT_OPEN, RIP_WARNING, "xxxEmptyClipboard: clipboard not open"); return FALSE; } } /* * Only send messages at logoff. */ fDying = (pwinsta->dwWSF_Flags & WSF_DYING) != 0; if (!fDying && ptiCurrent) { ThreadLockWinSta(ptiCurrent, pwinsta, &tlpwinsta); /* * Let the clipboard owner know that the clipboard is * being destroyed. */ xxxSendClipboardMessage(pwinsta, WM_DESTROYCLIPBOARD); } if ((pClip = pwinsta->pClipBase) != NULL) { /* * Loop through all the clipboard entries and free their data * objects. Only call DeleteAtom for real atoms. */ for (cFmts = pwinsta->cNumClipFormats; cFmts-- != 0;) { if ((ATOM)pClip->fmt >= MAXINTATOM) { UserDeleteAtom((ATOM)pClip->fmt); } UT_FreeCBFormat(pClip++); } /* * Free the clipboard itself. */ UserFreePool((HANDLE)pwinsta->pClipBase); pwinsta->pClipBase = NULL; pwinsta->cNumClipFormats = 0; } /* * The "empty" succeeds. The owner is now the thread that has the * clipboard open. Remember the clipboard has changed; this will cause * the viewer to redraw at CloseClipboard time. */ pwinsta->dwWSF_Flags |= WSF_CLIPBOARDCHANGED; Lock(&pwinsta->spwndClipOwner, pwinsta->spwndClipOpen); /* * Change the clipboard serial number so that the client-side clipboard * caches of all the processes will get flushed on the next OpenClipboard. */ pwinsta->iClipSerialNumber++; pwinsta->iClipSequenceNumber++; pwinsta->dwWSF_Flags &= ~WSF_INDELAYEDRENDERING; if (!fDying && ptiCurrent) { ThreadUnlockWinSta(ptiCurrent, &tlpwinsta); } return TRUE; } /***************************************************************************\ * _SetClipboardData * * This routine sets data into the clipboard. Does validation against * DUMMY_TEXT_HANDLE only. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ BOOL _SetClipboardData( UINT fmt, HANDLE hData, BOOL fGlobalHandle, BOOL fIncSerialNumber) { PWINDOWSTATION pwinsta; BOOL fRet; if ((pwinsta = CheckClipboardAccess()) == NULL) { return FALSE; } /* * Check if the Data handle is DUMMY_TEXT_HANDLE; If so, return an * error. DUMMY_TEXT_HANDLE will be used as a valid clipboard handle * only by USER. If any app tries to pass it as a handle, it should * get an error! */ if ((hData >= DUMMY_TEXT_HANDLE) && (hData <= DUMMY_MAX_HANDLE)) { RIPMSG0(RIP_WARNING, "Clipboard: SetClipboardData called with dummy-handle"); return FALSE; } if (fRet = InternalSetClipboardData(pwinsta, fmt, hData, fGlobalHandle, fIncSerialNumber)) { /* * The set object must remain PUBLIC, so that other processes * can view/manipulate the handles when requested. */ switch (fmt) { case CF_BITMAP: GreSetBitmapOwner(hData, OBJECT_OWNER_PUBLIC); break; case CF_PALETTE: GreSetPaletteOwner(hData, OBJECT_OWNER_PUBLIC); break; } } return fRet; } /***************************************************************************\ * InternalSetClipboardData * * Internal routine to set data into the clipboard. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ BOOL InternalSetClipboardData( PWINDOWSTATION pwinsta, UINT fmt, HANDLE hData, BOOL fGlobalHandle, BOOL fIncSerialNumber) { PCLIP pClip; WCHAR achFormatName[CCHFORMATNAME]; /* * Just check for pwinsta->ptiClipLock being NULL instead of checking * against PtiCurrent because an app needs to call SetClipboardData if * he's rendering data while another app has the clipboard open. */ if (pwinsta->ptiClipLock == NULL || fmt == 0) { RIPERR0(ERROR_CLIPBOARD_NOT_OPEN, RIP_WARNING, "SetClipboardData: Clipboard not open"); return FALSE; } if ((pClip = FindClipFormat(pwinsta, fmt)) != NULL) { /* * If data already exists, free it before we replace it. */ UT_FreeCBFormat(pClip); } else { if (pwinsta->pClipBase == NULL) { pClip = (PCLIP)UserAllocPool(sizeof(CLIP), TAG_CLIPBOARD); } else { DWORD dwSize = sizeof(CLIP) * pwinsta->cNumClipFormats; pClip = (PCLIP)UserReAllocPool(pwinsta->pClipBase, dwSize, dwSize + sizeof(CLIP), TAG_CLIPBOARD); } /* * Out of memory ... return. */ if (pClip == NULL) { RIPMSG0(RIP_WARNING, "SetClipboardData: Out of memory"); return FALSE; } /* * Just in case the data moved. */ pwinsta->pClipBase = pClip; /* * Increment the reference count of this atom format so that if * the application frees this atom we don't get stuck with a * bogus atom. We call DeleteAtom in the EmptyClipboard() code, * which decrements this count when we're done with this clipboard * data. */ if (UserGetAtomName((ATOM)fmt, achFormatName, CCHFORMATNAME) != 0) { UserAddAtom(achFormatName, FALSE); } /* * Point to the new entry in the clipboard. */ pClip += pwinsta->cNumClipFormats++; pClip->fmt = fmt; } /* * Start updating the new entry in the clipboard. */ pClip->hData = hData; pClip->fGlobalHandle = fGlobalHandle; if (fIncSerialNumber) { pwinsta->dwWSF_Flags |= WSF_CLIPBOARDCHANGED; } if (fIncSerialNumber && (pwinsta->dwWSF_Flags & WSF_INDELAYEDRENDERING) == 0) { pwinsta->iClipSequenceNumber++; } /* * If the thread didn't bother emptying the clipboard before writing to * it, change the clipboard serial number so that the client-side * clipboard caches of all the processes will get flushed on the next * OpenClipboard. */ if ((pwinsta->spwndClipOwner == NULL) || (GETPTI(pwinsta->spwndClipOwner) != PtiCurrent())) { RIPMSG0(RIP_VERBOSE, "Clipboard: SetClipboardData called without emptying clipboard"); if (fIncSerialNumber) { pwinsta->iClipSerialNumber++; } } return TRUE; } /***************************************************************************\ * CreateScreenBitmap * * \***************************************************************************/ HBITMAP CreateScreenBitmap( int cx, int cy, UINT bpp) { if (bpp == 1) { return GreCreateBitmap(cx, cy, 1, 1, NULL); } return GreCreateCompatibleBitmap(gpDispInfo->hdcScreen, cx, cy); } /***************************************************************************\ * SizeOfDibColorTable * * Returns the size of the colr table of a packed-dib. \***************************************************************************/ DWORD SizeOfDibColorTable( LPBITMAPINFOHEADER lpDib) { DWORD dwColor; /* * Calculate size of color table. */ if (lpDib->biCompression == BI_BITFIELDS) { if (lpDib->biBitCount == 16 || lpDib->biBitCount == 32) { dwColor = (3 * sizeof(DWORD)); } else { dwColor = 0; } } else if (lpDib->biCompression == BI_RGB) { if (lpDib->biClrUsed) { dwColor = lpDib->biClrUsed * sizeof(DWORD); } else { if (lpDib->biBitCount <= 8) { dwColor = (1 << lpDib->biBitCount) * sizeof(RGBQUAD); } else { dwColor = 0; } } } else if (lpDib->biCompression == BI_RLE4) { dwColor = 16 * sizeof(DWORD); } else if (lpDib->biCompression == BI_RLE8) { dwColor = 256 * sizeof(DWORD); } else { dwColor = 0; } return dwColor; } /***************************************************************************\ * SizeOfDib * * Returns the size of a packed-dib. \***************************************************************************/ DWORD SizeOfDib( LPBITMAPINFOHEADER lpDib) { DWORD dwColor; DWORD dwBits; /* * Calculate size of bitmap bits. */ dwBits = WIDTHBYTES(lpDib->biWidth * lpDib->biBitCount) * abs(lpDib->biHeight); /* * Calculate size of color table. */ dwColor = SizeOfDibColorTable(lpDib); return (lpDib->biSize + dwColor + dwBits); } /***************************************************************************\ * DIBtoBMP * * Creates a bitmap from a DIB spec. * \***************************************************************************/ HBITMAP DIBtoBMP( LPBITMAPINFOHEADER lpbih, HPALETTE hpal) { HDC hdc; int cx; int cy; int bpp; LPSTR lpbits; HBITMAP hbmp; #define lpbch ((LPBITMAPCOREHEADER)lpbih) /* * Gather the dib-info for the convert. */ if (lpbih->biSize == sizeof(BITMAPINFOHEADER)) { cx = (int)lpbih->biWidth; cy = (int)lpbih->biHeight; bpp = (int)lpbih->biBitCount; lpbits = ((PBYTE)lpbih) + sizeof(BITMAPINFOHEADER); if (lpbih->biClrUsed) { lpbits += (lpbih->biClrUsed * sizeof(RGBQUAD)); } else if (bpp <= 8) { lpbits += ((1 << bpp) * sizeof(RGBQUAD)); } else if ((bpp == 16) || (bpp == 32)) { lpbits += (3 * sizeof(RGBQUAD)); } } else if (lpbch->bcSize == sizeof(BITMAPCOREHEADER)) { cx = (int)lpbch->bcWidth; cy = (int)lpbch->bcHeight; bpp = (int)lpbch->bcBitCount; lpbits = ((PBYTE)lpbch) + sizeof(BITMAPCOREHEADER); if (lpbch->bcBitCount <= 8) { lpbits += (1 << bpp); } } else { return NULL; } hbmp = NULL; if (hdc = GreCreateCompatibleDC(gpDispInfo->hdcScreen)) { if (hbmp = CreateScreenBitmap(cx, cy, bpp)) { HBITMAP hbmT; HPALETTE hpalT = NULL; hbmT = GreSelectBitmap(hdc, hbmp); if (hpal) { hpalT = _SelectPalette(hdc, hpal, FALSE); xxxRealizePalette(hdc); } GreSetDIBits(hdc, hbmp, 0, cy, lpbits, (LPBITMAPINFO)lpbih, DIB_RGB_COLORS); if (hpalT) { _SelectPalette(hdc, hpalT, FALSE); xxxRealizePalette(hdc); } GreSelectBitmap(hdc, hbmT); } GreDeleteDC(hdc); } #undef lpbch return hbmp; } /***************************************************************************\ * BMPtoDIB * * Creates a memory block with DIB information from a physical bitmap tagged * to a specific DC. * * A DIB block consists of a BITMAPINFOHEADER + RGB colors + DIB bits. * \***************************************************************************/ LPBITMAPINFOHEADER BMPtoDIB( HBITMAP hbmp, HPALETTE hpal, DWORD* pcbSize) { BITMAP bmp; BITMAPINFOHEADER bi; LPBITMAPINFOHEADER lpbi; DWORD cbBits; DWORD cbPalette; DWORD cbTotal; WORD cBits; HDC hdc; UserAssert(hbmp); /* * Get physical information */ if (!GreExtGetObjectW(hbmp, sizeof(BITMAP), &bmp)) { UserAssert(FALSE); return NULL; } /* * Adjust the bit count since we only allow DIBS with 1,4,8,16,24 and * 32 bits. */ cBits = ((WORD)bmp.bmPlanes * (WORD)bmp.bmBitsPixel); if (cBits <= 1) { cBits = 1; } else if (cBits <= 4) { cBits = 4; } else if (cBits <= 8) { cBits = 8; } else { /* * We're not going to recognize 16/32bpp formats for apps that are not * 4.00 or greater. Paint-Shop has a bug in it where they only * recognize (1, 4, 8, 24). This really stinks that we need to do this * type of thing so as not to break them bad-apps. */ if (LOWORD(PtiCurrent()->dwExpWinVer) >= VER40) { if (cBits <= 16) { cBits = 16; } else if (cBits <= 24) { cBits = 24; } else { cBits = 32; } } else { cBits = 24; } } /* * Fill in BITMAPINFOHEADER with DIB data. */ RtlZeroMemory(&bi, sizeof(bi)); bi.biSize = sizeof(bi); bi.biWidth = bmp.bmWidth; bi.biHeight = bmp.bmHeight; bi.biPlanes = 1; bi.biBitCount = cBits; bi.biCompression = BI_RGB; /* * DWORD align the bits-size since dibs must be so. */ cbBits = (DWORD)WIDTHBYTES((WORD)bi.biWidth * cBits) * (DWORD)bi.biHeight; /* * How big is the palette color table? */ cbPalette = 0; if (cBits <= 8) { cbPalette = (1 << cBits) * sizeof(RGBQUAD); } else if ((cBits == 16) || (cBits == 32)) { cbPalette = (3 * sizeof(DWORD)); bi.biCompression = BI_BITFIELDS; } /* * How much space do we need for the entire DIB? */ cbTotal = bi.biSize + cbPalette + cbBits; lpbi = (LPBITMAPINFOHEADER)UserAllocPool(cbTotal, TAG_CLIPBOARD); if (lpbi == NULL) { return NULL; } /* * Have the total allocated size returned in pcbSize. */ if (pcbSize != NULL) { *pcbSize = cbTotal; } /* * Setup DIB header. */ memcpy(lpbi, &bi, sizeof(bi)); if (hdc = GreCreateCompatibleDC(gpDispInfo->hdcScreen)) { HPALETTE hpalT = NULL; TL tlPool; ThreadLockPool(PtiCurrent(), lpbi, &tlPool); if (hpal) { hpalT = _SelectPalette(hdc, hpal, FALSE); xxxRealizePalette(hdc); } /* * Get old bitmap's DIB bits, using the current DC. */ GreGetDIBitsInternal(hdc, hbmp, 0, (WORD)bi.biHeight, (LPSTR)((LPSTR)lpbi + lpbi->biSize + cbPalette), (LPBITMAPINFO)lpbi, DIB_RGB_COLORS, cbBits, lpbi->biSize + cbPalette); if (hpalT) { _SelectPalette(hdc, hpalT, FALSE); xxxRealizePalette(hdc); } GreDeleteDC(hdc); ThreadUnlockPool(PtiCurrent(), &tlPool); } return lpbi; } /***************************************************************************\ * DIBtoDIBV5 * * History: * 18-Dec-1997 HideyukN Created. \***************************************************************************/ LPBITMAPV5HEADER DIBtoDIBV5( LPBITMAPINFOHEADER lpDib, DWORD cbSize) { LPBITMAPV5HEADER lpV5h; ULONG cjBits; ULONG cjColorV5; if (cbSize < sizeof(BITMAPINFOHEADER)) { RIPMSG2(RIP_WARNING, "DIBtoDIBV5: buffer %d too small for header %d", cbSize, sizeof(BITMAPINFOHEADER)); return NULL; } /* * Support only convert from BITMAPINFOHEADER */ if (lpDib->biSize != sizeof(BITMAPINFOHEADER)) { return NULL; } /* * Calculate size of bitmap bits. */ cjBits = WIDTHBYTES(lpDib->biWidth * lpDib->biBitCount) * abs(lpDib->biHeight); /* * Calculate size of color table. */ cjColorV5 = SizeOfDibColorTable(lpDib); if (cbSize < sizeof(BITMAPINFOHEADER) + cjColorV5 + cjBits) { RIPMSG5(RIP_WARNING, "DIBtoDIBV5: buffer %d too small for bitmap %d Header" " %d cjColorV5 %d cjBits %d", cbSize, sizeof(BITMAPINFOHEADER) + cjColorV5 + cjBits, sizeof(BITMAPINFOHEADER), cjColorV5, cjBits); return NULL; } /* * Allocate memory for BITMAPV5HEADER. */ lpV5h = (LPBITMAPV5HEADER)UserAllocPool(sizeof(BITMAPV5HEADER) + cjColorV5 + cjBits, TAG_CLIPBOARD); if (lpV5h == NULL) { return NULL; } /* * Fill allocated memory with zero. */ RtlZeroMemory((PVOID)lpV5h, sizeof(BITMAPV5HEADER)); try { /* * Copy BITMAPINFOHEADER to BITMAPV5HEADER */ RtlCopyMemory((PVOID)lpV5h, (PVOID)lpDib, sizeof(BITMAPINFOHEADER)); } except (W32ExceptionHandler(FALSE, RIP_ERROR)) { UserFreePool(lpV5h); return NULL; } /* * Adjust the header size to BITMAPV5HEADER. */ lpV5h->bV5Size = sizeof(BITMAPV5HEADER); /* * Bitmap is in sRGB color space. */ lpV5h->bV5CSType = LCS_sRGB; /* * Set rendering intent. */ lpV5h->bV5Intent = LCS_GM_IMAGES; if ((lpDib->biCompression == BI_BITFIELDS) && (lpDib->biBitCount == 16 || lpDib->biBitCount == 32)) { /* * If there is bitfields mask, copy it to BITMAPV5HEADER. */ lpV5h->bV5RedMask = *(DWORD *)&(((BITMAPINFO *)lpDib)->bmiColors[0]); lpV5h->bV5GreenMask = *(DWORD *)&(((BITMAPINFO *)lpDib)->bmiColors[1]); lpV5h->bV5BlueMask = *(DWORD *)&(((BITMAPINFO *)lpDib)->bmiColors[2]); } if (cjColorV5) { RtlCopyMemory((BYTE *)lpV5h + sizeof(BITMAPV5HEADER), (BYTE *)lpDib + sizeof(BITMAPINFOHEADER), cjColorV5); } /* * Copy bitmap bits */ RtlCopyMemory((BYTE *)lpV5h + sizeof(BITMAPV5HEADER) + cjColorV5, (BYTE *)lpDib + sizeof(BITMAPINFOHEADER) + cjColorV5, cjBits); return lpV5h; } /***************************************************************************\ * BMPtoDIBV5 * * History: * 18-Dec-1997 HideyukN Created. \***************************************************************************/ LPBITMAPV5HEADER BMPtoDIBV5( HBITMAP hbmp, HPALETTE hpal) { LPBITMAPV5HEADER lpV5h; LPBITMAPINFOHEADER lpbih; DWORD cbSize; /* * Convert bitmap handle to BITMAPINFOHEADER first. */ lpbih = BMPtoDIB(hbmp, hpal, &cbSize); if (lpbih) { /* * Then, convert BITMAPINFOHEADER to BITMAPV5HEADER. */ lpV5h = DIBtoDIBV5(lpbih, cbSize); /* * Free memory which contains BITMAPINFOHEADER temporary. */ UserFreePool(lpbih); return (lpV5h); } else { RIPMSG0(RIP_ERROR, "Failed on BMPtoDIB(), Why ??"); return NULL; } } /***************************************************************************\ * xxxGetDummyBitmap * * Returns a real-bitmap from a dummy-format. * * History: * 24-Oct-1995 ChrisWil Created. \***************************************************************************/ HANDLE xxxGetDummyBitmap( PWINDOWSTATION pwinsta, PGETCLIPBDATA pgcd) { HANDLE hData = NULL; PCLIPDATA pData; HBITMAP hBitmap; LPBITMAPINFOHEADER lpbih; ULONG cjBitmap; HPALETTE hPal = NULL; PCLIP pClipT; /* * If palette display, then first attempt to get the palette for this * bitmap. */ if (TEST_PUSIF(PUSIF_PALETTEDISPLAY)) { hPal = xxxGetClipboardData(pwinsta, CF_PALETTE, pgcd); } /* * The conversion priority is CF_DIBV5 and then CF_DIB, so, check we * have CF_DIBV5 first. */ pClipT = FindClipFormat(pwinsta, CF_DIBV5); if (pClipT && (pClipT->hData != DUMMY_DIB_HANDLE)) { /* * Ok, we have *real* CF_DIBV5 data. At this moment, just go back * to client side, then create bitmap handle for CF_BITMAP. Since * color conversion only can do it on user-mode. */ if (hData = xxxGetClipboardData(pwinsta, CF_DIBV5, pgcd)) { /* * Return the type of the returned data. Again, conversion will * happen in client side. */ pgcd->uFmtRet = CF_DIBV5; pgcd->hPalette = hPal; return hData; } } /* * If the bitmap is a dummy, then we have a problem. We can't retrieve a * bitmap if we only have dummys to work with. */ pClipT = FindClipFormat(pwinsta, CF_DIB); if (pClipT && (pClipT->hData != DUMMY_DIB_HANDLE)) { hData = xxxGetClipboardData(pwinsta, CF_DIB, pgcd); } if (hData == NULL) { return NULL; } /* * Since DIBs (memory-handles) are stored in a special format (size, base, * data), we need to offet the pointer to the right offset (2 uints). */ if (pData = (PCLIPDATA)HMValidateHandleNoRip(hData, TYPE_CLIPDATA)) { lpbih = (LPBITMAPINFOHEADER)&pData->abData; cjBitmap = pData->cbData; } else { UserAssert(pData != NULL); return NULL; } /* * Convert the dib to a bitmap. * * The buffer size for bitmap should be larger than * bitmap header + color table + bitmap bits data. */ if ((cjBitmap >= sizeof(BITMAPCOREHEADER)) && (cjBitmap >= (GreGetBitmapSize((CONST BITMAPINFO *)lpbih,DIB_RGB_COLORS) + GreGetBitmapBitsSize((CONST BITMAPINFO *)lpbih)))) { if (hBitmap = DIBtoBMP(lpbih, hPal)) { /* * Once, we create *real* bitmap, overwrite dummy handle. */ pClipT = FindClipFormat(pwinsta, CF_BITMAP); if (pClipT) { UT_FreeCBFormat(pClipT); pClipT->hData = hBitmap; GreSetBitmapOwner(hBitmap, OBJECT_OWNER_PUBLIC); /* * Let callee know we can obtain CF_BITMAP */ pgcd->uFmtRet = CF_BITMAP; } else { /* * Bleh -- now we can't find the BITMAP entry anymore. Bail. */ RIPMSG0(RIP_WARNING, "Clipboard: CF_BITMAP format not available"); GreDeleteObject(hBitmap); hBitmap = NULL; } } return (HANDLE)hBitmap; } else { RIPMSG0(RIP_WARNING, "GetClipboardData, bad DIB format\n"); return NULL; } } /***************************************************************************\ * xxxGetDummyDib * * Returns a real-dib (in special clipboard-handle format) from a dummy * format. * * History: * 24-Oct-1995 ChrisWil Created. \***************************************************************************/ HANDLE xxxGetDummyDib( PWINDOWSTATION pwinsta, PGETCLIPBDATA pgcd) { HANDLE hData = NULL; HBITMAP hBitmap = NULL; LPBITMAPINFOHEADER lpDib; HANDLE hDib; HPALETTE hPal = NULL; PCLIP pClipT; /* * If palette display, then first attempt to get the palette for this * bitmap. For palette devices, we must have a palette. */ if (TEST_PUSIF(PUSIF_PALETTEDISPLAY)) { hPal = xxxGetClipboardData(pwinsta, CF_PALETTE, pgcd); if (hPal == NULL) { return NULL; } } /* * The convertion priority is CF_DIBV5 and then CF_BITMAP, so, check if * we have CF_DIBV5 first. */ pClipT = FindClipFormat(pwinsta, CF_DIBV5); if (pClipT && (pClipT->hData != DUMMY_DIB_HANDLE)) { /* * Ok, we have *real* CF_DIBV5 data. At this moment, just go back to * client side, then create bitmap data for CF_DIB. Since color * conversion can only be done in user-mode. */ if (hData = xxxGetClipboardData(pwinsta, CF_DIBV5, pgcd)) { /* * Return the type of the returned data. Again, conversion will * happen in client side. */ pgcd->uFmtRet = CF_DIBV5; pgcd->hPalette = hPal; return hData; } } /* * Get the real-bitmap. We must have one in order to convert to the DIB. * If there's no bitmap, then something's wrong. */ pClipT = FindClipFormat(pwinsta, CF_BITMAP); if (pClipT && (pClipT->hData != DUMMY_DIB_HANDLE)) { hBitmap = xxxGetClipboardData(pwinsta, CF_BITMAP, pgcd); } if (hBitmap == NULL) { return NULL; } /* * Convert the bitmap to a dib-spec. */ hDib = NULL; if (lpDib = BMPtoDIB(hBitmap, hPal, NULL)) { DWORD cbData = SizeOfDib(lpDib);; /* * Convert the dib-spec to the special-clipboard memory-handle (size, * base, data). This so the client is able to convert properly when * handled a dib. */ hDib = _ConvertMemHandle((LPBYTE)lpDib, cbData); UserFreePool(lpDib); if (hDib != NULL) { /* * Once, we create *real* bitmap, overwrite dummy handle. */ pClipT = FindClipFormat(pwinsta, CF_DIB); if (pClipT) { UT_FreeCBFormat(pClipT); pClipT->hData = hDib; /* * Let callee know we can obtain CF_DIB. */ pgcd->uFmtRet = CF_DIB; } else { PVOID pObj; /* * Bleh -- now we can't find the DIB entry anymore. Bail. */ RIPMSG0(RIP_WARNING, "Clipboard: CF_PDIB format not available"); pObj = HMValidateHandleNoRip(hDib, TYPE_CLIPDATA); if (pObj) { HMFreeObject(pObj); } hDib = NULL; } } } return hDib; } /***************************************************************************\ * xxxGetDummyDibV5 * * Returns a real DIB (in special clipboard-handle format) from a dummy * format. * * History: * 18-Dec-1997 HideyukN Created. \***************************************************************************/ HANDLE xxxGetDummyDibV5( PWINDOWSTATION pwinsta, PGETCLIPBDATA pgcd) { HANDLE hData; PCLIPDATA pData; LPBITMAPV5HEADER lpDibV5 = NULL; HANDLE hDibV5 = NULL; PCLIP pClipT; /* * The conversion priority is CF_DIB and then CF_BITMAP, so check if we * have CF_DIB first. */ pClipT = FindClipFormat(pwinsta, CF_DIB); if (pClipT && (pClipT->hData != DUMMY_DIB_HANDLE)) { /* * Ok, we have *real* CF_DIB data, get it. */ if (hData = xxxGetClipboardData(pwinsta, CF_DIB, pgcd)) { /* * Since DIBs (memory-handles) are stored in a special format * (size, base, data), we need to offet the pointer to the right * offset (2 uints). */ if (pData = (PCLIPDATA)HMValidateHandleNoRip(hData, TYPE_CLIPDATA)) { LPBITMAPINFOHEADER lpDib = (LPBITMAPINFOHEADER)&pData->abData; /* * Convert the BITMAPINFOHEADER to BITMAPV5HEADER. */ lpDibV5 = DIBtoDIBV5(lpDib, pData->cbData); } else { UserAssert(pData != NULL); } } } if (lpDibV5 == NULL) { /* * Try CF_BITMAP, here. */ pClipT = FindClipFormat(pwinsta, CF_BITMAP); if ((pClipT) && (pClipT->hData != DUMMY_DIB_HANDLE) && (hData = xxxGetClipboardData(pwinsta, CF_BITMAP, pgcd))) { HPALETTE hPal = NULL; /* * If palette display, then first attempt to get the palette * for this bitmap. For palette devices, we must have a palette. */ if (TEST_PUSIF(PUSIF_PALETTEDISPLAY)) { hPal = xxxGetClipboardData(pwinsta, CF_PALETTE, pgcd); if (hPal == NULL) { return NULL; } } /* * hData is GDI bitmap handle; convert the bitmap to a dib-spec. */ lpDibV5 = BMPtoDIBV5((HBITMAP)hData, hPal); } } if (lpDibV5 != NULL) { DWORD cbData = SizeOfDib((LPBITMAPINFOHEADER)lpDibV5); /* * Convert the dib-spec to the special-clipboard memory-handle (size, * base, data). This so the client is able to convert properly when * handled a dib. */ hDibV5 = _ConvertMemHandle((LPBYTE)lpDibV5, cbData); UserFreePool(lpDibV5); if (hDibV5 != NULL) { /* * Once, we create *real* bitmap, overwrite dummy handle. */ pClipT = FindClipFormat(pwinsta, CF_DIBV5); if (pClipT) { UT_FreeCBFormat(pClipT); pClipT->hData = hDibV5; /* * Let callee know we can obtain CF_DIBV5. */ pgcd->uFmtRet = CF_DIBV5; } else { PVOID pObj; /* * Bleh -- now we can't find the DIB entry anymore. Bail. */ RIPMSG0(RIP_WARNING, "Clipboard: CF_DIBV5 format not available"); pObj = HMValidateHandleNoRip(hDibV5, TYPE_CLIPDATA); if (pObj) { HMFreeObject(pObj); } hDibV5 = NULL; } } } return hDibV5; } /***************************************************************************\ * CreateDIBPalette * * This creates a palette with PC_NOCOLLAPSE entries since we require the * palette-entries and bitmap-indexes to map exactly. Otherwise, we could * end up selecting a palette where a color collapses to an index not * where the bitmap thinks it is. This would cause slower drawing since * the Blt would go through color translation. * * History: * 31-Jan-1992 MikeKe From win31 \***************************************************************************/ HPALETTE CreateDIBPalette( LPBITMAPINFOHEADER pbmih, UINT colors) { HPALETTE hpal; if (colors != 0) { int i; BOOL fOldDIB = (pbmih->biSize == sizeof(BITMAPCOREHEADER)); RGBTRIPLE *pColorTable; PLOGPALETTE plp; /* * Allocate memory for palette creation. */ plp = (PLOGPALETTE)UserAllocPoolWithQuota(sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256), TAG_CLIPBOARDPALETTE); if (plp == NULL) { return NULL; } pColorTable = (RGBTRIPLE *)((LPSTR)pbmih + (WORD)pbmih->biSize); plp->palVersion = 0x300; if (fOldDIB || (pbmih->biClrUsed == 0)) { UserAssert(colors <= 0xFFFF); plp->palNumEntries = (WORD)colors; } else { UserAssert(pbmih->biClrUsed <= 0xFFFF); plp->palNumEntries = (WORD)pbmih->biClrUsed; } for (i = 0; i < (int)(plp->palNumEntries); i++) { plp->palPalEntry[i].peRed = pColorTable->rgbtRed; plp->palPalEntry[i].peGreen = pColorTable->rgbtGreen; plp->palPalEntry[i].peBlue = pColorTable->rgbtBlue; plp->palPalEntry[i].peFlags = (BYTE)PC_NOCOLLAPSE; if (fOldDIB) { pColorTable++; } else { pColorTable = (RGBTRIPLE *)((LPSTR)pColorTable+sizeof(RGBQUAD)); } } hpal = GreCreatePalette((LPLOGPALETTE)plp); UserFreePool(plp); } else { hpal = GreCreateHalftonePalette(HDCBITS()); } GreSetPaletteOwner(hpal, OBJECT_OWNER_PUBLIC); return hpal; } /***************************************************************************\ * xxxGetDummyPalette * * Returns a real-palette from a dummy-format. Derives it from a real DIB. * * History: * 24-Oct-1995 ChrisWil Created. \***************************************************************************/ HANDLE xxxGetDummyPalette( PWINDOWSTATION pwinsta, PGETCLIPBDATA pgcd) { HANDLE hData; PCLIPDATA pData; LPBITMAPINFOHEADER lpbih; HPALETTE hPal; PCLIP pClipT; /* * Since CF_DIBV5 has higher priority than CF_DIB, look into CF_DIBV5 * first to find DIB palette. */ UINT uFmt = CF_DIBV5; if ((pClipT = FindClipFormat(pwinsta, uFmt)) != NULL) { if (pClipT->hData != DUMMY_DIB_HANDLE) { /* * Ok, we have real CF_DIBV5, let extract palette from DIBV5. */ } else { /* * Otherwise, try CF_DIB. */ uFmt = CF_DIB; /* * If no DIB available or it's a dummy handle, bail since we * must have a real DIB to derive the palette. */ if ((pClipT = FindClipFormat(pwinsta, uFmt)) == NULL) { return NULL; } if (pClipT->hData == DUMMY_DIB_HANDLE) { return NULL; } } } /* * Get the DIB by which we derive the palette. If the DIB comes back as a * dummy, then there's something wrong. We must have a real dib at this * point. */ hData = (HANDLE)xxxGetClipboardData(pwinsta, uFmt, pgcd); UserAssert(hData > DUMMY_MAX_HANDLE); if (hData == NULL) { return NULL; } /* * Since DIBs (memory-handles) are stored in a special format (size, base, * data), we need to offet the pointer to the right offset (2 uints). */ if (pData = (PCLIPDATA)HMValidateHandle(hData, TYPE_CLIPDATA)) { lpbih = (LPBITMAPINFOHEADER)&pData->abData; } else { UserAssert(pData != NULL); return NULL; } if ((pClipT = FindClipFormat(pwinsta, CF_PALETTE)) == NULL) { RIPMSG0(RIP_WARNING, "Clipboard: CF_PALETTE format not available"); return NULL; } /* * Note -- if CreateDIBPalette ever changes to leave the crit sect, * we will need to move the above FindClipFormat to after the create * call and deal with gracefully freeing hPal on failure. pClipT * can change during callbacks. */ hPal = CreateDIBPalette(lpbih, lpbih->biClrUsed); if (hPal != NULL) { UT_FreeCBFormat(pClipT); pClipT->hData = hPal; GreSetPaletteOwner(hPal, OBJECT_OWNER_PUBLIC); } return (HANDLE)hPal; } /***************************************************************************\ * xxxGetDummyText * * Returns a handle to text from a dummy-format. * * History: * 24-Oct-1995 ChrisWil Created. \***************************************************************************/ HANDLE xxxGetDummyText( PWINDOWSTATION pwinsta, UINT fmt, PGETCLIPBDATA pgcd) { HANDLE hText; PCLIP pClipT; UINT uFmtMain; UINT uFmtAlt; BOOL bMain = TRUE; /* * Get the handle of the other text format available. */ switch (fmt) { case CF_TEXT: uFmtMain = CF_UNICODETEXT; uFmtAlt = CF_OEMTEXT; goto GetRealText; case CF_OEMTEXT: uFmtMain = CF_UNICODETEXT; uFmtAlt = CF_TEXT; goto GetRealText; case CF_UNICODETEXT: uFmtMain = CF_TEXT; uFmtAlt = CF_OEMTEXT; GetRealText: if ((pClipT = FindClipFormat(pwinsta, uFmtMain)) == NULL) { return NULL; } if (pClipT->hData != DUMMY_TEXT_HANDLE) { if (xxxGetClipboardData(pwinsta, uFmtMain, pgcd)) { break; } return NULL; } if ((pClipT = FindClipFormat(pwinsta, uFmtAlt)) == NULL) { return NULL; } if (pClipT->hData != DUMMY_TEXT_HANDLE) { bMain = FALSE; if (xxxGetClipboardData(pwinsta, uFmtAlt, pgcd)) { break; } } /* * Fall through to return a dummy handle. */ default: return NULL; } /* * Since xxxGetClipboardData leaves the critsect, we need to reacquire * pClipT. */ pClipT = FindClipFormat(pwinsta, bMain ? uFmtMain : uFmtAlt); if (pClipT == NULL) { RIPMSG1(RIP_WARNING, "Clipboard: GetDummyText, format 0x%x not available", bMain ? uFmtMain : uFmtAlt); return NULL; } /* * Return the type of the returned data. */ pgcd->uFmtRet = pClipT->fmt; hText = pClipT->hData; /* * Set the locale, since the text will need to be converted to another * format. */ if (pClipT = FindClipFormat(pwinsta, CF_LOCALE)) { pgcd->hLocale = pClipT->hData; } else { pgcd->hLocale = NULL; } return hText; } /***************************************************************************\ * xxxGetRenderData * * Returns a handle to delayed rendered data. This requires a call to the * client to supply the data. This causes us to regenerate our pointer to * pClip. * * History: * 24-Oct-1995 ChrisWil Created. \***************************************************************************/ HANDLE xxxGetRenderData( PWINDOWSTATION pwinsta, UINT fmt) { BOOL fClipboardChangedOld; TL tlpwndClipOwner; PCLIP pClip; DWORD_PTR lpdwResult; /* * If the handle is NULL, the data is delay rendered. This means we send * a message to the current clipboard owner and have it render the data * for us. */ if (pwinsta->spwndClipOwner != NULL) { BOOL fSucceeded; /* * Preserve the WSF_CLIPBOARDCHANGED flag before SendMessage and * restore the flag later. Thus we ignore the changes done to the * WSF_CLIPBOARDCHANGED flag by apps while rendering data in the * delayed rendering scheme. This avoids clipboard viewers from * painting twice. */ fClipboardChangedOld = (pwinsta->dwWSF_Flags & WSF_CLIPBOARDCHANGED) != 0; SET_FLAG(pwinsta->dwWSF_Flags, WSF_INDELAYEDRENDERING); ThreadLockAlways(pwinsta->spwndClipOwner, &tlpwndClipOwner); if (!xxxSendMessageTimeout(pwinsta->spwndClipOwner, WM_RENDERFORMAT, fmt, 0L, SMTO_ABORTIFHUNG, CB_DELAYRENDER_TIMEOUT, &lpdwResult)) { fSucceeded = FALSE; } else { fSucceeded = TRUE; } ThreadUnlock(&tlpwndClipOwner); SET_OR_CLEAR_FLAG(pwinsta->dwWSF_Flags, WSF_CLIPBOARDCHANGED, fClipboardChangedOld); CLEAR_FLAG(pwinsta->dwWSF_Flags, WSF_INDELAYEDRENDERING); if (!fSucceeded) { return NULL; } } if ((pClip = FindClipFormat(pwinsta, fmt)) == NULL) { RIPMSGF1(RIP_WARNING, "Meta Render/Clone format 0x%x not available", fmt); return NULL; } /* * We should have the handle now since it has been rendered. */ return pClip->hData; } /***************************************************************************\ * xxxGetClipboardData (API) * * Grabs a particular data object out of the clipboard. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 20-Aug-1991 EichiM UNICODE enabling \***************************************************************************/ HANDLE xxxGetClipboardData( PWINDOWSTATION pwinsta, UINT fmt, PGETCLIPBDATA pgcd) { PCLIP pClip; HANDLE hData; /* * Check the clipboard owner. */ if (pwinsta->ptiClipLock != PtiCurrent()) { RIPERR0(ERROR_CLIPBOARD_NOT_OPEN, RIP_VERBOSE, "GetClipboardData: clipboard not open"); return NULL; } /* * Make sure the format is available. */ if ((pClip = FindClipFormat(pwinsta, fmt)) == NULL) { RIPMSG1(RIP_VERBOSE, "Clipboard: Requested format 0x%lX not available", fmt); return NULL; } /* * If this is a DUMMY_META*_HANDLE it means that the other metafile * format was set in as a delay render format and we should ask for that * format to get the metafile because the app has not told us they know * about this format. */ if (IsMetaDummyHandle(pClip->hData)) { if (fmt == CF_ENHMETAFILE) { fmt = CF_METAFILEPICT; } else if (fmt == CF_METAFILEPICT) { fmt = CF_ENHMETAFILE; } else { RIPMSG0(RIP_WARNING, "Clipboard: Meta Render/Clone expects a metafile type"); } if ((pClip = FindClipFormat(pwinsta, fmt)) == NULL) { RIPMSG1(RIP_WARNING, "Clipboard: Meta Render/Clone format 0x%x not available", fmt); return NULL; } } /* * This is the data we're returning, unless it's a dummy or render handle. */ hData = pClip->hData; /* * We are dealing with non-handles. Retrieve the real data through these * inline-routines. NOTE: These make recursive calls to * xxxGetClipboardData(), so care must be taken to assure the pClip is * pointing to what we think it's pointing to. */ if (hData == NULL || hData == DUMMY_METARENDER_HANDLE) { hData = xxxGetRenderData(pwinsta, fmt); } else if (hData == DUMMY_DIB_HANDLE) { switch (fmt) { case CF_DIB: hData = xxxGetDummyDib(pwinsta, pgcd); break; case CF_DIBV5: hData = xxxGetDummyDibV5(pwinsta, pgcd); break; case CF_BITMAP: hData = xxxGetDummyBitmap(pwinsta, pgcd); break; case CF_PALETTE: hData = xxxGetDummyPalette(pwinsta, pgcd); break; } } else if (hData == DUMMY_TEXT_HANDLE) { hData = xxxGetDummyText(pwinsta, fmt, pgcd); } else { /* * This path took no callbacks, so we know pClip is OK. */ if (pgcd) { pgcd->fGlobalHandle = pClip->fGlobalHandle; } return hData; } /* * The callbacks for dummy handle resolution have possibly invalidated * pClip -- recreate it. */ if ((pClip = FindClipFormat(pwinsta, fmt)) == NULL) { RIPMSG1(RIP_VERBOSE, "Clipboard: Requested format 0x%x not available", fmt); return NULL; } /* * Return if this is a global-handle. */ if (pgcd) { pgcd->fGlobalHandle = pClip->fGlobalHandle; } return hData; } /***************************************************************************\ * FindClipFormat * * Finds a particular clipboard format in the clipboard, returns a pointer * to it, or NULL. If a pointer is found, on return the clipboard is locked * and pwinsta->pClipBase has been updated to point to the beginning of the * clipboard. * * History: * 18-Nov-1990 ScottLu Ported from Win3. \***************************************************************************/ PCLIP FindClipFormat( PWINDOWSTATION pwinsta, UINT format) { PCLIP pClip; int iFmt; if (format != 0 && ((pClip = pwinsta->pClipBase) != NULL)) { for (iFmt = pwinsta->cNumClipFormats; iFmt-- != 0;) { if (pClip->fmt == format) { return pClip; } pClip++; } } return NULL; } /***************************************************************************\ * _GetPriorityClipboardFormat (API) * * This api allows an application to look for any one of a range of * clipboard formats in a predefined search order. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 11-Feb-1991 JimA Added access checks. \***************************************************************************/ int _GetPriorityClipboardFormat( PUINT lpPriorityList, int cfmts) { PWINDOWSTATION pwinsta; PCLIP pClip; int iFmt; UINT fmt; /* * Blow it off if the caller does not have the proper access rights. */ if ((pwinsta = CheckClipboardAccess()) == NULL) { return 0; } /* * If there is no clipboard or no objects in the clipboard, return 0. */ if (pwinsta->cNumClipFormats == 0 || pwinsta->pClipBase == NULL) { return 0; } /* * Look through the list for any of the formats in lpPriorityList. */ while (cfmts-- > 0) { fmt = *lpPriorityList; if (fmt != 0) { pClip = pwinsta->pClipBase; for (iFmt = pwinsta->cNumClipFormats; iFmt-- != 0; pClip++) { if (pClip->fmt == fmt) { return fmt; } } } lpPriorityList++; } /* * There is no matching format, so return -1. */ return -1; } /***************************************************************************\ * xxxSetClipboardViewer (API) * * Sets the clipboard viewer window. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 11-Feb-1991 JimA Added access checks. \***************************************************************************/ PWND xxxSetClipboardViewer( PWND pwndClipViewerNew) { TL tlpwinsta; PWINDOWSTATION pwinsta; HWND hwndClipViewerOld; PTHREADINFO ptiCurrent; CheckLock(pwndClipViewerNew); /* * Do not let a destroyed window be locked into the winsta. See * _OpenClipboard for more details. */ if (pwndClipViewerNew != NULL && TestWF(pwndClipViewerNew, WFDESTROYED)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "Destroyed pwnd 0x%p trying to become clipboard viewer", pwndClipViewerNew); return NULL; } /* * Blow it off if the caller does not have the proper access rights. The * NULL return really doesn't indicate an error but the supposed viewer * will never receive any clipboard messages, so it shouldn't cause any * problems. */ if ((pwinsta = CheckClipboardAccess()) == NULL) { return NULL; } ptiCurrent = PtiCurrent(); ThreadLockWinSta(ptiCurrent, pwinsta, &tlpwinsta); hwndClipViewerOld = HW(pwinsta->spwndClipViewer); Lock(&pwinsta->spwndClipViewer, pwndClipViewerNew); xxxDrawClipboard(pwinsta); ThreadUnlockWinSta(ptiCurrent, &tlpwinsta); if (hwndClipViewerOld != NULL) { return RevalidateHwnd(hwndClipViewerOld); } return NULL; } /***************************************************************************\ * xxxChangeClipboardChain (API) * * Changes the clipboard viewer chain. * * History: * 18-Nov-1990 ScottLu Ported from Win3. * 11-Feb-1991 JimA Added access checks. \***************************************************************************/ BOOL xxxChangeClipboardChain( PWND pwndRemove, PWND pwndNewNext) { TL tlpwinsta; PWINDOWSTATION pwinsta; BOOL result; TL tlpwndClipViewer; PTHREADINFO ptiCurrent; CheckLock(pwndRemove); CheckLock(pwndNewNext); /* * Blow it off if the caller does not have the proper access rights. */ if ((pwinsta = CheckClipboardAccess()) == NULL) { return FALSE; } /* * pwndRemove should be this thread's window, pwndNewNext will either be * NULL or another thread's window. */ ptiCurrent = PtiCurrent(); if (GETPTI(pwndRemove) != ptiCurrent) { RIPMSG0(RIP_WARNING, "Clipboard: ChangeClipboardChain will not remove cross threads"); return FALSE; } if (pwinsta->spwndClipViewer == NULL) { RIPMSG0(RIP_WARNING, "Clipboard: ChangeClipboardChain has no viewer window"); return FALSE; } ThreadLockWinSta(ptiCurrent, pwinsta, &tlpwinsta); if (pwndRemove == pwinsta->spwndClipViewer) { Lock(&pwinsta->spwndClipViewer, pwndNewNext); result = TRUE; } else { ThreadLockAlways(pwinsta->spwndClipViewer, &tlpwndClipViewer); result = (BOOL)xxxSendMessage(pwinsta->spwndClipViewer, WM_CHANGECBCHAIN, (WPARAM)HW(pwndRemove), (LPARAM)HW(pwndNewNext)); ThreadUnlock(&tlpwndClipViewer); } ThreadUnlockWinSta(ptiCurrent, &tlpwinsta); return result; } /***************************************************************************\ * xxxDisownClipboard * * Disowns the clipboard so someone else can grab it. * * pwndClipOwner is the pwnd that is the reason for disowning the clipboard * when that window is deleted. * * History: * 18-Jun-1991 DarrinM Ported from Win3. \***************************************************************************/ VOID xxxDisownClipboard( PWND pwndClipOwner) { TL tlpwinsta; PWINDOWSTATION pwinsta; int iFmt; int cFmts; PCLIP pClip; PCLIP pClipOut; BOOL fKeepDummyHandle; PTHREADINFO ptiCurrent; if ((pwinsta = CheckClipboardAccess()) == NULL) { return; } ptiCurrent = PtiCurrent(); ThreadLockWinSta(ptiCurrent, pwinsta, &tlpwinsta); xxxSendClipboardMessage(pwinsta, WM_RENDERALLFORMATS); pClipOut = pClip = pwinsta->pClipBase; fKeepDummyHandle = FALSE; for (cFmts = 0, iFmt = pwinsta->cNumClipFormats; iFmt-- != 0;) { /* * We have to remove the Dummy handles also if the corresponding * valid handles are NULL; We should not remove the dummy handles if * the corresponding valid handles are not NULL. The following code * assumes that only one dummy handle is possible and that can appear * only after the corresponding valid handle in the pClip linked list. */ if (pClip->hData != NULL) { if ((pClip->hData != DUMMY_TEXT_HANDLE) || ((pClip->hData == DUMMY_TEXT_HANDLE) && fKeepDummyHandle)) { cFmts++; *pClipOut++ = *pClip; if (IsTextHandle(pClip->fmt, pClip->hData)) { fKeepDummyHandle = TRUE; } } } pClip++; } /* * Unlock the clipboard owner if the owner is still the window we were * cleaning up for. */ if (pwndClipOwner == pwinsta->spwndClipOwner) { Unlock(&pwinsta->spwndClipOwner); } else { RIPMSGF2(RIP_WARNING, "pwndClipOwner changed from 0x%p to 0x%p", pwndClipOwner, pwinsta->spwndClipOwner); } /* * If number of formats changed, redraw. */ if (cFmts != pwinsta->cNumClipFormats) { pwinsta->dwWSF_Flags |= WSF_CLIPBOARDCHANGED; pwinsta->iClipSequenceNumber++; } pwinsta->cNumClipFormats = cFmts; /* * If anything changed, redraw, and make sure the data type munging is * done. Else we will lose them when xxxDrawClipboard clears the * WSF_CLIPBOARDCHANGED flag. */ if (pwinsta->dwWSF_Flags & WSF_CLIPBOARDCHANGED) { xxxDrawClipboard(pwinsta); MungeClipData(pwinsta); } ThreadUnlockWinSta(ptiCurrent, &tlpwinsta); } /***************************************************************************\ * ForceEmptyClipboard * * We're logging off. Force the clipboard contents to go away. * * 23-Jul-1992 ScottLu Created. \***************************************************************************/ VOID ForceEmptyClipboard( PWINDOWSTATION pwinsta) { /* * This will be NULL for a non-GUI thread. */ pwinsta->ptiClipLock = ((PTHREADINFO)(W32GetCurrentThread())); Unlock(&pwinsta->spwndClipOwner); Unlock(&pwinsta->spwndClipViewer); Unlock(&pwinsta->spwndClipOpen); xxxEmptyClipboard(pwinsta); /* * If the windowstation is dying, don't bother closing the clipboard. */ if (!(pwinsta->dwWSF_Flags & WSF_DYING)) { xxxCloseClipboard(pwinsta); } }