You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2821 lines
84 KiB
2821 lines
84 KiB
/****************************** 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);
|
|
}
|
|
}
|