Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2266 lines
74 KiB

/******************************Module*Header*******************************\
* Module Name: OPENDC.CXX
*
* Handles DC creation and driver loading.
*
* Copyright (c) 1990-1999 Microsoft Corporation
\**************************************************************************/
#include "precomp.hxx"
extern FLONG flRaster(ULONG, FLONG);
/******************************Exported*Routine****************************\
* GreCreateDisplayDC
*
* Opens a DC on the specified display PDEV.
* Allocates space for a DC, fills in the defaults.
* If successfull, increments the PDEV reference count.
*
\**************************************************************************/
HDC
GreCreateDisplayDC(
HDEV hdev,
ULONG iType,
BOOL bAltType)
{
GDIFunctionID(GreCreateDisplayDC);
HDC hdc = (HDC) NULL;
//
// We hold the devlock to protect against dynamic mode changes
// while we're copying mode specific information to the DC.
//
PDEVOBJ pdo(hdev);
DEVLOCKOBJ dlo(pdo);
DCMEMOBJ dcmo(iType, bAltType);
if (dcmo.bValid())
{
//
// Copy info from the PDEV into the DC.
//
dcmo.pdc->ppdev((PDEV *) pdo.hdev());
dcmo.pdc->flGraphicsCaps(pdo.flGraphicsCaps()); // cache it for later use by graphics output functions
dcmo.pdc->flGraphicsCaps2(pdo.flGraphicsCaps2()); // cache it for later use by graphics output functions
dcmo.pdc->dhpdev(pdo.dhpdev());
dcmo.hsemDcDevLock(pdo.hsemDevLock());
if (iType == DCTYPE_MEMORY)
{
SIZEL sizlTemp;
sizlTemp.cx = 1;
sizlTemp.cy = 1;
dcmo.pdc->sizl(sizlTemp);
}
else
{
dcmo.pdc->sizl(pdo.sizl());
//
// The info and direct DC's for the screen need to grab
// the semaphore before doing output, the memory DC's will
// grab the semaphore only if a DFB is selected.
//
if (iType == DCTYPE_DIRECT)
{
dcmo.bSynchronizeAccess(pdo.bDisplayPDEV());
dcmo.pdc->vDisplay(pdo.bDisplayPDEV());
//
// If this DC is created against with disabled PDEV,
// mark it as full screen mode. All hdc on disbaled
// PDEV should be marked as 'in fullscreen'.
// (see PDEVOBJ::bDisabled(BOOL bDisable))
//
dcmo.pdc->bInFullScreen(pdo.bDisabled());
if (!pdo.bPrinter())
dcmo.pdc->pSurface(pdo.pSurface());
}
}
//
// Call the region code to set a default clip region.
//
if (dcmo.pdc->bSetDefaultRegion())
{
// If display PDEV, select in the System stock font.
dcmo.vSetDefaultFont(pdo.bDisplayPDEV());
// set user mode VisRect
dcmo.pdc->vUpdate_VisRect(dcmo.pdc->prgnVis());
if (GreSetupDCAttributes((HDC)(dcmo.pdc->hGet())))
{
// Mark the DC as permanent, hold the PDEV reference.
dcmo.vKeepIt();
// This will permanently increase the ref count to indicate
// a new DC has been created.
pdo.vReferencePdev();
// turn on the DC_PRIMARY_DISPLAY flag for primary dc's
if (hdev == UserGetHDEV())
{
dcmo.pdc->ulDirtyAdd(DC_PRIMARY_DISPLAY);
}
// finish initializing the DC.
hdc = dcmo.hdc();
}
else
{
// DCMEMOBJ will be freed, delete vis region
dcmo.pdc->vReleaseVis();
// delete LOGFONT in DC which set by dcmo.vSetDefaultFont() in above.
DEC_SHARE_REF_CNT_LAZY_DEL_LOGFONT(dcmo.pdc->plfntNew());
}
}
if (hdc == NULL)
{
// dec reference counts on brush, pen and others
// (which incremented in DCOBJ::DCOBJ())
DEC_SHARE_REF_CNT_LAZY0(dcmo.pdc->pbrushFill());
DEC_SHARE_REF_CNT_LAZY0(dcmo.pdc->pbrushLine());
DEC_SHARE_REF_CNT_LAZY_DEL_COLORSPACE(dcmo.pdc->pColorSpace());
}
#if DBG
else
{
// Enable visrgn validation from this point
GreValidateVisrgn(hdc, TRUE);
}
#endif
}
return hdc;
}
/******************************Public*Routine******************************\
* NtGdiCreateMetafileDC()
*
* History:
* 01-Jun-1995 -by- Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/
HDC
APIENTRY
NtGdiCreateMetafileDC(
HDC hdc
)
{
HDC hdcNew = NULL;
if (hdc)
{
//
// Lock down the given DC.
//
DCOBJ dco(hdc);
if (dco.bValid())
{
//
// Allocate the DC, fill it with defaults.
//
hdcNew = GreCreateDisplayDC((HDEV) dco.pdc->ppdev(), DCTYPE_INFO, TRUE);
}
}
else
{
//
// We must call USER to get the current PDEV\HDEV for this thread
// This should end up right back in GreCreateDisplayDC
//
hdcNew = UserGetDesktopDC(DCTYPE_INFO, TRUE, FALSE);
}
return hdcNew;
}
/******************************Public*Routine******************************\
* NtGdiCreateCompatibleDC()
*
* History:
* 01-Nov-1994 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
HDC
APIENTRY
NtGdiCreateCompatibleDC(
HDC hdc
)
{
HDC hdcRet = GreCreateCompatibleDC(hdc);
//
// Make sure user attributes are added to this dc
//
#if DBG
if (hdcRet)
{
DCOBJ dco(hdcRet);
if (dco.bValid())
{
ASSERTGDI((PENTRY_FROM_POBJ((POBJ)dco.pdc))->pUser != NULL,"NtGdiCreateCompatibleDC: pUser == NULL");
}
}
#endif
return(hdcRet);
}
/******************************Public*Routine******************************\
* HDC GreCreateCompatibleDC(hdc)
*
* History:
* 01-Jun-1995 -by- Andre Vachon [andreva]
* Wrote it.
*
\**************************************************************************/
HDC APIENTRY GreCreateCompatibleDC(HDC hdc)
{
HDC hdcNew = NULL;
if (hdc)
{
//
// Lock down the given DC.
//
DCOBJ dco(hdc);
if (dco.bValid())
{
//
// Allocate the DC, fill it with defaults.
//
hdcNew = GreCreateDisplayDC((HDEV) dco.pdc->ppdev(),
DCTYPE_MEMORY,
FALSE);
//
// If the compatible DC for a mirrored DC then mirror it.
//
if (hdcNew)
{
DWORD dwLayout = dco.pdc->dwLayout();
if (dwLayout & LAYOUT_ORIENTATIONMASK)
{
GreSetLayout(hdcNew, -1, dwLayout);
}
}
}
}
else
{
hdcNew = UserGetDesktopDC(DCTYPE_MEMORY, FALSE, FALSE);
}
return hdcNew;
}
/******************************Public*Routine******************************\
* BOOL GreSelectRedirectionBitmap
*
* This routine selects a redirection bitmap into a redirection DC. A single
* redirection bitmap can be selected into multiple redirection DCs.
* Note: The caller needs to hold the devlock.
* Note: The redirection bitmap must be in system memory because GDI may
* attempt to convert it to a memory bitmap in the future (mode change,
* speed optimizations in some code paths, etc.) and we cannot do that
* here because there's no easy way of getting the list of redirection
* DCs on that surface.
*
* Arguments:
*
* hdc -- The redirection DC as created by GreCreateRedirectionDC.
*
* hbitmap -- The redirection bitmap to be selected into hdc. This needs
* to be a memory bitmap (but not DIB-section) which is compatible with
* the display pdev.
*
* Return Value:
*
* True upon success
*
* History:
* 21-Sep-1998 -by- Ori Gershony [orig]
* Wrote it.
*
\**************************************************************************/
BOOL
APIENTRY
GreSelectRedirectionBitmap(
HDC hdc,
HBITMAP hBitmap
)
{
ULONG ulRet=TRUE;
DCOBJA dco(hdc);
if (dco.bValid())
{
SURFACE* pSurface = NULL;
PDEVOBJ po(dco.hdev());
#if DBG
po.vAssertDevLock();
#endif
if (hBitmap == NULL)
{
dco.pdc->bRedirection(FALSE);
pSurface = po.pSurface();
}
else
{
dco.pdc->bRedirection(TRUE);
SURFREF surfref((HSURF) hBitmap);
if (surfref.bValid())
{
pSurface = surfref.ps;
ASSERTGDI((pSurface->iType() == STYPE_BITMAP) && (!(pSurface->fjBitmap() & BMF_NOTSYSMEM)),
"GreSelectRedirectionBitmap: bitmap must be in system memory");
ASSERTGDI(!pSurface->hDIBSection(),
"GreSelectRedirectionBitmap: cannot select DIB-section into a redirection DC");
PPALETTE ppalReference;
ASSERTGDI(bIsCompatible(&ppalReference, NULL, pSurface, dco.hdev()),
"GreSelectRedirectionBitmap: hBitmap not compatible with display hdev");
//
// Mark bitmap as redirection bitmap if not one already
//
if (!pSurface->bRedirection())
{
pSurface->vSetRedirection();
}
}
}
if (pSurface != NULL)
{
//
// Replace the surface in all the saved DCs too.
//
if (dco.lSaveDepth() > 1)
{
ulRet = GreSelectRedirectionBitmap (dco.hdcSave(), hBitmap);
//
// The recursive call above should never fail, and we are not
// setup to deal with failure correctly (need to undo change
// to DC redirection flag, etc.). We can add code to do that,
// but user (who calls us) cannot deal with failure either. So
// instead we assert that this call succeeded.
//
ASSERTGDI(ulRet == TRUE,
"GreSelectRedirectionBitmap: Recursive call on hdcSave failed");
}
if (ulRet)
{
//
// Replace the pSurf reference in the DCLEVEL
//
dco.pdc->pSurface(pSurface);
//
// Set the size to that of the new surface
//
dco.pdc->sizl(pSurface->sizl());
//
// Make sure that the surface pointers in any EBRUSHOBJ's get
// updated, by ensuring that vInitBrush() gets called the next
// time any brush is used in this DC.
//
dco.pdc->flbrushAdd(DIRTY_BRUSHES);
//
// Note: we don't set the visrgn so that if user wants to reuse this dc over
// the same redirection bitmap it won't have to recompute the visrgn (though it
// will have to recompute it if it will be reused for a different redirection
// bitmap).
//
}
}
else
{
ulRet = FALSE;
}
}
else
{
ulRet = FALSE;
}
return ulRet;
}
/******************************Exported*Routine****************************\
* hdcOpenDCW
*
* Opens a DC for a device which is not a display. GDI should call this
* function from within DevOpenDC, in the case that an hdc is not passed
* in. This call locates the device and creates a new PDEV. The physical
* surface associated with this PDEV will be distinct from all other
* physical surfaces.
*
* The window manager should not call this routine unless it is providing
* printing services for an application.
*
* pwszDriver
*
* This points to a string which identifies the device driver.
* The given string must be a fully qualified path name.
*
* pdriv
*
* This is a pointer to the DEVMODEW block.
*
* Since a single driver, like PSCRIPT.DRV, may support multiple
* different devices, the szDeviceName field defines which device to
* use.
*
* This structure also contains device specific data in abGeneralData.
* This data is set by the device driver in bPostDeviceModes.
*
* If the pdriv pointer is NULL, the device driver assumes some default
* configuration.
*
* iType
*
* Identifies the type of the DC. Must be one of DCTYPE_DIRECT,
* DCTYPE_INFO, or DCTYPE_MEMORY.
*
* Returns:
*
* HDC - A handle to the DC.
*
\**************************************************************************/
class PRINTER
{
public:
HANDLE hSpooler_;
BOOL bKeep;
public:
PRINTER(PWSZ pwszDevice,DEVMODEW *pdriv,HANDLE hspool );
~PRINTER()
{
if (!bKeep && (hSpooler_ != (HANDLE) NULL))
ClosePrinter(hSpooler_);
}
BOOL bValid() {return(hSpooler_ != (HANDLE) NULL);}
VOID vKeepIt() {bKeep = TRUE;}
HANDLE hSpooler() {return(hSpooler_);}
};
// PRINTER constructor -- Attempts to open a spooler connection to the
// printer.
PRINTER::PRINTER(
PWSZ pwszDevice,
DEVMODEW *pdriv,
HANDLE hspool )
{
bKeep = FALSE;
PRINTER_DEFAULTSW defaults;
defaults.pDevMode = pdriv;
defaults.DesiredAccess = PRINTER_ACCESS_USE;
//
// Attempt to open the printer for spooling journal files.
// NOTE: For debugging, a global flag disables journaling.
//
defaults.pDatatype = (LPWSTR) L"RAW";
if (hspool)
{
hSpooler_ = hspool;
}
else
{
if (!OpenPrinterW(pwszDevice,&hSpooler_,&defaults))
{
//
// It's not a printer. OpenPrinterW doesn't guarantee the value
// of hSpooler in this case, so we have to clear it.
//
hSpooler_ = (HANDLE) NULL;
}
}
return;
}
/******************************Public*Routine******************************\
* See comments above.
*
* History:
* Andre Vachon [andreva]
*
\**************************************************************************/
HDC hdcOpenDCW(
PWSZ pwszDevice, // The device driver name.
DEVMODEW *pdriv, // Driver data.
ULONG iType, // Identifies the type of DC to create.
HANDLE hspool, // do we already have a spooler handle?
PREMOTETYPEONENODE prton,
DRIVER_INFO_2W *pDriverInfo2, // we pass in pDriverInfo for UMPD, NULL otherwise
PVOID pUMdhpdev
)
{
HDC hdc = (HDC) 0; // Prepare for the worst.
DWORD cbNeeded = 0;
PVOID mDriverInfo;
BOOL bUMPD = pDriverInfo2 ? TRUE: FALSE; // TRUE if User Mode driver
TRACE_INIT(("\nhdcOpenDCW: ENTERING\n"));
//
// Attempt to open a display DC.
//
if (pwszDevice && !bUMPD)
{
PMDEV pmdev = NULL;
HDEV hdev = NULL;
UNICODE_STRING usDevice;
RtlInitUnicodeString(&usDevice,
pwszDevice);
if (pdriv == NULL)
{
//
// If no DEVMODE is present, then we just want to create a DC on
// the specific device.
// This will allow an application to make some escape calls to a
// specific device\driver.
//
hdev = DrvGetHDEV(&usDevice);
}
else
{
//
// We have to acquire the USER lock to synchronize with
// ChangeDisplaySettings.
//
UserEnterUserCritSec();
TRACE_INIT(("hdcOpenDCW: Trying to open as a second display device\n"));
//
// Only let the user do this if the device is not part of the
// desktop.
//
pmdev = DrvCreateMDEV(&usDevice,
pdriv,
(PVOID) (ULONG_PTR)0xFFFFFFFF,
GRE_DISP_CREATE_NODISABLE |
GRE_DISP_NOT_APARTOF_DESKTOP,
NULL,
KernelMode,
GRE_RAWMODE,
FALSE); // buffer is captured in NtGdiOpenDCW()
if (!pmdev)
{
DWORD dwDesktopId;
if (UserGetCurrentDesktopId(&dwDesktopId))
{
//
// Try match from current desktop display.
// but don't create new display instance.
//
pmdev = DrvCreateMDEV(&usDevice,
pdriv,
(PVOID) (ULONG_PTR) dwDesktopId,
GRE_DISP_CREATE_NODISABLE |
GRE_DISP_NOT_APARTOF_DESKTOP,
NULL,
KernelMode,
GRE_RAWMODE,
FALSE);
}
}
UserLeaveUserCritSec();
if (pmdev)
{
TRACE_INIT(("Drv_Trace: CreateExclusiveDC: We have an hdev\n"));
ASSERTGDI(pmdev->chdev == 1,"hdcOpenDCW(): pmdev->chdev != 1");
hdev = pmdev->Dev[0].hdev;
}
}
if (hdev)
{
hdc = GreCreateDisplayDC(hdev, DCTYPE_DIRECT, FALSE);
if (hdc)
{
if (pmdev || hdev)
{
DCOBJ dco(hdc);
PDEVOBJ po(dco.hdev());
//
// Dereference the object since we want DeleteDC
// to automatically destroy the PDEV.
//
// This basically counteracts the extra reference that it done
// by DrvCreateMDEV(), or DrvGetHDEV().
//
po.vUnreferencePdev();
}
}
else
{
TRACE_INIT(("hdcOpenDCW: Failed to get DC\n"));
if (pmdev)
{
DrvDestroyMDEV(pmdev);
}
else
{
PDEVOBJ po(hdev);
po.vUnreferencePdev();
}
}
}
if (pmdev)
{
VFREEMEM(pmdev);
}
}
//
// Attempt to open a new printer DC.
//
if (hdc == NULL)
{
//
// Open the spooler connection to the printer.
// Allocate space for DRIVER_INFO.
//
PRINTER print(pwszDevice, pdriv, hspool);
if (print.bValid())
{
if (!bUMPD)
{
if (mDriverInfo = PALLOCMEM(512, 'pmtG'))
{
//
// Fill the DRIVER_INFO.
//
if (!GetPrinterDriverW(
print.hSpooler(),
NULL,
2,
(LPBYTE) mDriverInfo,
512,
&cbNeeded))
{
//
// Call failed - free the memory.
//
VFREEMEM(mDriverInfo);
mDriverInfo = NULL;
//
// Get more space if we need it.
//
if ((EngGetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(cbNeeded > 0))
{
if (mDriverInfo = PALLOCMEM(cbNeeded, 'pmtG'))
{
if (!GetPrinterDriverW(print.hSpooler(),
NULL,
2,
(LPBYTE) mDriverInfo,
cbNeeded,
&cbNeeded))
{
VFREEMEM(mDriverInfo);
mDriverInfo = NULL;
}
}
}
}
}
}
else
{
//
// we have a pDriverInfo2 passed in for UMPD
//
mDriverInfo = pDriverInfo2;
}
if (mDriverInfo != (PVOID) NULL)
{
PLDEV pldev;
if ( bUMPD)
{
//
// we fake a pldev for each LoadDriver call.
// the real driver list is kept at the client side as a pUMPD list
//
pldev = UMPD_ldevLoadDriver(((DRIVER_INFO_2W *)mDriverInfo)->pDriverPath,
LDEV_DEVICE_PRINTER);
}
else
{
pldev = ldevLoadDriver(((DRIVER_INFO_2W *)mDriverInfo)->pDriverPath,
LDEV_DEVICE_PRINTER);
}
if (pldev == NULL)
{
SAVE_ERROR_CODE(ERROR_BAD_DRIVER_LEVEL);
}
else
{
//
// Create a PDEV. If no DEVMODEW passed in from above,
// use the default from the printer structure.
//
PDEVOBJ po(pldev,
(PDEVMODEW) pdriv,
pwszDevice,
((DRIVER_INFO_2W *)mDriverInfo)->pDataFile,
((DRIVER_INFO_2W *)mDriverInfo)->pName,
print.hSpooler(),
prton,
NULL,
NULL,
bUMPD);
if (po.bValid())
{
//
// Make a note that this is a printer.
//
po.bPrinter(TRUE);
//
// Allocate the DC, fill it with defaults.
//
hdc = GreCreateDisplayDC(po.hdev(),
iType,
TRUE);
//
// If the DC was properly create, keep the printer
// object so the printer stays open
if (hdc)
{
print.vKeepIt();
if (bUMPD && pUMdhpdev)
{
__try
{
ProbeForWritePointer(pUMdhpdev);
*(PUMDHPDEV *)pUMdhpdev = (PUMDHPDEV)po.dhpdev();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
bDeleteDCInternal(hdc, FALSE, FALSE);
hdc = 0;
}
}
}
//
// Always delete the PEV reference count.
// If the DC was created, the DC keeps the ref count
// at 1, and the PDEV will get destroyed when the
// DC gets destroyed.
// If the DC was not created properly, this will cause
// the PDEV to get destroyed immediately.
//
po.vUnreferencePdev();
}
else
{
if (!bUMPD)
{
ldevUnloadImage(pldev);
}
else
{
UMPD_ldevUnloadImage(pldev);
}
}
}
if (!bUMPD)
{
VFREEMEM(mDriverInfo);
}
}
}
}
if (hdc == (HDC) NULL)
{
WARNING("opendc.cxx: failed to create DC in hdcOpenDCW\n");
}
return(hdc);
}
/******************************Public*Routine******************************\
* GreResetDCInternal
*
* Reset the mode of a DC. The DC returned will be a different DC than
* the original. The only common piece between the original DC and the
* new one is the hSpooler.
*
* There are a number of intresting problems to be carefull of. The
* original DC can be an info DC. The new one will always be a direct DC.
*
* Also, it is important to be carefull of the state of the DC when this
* function is called and the effects of journaling vs non journaling.
* In the case of journaling, the spooler is responsible for doing a CreateDC
* to play the journal file to. For this reason, the spooler must have the
* current DEVMODE. For this reason, ResetDC must call ResetPrinter for
* spooled DC's.
*
* ResetDC can happen at any time other than between StartPage-EndPage, even
* before StartDoc.
*
*
* History:
* 13-Jan-1994 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
extern "C" BOOL GreResetDCInternal(
HDC hdc,
DEVMODEW *pdmw, // Driver data.
BOOL *pbBanding,
DRIVER_INFO_2W *pDriverInfo2,
PVOID ppUMdhpdev)
{
BOOL bSurf;
BOOL bTempInfoDC = FALSE;
HDC hdcNew;
BOOL bRet = FALSE;
// we need this set of brackets so the DC's get unlocked before we try to delete
// the dc>
{
DCOBJ dco(hdc);
if (!dco.bValid())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
else
{
// if this has been made into a TempInfoDC for printing, undo it now
bTempInfoDC = dco.pdc->bTempInfoDC();
if (bTempInfoDC)
dco.pdc->bMakeInfoDC(FALSE);
PDEVOBJ po(dco.hdev());
// get list of Type1 remote type one fonts if there is one and transfer
// it accross PDEV's.
PREMOTETYPEONENODE prton = po.RemoteTypeOneGet();
po.RemoteTypeOneSet(NULL);
// This call only makes sense on RASTER technology printers.
if (!dco.bKillReset() &&
!(dco.dctp() == DCTYPE_MEMORY) &&
(po.bPrinter()))
{
// First, remember if a surface needs to be created
bSurf = dco.bHasSurface();
// Now, clean up the DC
if (dco.bCleanDC())
{
// If there are any outstanding references to this PDEV, fail.
if (((PDEV *) po.hdev())->cPdevRefs == 1)
{
// create the new DC
hdcNew = hdcOpenDCW(L"",
pdmw,
DCTYPE_DIRECT,
po.hSpooler(),
prton,
pDriverInfo2,
ppUMdhpdev);
if (hdcNew)
{
// don't want to delete the spooler handle since it
// is in the new DC
po.hSpooler(NULL);
// lock down the new DC and PDEV
DCOBJ dcoNew(hdcNew);
if (!dcoNew.bValid())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
else
{
// Transfer any remote fonts
dcoNew.PFFListSet(dco.PFFListGet());
dco.PFFListSet(NULL);
// Transfer any color transform
dcoNew.CXFListSet(dco.CXFListGet());
dco.CXFListSet(NULL);
PDEVOBJ poNew((HDEV) dcoNew.pdc->ppdev());
// let the driver know
PFN_DrvResetPDEV rfn = PPFNDRV(po,ResetPDEV);
if (rfn != NULL)
{
(*rfn)(po.dhpdev(),poNew.dhpdev());
}
// now swap the two handles
{
MLOCKFAST mlo;
BOOL bRes = HmgSwapLockedHandleContents((HOBJ)hdc,0,(HOBJ)hdcNew,0,DC_TYPE);
ASSERTGDI(bRes,"GreResetDC - SwapHandleContents failed\n");
}
bRet = TRUE;
}
}
}
}
}
}
// DON'T DO ANYTHING HERE, the dcobj's don't match the handles, so
// unlock them first
}
if (bRet)
{
// got a new dc, get rid of the old one (remember the handles have
// been swapped)
bDeleteDCInternal(hdcNew,TRUE,FALSE);
// now deal with the new one
DCOBJ newdco(hdc);
if (!newdco.bValid())
{
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
bRet = FALSE;
}
else
{
PDEVOBJ newpo(newdco.hdev());
// Create a new surface for the DC.
if (bSurf)
{
if (!newpo.bMakeSurface())
{
bRet = FALSE;
}
else
{
newdco.pdc->pSurface(newpo.pSurface());
*pbBanding = newpo.pSurface()->bBanding();
if( *pbBanding )
{
// if banding set Clip rectangle to size of band
newdco.pdc->sizl((newpo.pSurface())->sizl());
newdco.pdc->bSetDefaultRegion();
}
PFN_DrvStartDoc pfnDrvStartDoc = PPFNDRV(newpo, StartDoc);
(*pfnDrvStartDoc)(newpo.pSurface()->pSurfobj(),NULL,0);
}
}
else
{
// important to set this to FALSE is a surface has not yet been created
// ie StartDoc has not yet been called.
*pbBanding = FALSE;
}
// if the original was a tempinfo dc for printing, this one needs to be too.
if (bRet && bTempInfoDC)
{
newdco.pdc->bMakeInfoDC(TRUE);
}
}
}
return(bRet);
}
/******************************Public*Routine******************************\
* bDynamicMatchEnoughForModeChange
*
* We can dynamically change modes only if the new mode matches the old
* in certain respects. This is because, for example, we don't have code
* written to track down all the places where flGraphicsCaps has been copied,
* and then change it asynchronously.
*
* History:
* 8-Feb-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL
bDynamicMatchEnoughForModeChange(
HDEV hdevOld,
HDEV hdevNew
)
{
PDEVOBJ poOld(hdevOld);
PDEVOBJ poNew(hdevNew);
BOOL b = TRUE;
// We would get quite confused with converting monochrome bitmaps if
// we were to handle 1bpp:
if ((poOld.iDitherFormat() == BMF_1BPP) ||
(poNew.iDitherFormat() == BMF_1BPP))
{
WARNING("bDynamicMatchEnoughForModeChange: Can't handle 1bpp");
b = FALSE;
}
// Some random stuff must be the same between the old instance and
// the new:
//
// We impose the restriction that some flGraphicsCaps flags must stay
// the same with the new mode. Specifically, we can't allow the
// following to change:
//
// o We don't allow GCAPS_HIGHRESTEXT to change because ESTROBJ::
// vInit needs to look at it sometimes without acquiring a lock,
// and it's pretty much a printer specific feature anyway.
// o We don't allow GCAPS_FORCEDITHER to change because vInitBrush
// needs to look for this flag without holding a lock, and because
// this flag is intended to be used only by printer drivers.
if ((poNew.flGraphicsCaps() ^ poOld.flGraphicsCaps())
& (GCAPS_HIGHRESTEXT | GCAPS_FORCEDITHER))
{
WARNING("bDynamicMatchEnoughForModeChange: Driver's flGraphicsCaps did");
WARNING(" not match for GCAPS_HIGHRESTEXT and GCAPS_FORCEDITHER\n");
b = FALSE;
}
if ((poNew.ulLogPixelsX() != poOld.ulLogPixelsX()) ||
(poNew.ulLogPixelsY() != poOld.ulLogPixelsY()))
{
WARNING("bDynamicMatchEnoughForModeChange: Driver's ulLogPixels did not match\n");
b = FALSE;
}
// We can't handle font producers because I haven't bothered with
// code to traverse the font code's producer lists and Do The Right
// Thing (appropriate locks are the biggest pain). Fortunately,
// font producing video drivers should be extremely rare.
if (PPFNDRV(poNew, QueryFont) ||
PPFNDRV(poNew, QueryFontCaps) ||
PPFNDRV(poNew, LoadFontFile) ||
PPFNDRV(poNew, QueryFontFile) ||
PPFNDRV(poNew, GetGlyphMode))
{
WARNING("bDynamicMatchEnoughForModeChange: New driver can't be a font provider\n");
b = FALSE;
}
if (PPFNDRV(poOld, QueryFont) ||
PPFNDRV(poOld, QueryFontCaps) ||
PPFNDRV(poOld, LoadFontFile) ||
PPFNDRV(poOld, QueryFontFile) ||
PPFNDRV(poOld, GetGlyphMode))
{
WARNING("bDynamicMatchEnoughForModeChange: Old driver can't be a font provider\n");
b = FALSE;
}
ASSERTGDI((poNew.ulTechnology() == DT_RASDISPLAY) &&
(poOld.ulTechnology() == DT_RASDISPLAY),
"Display drivers must specify DT_RASDISPLAY for ulTechnology");
ASSERTGDI((poNew.flTextCaps() & ~TC_SCROLLBLT) ==
(poOld.flTextCaps() & ~TC_SCROLLBLT),
"Display drivers should set only TC_RA_ABLE in flTextCaps");
return(b);
}
/******************************Public*Routine******************************\
* vAssertPaletteRefCountCorrect
*
* Validates the reference count for a palette by counting all the surfaces
* that use it.
*
* History:
* 14-Oct-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
#if DBG
VOID
vAssertPaletteRefCountCorrect(
PALETTE* ppalOld
)
{
LONG cAltLocks;
LONG cSurfaces;
HOBJ hobj;
SURFACE* pSurface;
PALETTE* ppalSurface;
MLOCKFAST mo;
cSurfaces = 0;
hobj = 0;
while (pSurface = (SURFACE*) HmgSafeNextObjt(hobj, SURF_TYPE))
{
hobj = (HOBJ) pSurface->hGet();
ppalSurface = pSurface->ppal();
if (ppalSurface == ppalOld)
{
cSurfaces++;
}
else if ((ppalSurface != NULL) && (ppalSurface->ppalColor == ppalOld))
{
cSurfaces++;
}
}
cAltLocks = ((POBJ) ppalOld)->ulShareCount;
// The PDEV keeps a reference count on the palette, as does the
// EngCreatePalette call. So the number of Alt-locks should be
// two more than the number of surfaces with this palette:
if (cAltLocks < cSurfaces + 2)
{
KdPrint(("vAssertPaletteRefCountCorrect cAltLocks: %li != cSurfaces: %li.\n",
cAltLocks - 2, cSurfaces));
hobj = 0;
while (pSurface = (SURFACE*) HmgSafeNextObjt(hobj, SURF_TYPE))
{
hobj = (HOBJ) pSurface->hGet();
ppalSurface = pSurface->ppal();
if (ppalSurface == ppalOld)
{
KdPrint((" %p\n", pSurface));
}
else if ((ppalSurface != NULL) && (ppalSurface->ppalColor == ppalOld))
{
KdPrint((" -%p\n", pSurface));
}
}
RIP("Breaking to debugger...");
}
}
#else
#define vAssertPaletteRefCountCorrect(ppalOld)
#endif
/******************************Public*Routine******************************\
* bDynamicRemoveAllDriverRealizations
*
* Cleanses a PDEV of all state that is specific to the device, such as
* device format bitmaps, and brush and font realizations.
*
* NOTE: The following locks must be held:
*
* 1. Devlock;
* 2. RFont list lock;
* 3. Handle manager lock.
*
* History:
* 8-Feb-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL
bDynamicRemoveAllDriverRealizations(
HDEV hdev
)
{
BOOL bConversionSuccessful;
HOBJ hobj; // Temporary object handle
SURFACE* pSurface; // Temporary surface pointer
RFONT* prfnt; // Temporary RFONT pointer
FONTOBJ* pfo; // Temporary FONTOBJ pointer
BRUSH* pbrush; // Temporary BRUSH pointer
PFN_DrvDestroyFont pfnDrvDestroyFont;
PDEV* ppdev;
PDEVOBJ po(hdev);
ppdev = po.ppdev;
// Now traverse all the device bitmaps associated with this device,
// and convert any driver-owned bitmaps to DIBs.
bConversionSuccessful = TRUE;
hobj = 0;
while (pSurface = (SURFACE*) HmgSafeNextObjt(hobj, SURF_TYPE))
{
// Retrieve the handle to the next surface before we delete
// the current one:
hobj = (HOBJ) pSurface->hGet();
// Note that the sprite code handles mode changes for sprite
// surfaces.
if ((pSurface->hdev() == hdev) &&
(pSurface->bDeviceDependentBitmap()) &&
(pSurface->dhpdev() != NULL))
{
// The surface cannot be converted if there is any
// outstanding lock other than those done to select
// a bitmap into a DC. We can't very well go and de-
// allocate 'pSurface' while someone is looking at it.
//
// However, we really shouldn't fail a dynamic mode
// change if some thread somewhere in the system should
// happen to be doing a bitmap operation with a DFB.
// For that reason, wherever a surface is locked,
// we endeavour to hold either the dynamic mode change
// lock or the devlock -- and since at this very moment
// we have both, that should mean that these conversions
// will never fail due to lock issues.
if (pSurface->hdc() != 0)
{
// WinBug #242572 3-13-2001 jasonha Surface not selected by DC it thinks it is
//
// When a DC is saved while a DFB is selected, but then another
// surface or no surface is selected, bConvertDfbDcToDib will
// try to convert the top-level DC's surface to a DIB.
//
// Walk the saved DC chain until the first DC actually referencing
// this surface is found.
//
HDC hdc = pSurface->hdc();
while (1)
{
MDCOBJA dco(hdc); // Alt-lock
ASSERTGDI(dco.bValid(), "Surface DC is invalid");
if (dco.pSurface() == pSurface)
{
if (!bConvertDfbDcToDib(&dco))
{
WARNING("Failed DC surface conversion (possibly from low memory)\n");
bConversionSuccessful = FALSE;
}
break;
}
ASSERTGDI(dco.lSaveDepth() > 1, "DC selected surface not found in DC stack.");
hdc = dco.hdcSave();
}
}
else
{
// Handle Compatible Stock Surfaces
if (pSurface->bStockSurface())
{
if (!pConvertDfbSurfaceToDib(hdev, pSurface, pSurface->cRef()))
{
WARNING("Failed stock surface conversion in bConvertStockDfbToDib()\n");
bConversionSuccessful = FALSE;
}
}
else
{
// No-one should have a lock on the bitmap:
if (!pConvertDfbSurfaceToDib(hdev, pSurface, 0))
{
WARNING("Failed surface conversion (possibly from low memory)\n");
bConversionSuccessful = FALSE;
}
}
}
}
}
// We are safe from new DFBs being created right now because we're
// holding the devlock.
if (bConversionSuccessful)
{
// Now get rid of any font caches that the old instance
// of the driver may have.
//
// We're protected against having bDeleteRFONT call the
// driver at the same time because it has to grab the
// Devlock, and we're already holding it.
pfnDrvDestroyFont = PPFNDRV(po, DestroyFont);
if (pfnDrvDestroyFont != NULL)
{
// We must hold the RFONT list semaphore while we traverse the
// RFONT list!
for (prfnt = ppdev->prfntInactive;
prfnt != NULL;
prfnt = prfnt->rflPDEV.prfntNext)
{
pfo = &prfnt->fobj;
pfnDrvDestroyFont(pfo);
pfo->pvConsumer = NULL;
}
for (prfnt = ppdev->prfntActive;
prfnt != NULL;
prfnt = prfnt->rflPDEV.prfntNext)
{
pfo = &prfnt->fobj;
pfnDrvDestroyFont(pfo);
pfo->pvConsumer = NULL;
}
}
// Make it so that any brush realizations are invalidated, because
// we don't want a new instance of the driver trying to use old
// instance 'pvRbrush' data.
//
// This also takes care of invalidating the brushes for all the
// DDB to DIB conversions.
//
// Note that we're actually invalidating the caches of all brushes
// in the system, because we don't store any 'hdevOld' information
// with the brush. Because dynamic mode changes should be relatively
// infrequent, and because realizations are reasonably cheap, I don't
// expect this to be a big hit.
hobj = 0;
while (pbrush = (BRUSH*) HmgSafeNextObjt(hobj, BRUSH_TYPE))
{
hobj = (HOBJ) pbrush->hGet();
// Mark as dirty by setting the cache ID to
// an invalid state.
pbrush->ulSurfTime((ULONG) -1);
// Set the uniqueness so the are-you-really-
// dirty check in vInitBrush will not think
// an old realization is still valid.
pbrush->ulBrushUnique(pbrush->ulGlobalBrushUnique());
}
// We must disable the halftone device information when changing
// colour depths because the GDIINFO data it was constructed with
// are no longer valid. It is always enabled lazily, so there's no
// need to reenable it here:
if (ppdev->pDevHTInfo != NULL)
{
po.bDisableHalftone();
}
}
return(bConversionSuccessful);
}
/******************************Public*Routine******************************\
* bDynamicIntersectVisRect
*
* It is critical that we must be able to update all VisRgns if the new
* mode is smaller than the old. If this did not happen, we could allow
* GDI calls to the driver that are outside the bounds of the visible
* display, and as a result the driver would quite likely fall over.
*
* We don't entrust USER to always take care of this case because it
* updates the VisRgns after it knows that bDynamicModeChange was
* successful -- which is too late if the VisRgn change should fail
* because of low memory. It's acceptable in low memory situations to
* temporarily draw incorrectly as a result of a wrong VisRgn, but it
* is *not* acceptable to crash.
*
* Doing this here also means that USER doesn't have to call
* us while holding the Devlock.
*
* History:
* 8-Feb-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
BOOL
bDynamicIntersectVisRect(
SURFACE* pSurfaceOld,
SIZEL sizlNew
)
{
HOBJ hobj;
DC* pdc;
hobj = 0;
while (pdc = (DC*) HmgSafeNextObjt(hobj, DC_TYPE))
{
hobj = (HOBJ) pdc->hGet();
if ((!(pdc->fs() & DC_IN_CLONEPDEV)) &&
(pdc->pSurface() == pSurfaceOld) &&
(pdc->prgnVis() != NULL))
{
if (!GreIntersectVisRect((HDC) hobj, 0, 0,
sizlNew.cx, sizlNew.cy))
{
WARNING("bDynamicModeChange: Failed reseting VisRect!\n");
// Note that if we fail here, we may have already
// shrunk some VisRgn's. However, we should have only
// failed in a very low-memory situation, in which case
// there will be plenty of other drawing problems. The
// regions will likely all be reset back to the correct
// dimensions by the owning applications, eventually.
return(FALSE);
}
}
}
return(TRUE);
}
/******************************Public*Routine******************************\
* vDynamicSwitchPalettes
*
* Device Independent Bitmaps (DIBs) are great when switching colour
* depths because, by virtue of their attached colour table (also known
* as a 'palette'), they Just Work at any colour depth.
*
* Device Dependent Bitmaps (DDBs) are more problematic. They implicitly
* share their palette with the display -- meaning that if the display's
* palette is dynamically changed, the old DDBs will not work. We get
* around this by dynamically creating palettes to convert them to DIBs.
* Unfortunately, at palettized 8bpp we sometimes have to guess at what
* the appropriate palette would be. For this reason, whenever we switch
* back to 8bpp we make sure we convert them back to DDBs by removing their
* palettes.
*
* History:
* 8-Feb-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID
vDynamicSwitchPalettes(
SURFACE* pSurface,
PDEV* ppdevOld,
PDEV* ppdevNew
)
{
PALETTE* ppalOld;
HOBJ hobj;
BOOL bHintCreated;
ASSERTGDI(pSurface->iType() == STYPE_BITMAP,
"Unexpected bitmap type");
ASSERTGDI(pSurface->iFormat() == ppdevOld->devinfo.iDitherFormat,
"Unexpected bitmap format");
// Device Format Bitmaps (DFBs) are DDBs that are created via
// CreateCompatibleBitmap, and so we know what device they're
// associated with.
//
// Unfortunately, non-DFB DDBs (created via CreateBitmap or
// CreateDiscardableBitmap) have no device assocation -- so we
// don't know whether or not they're really associated with the
// display.
//
// If a non-DFB DDB is currently selected into a DC owned by the
// display, we will add a palette to it. If a non-DFB DDB is
// not currently selected into a DC owned by the display (implied
// by having its surface 'hdev' as zero), we will not convert it.
// SelectBitmap will refuse to load it into a DC. (In 4.0 we used
// to always assume that any such bitmap was intended for the
// display, but that's not always a valid assumption, and doesn't
// make much sense in a multiple-monitor or multi-user environment
// anyway.) Fortunately, non-DFB DDBs are increasingly rare.
//
// Note that DFBs are synchronized by the Devlock, and we don't
// need a lock to change the palette for a non-DFB DDB (see
// PALETTE_SELECT_SET logic).
ppalOld = ppdevOld->ppalSurf;
if (pSurface->ppal() == NULL)
{
// Mark the surface to note that we added a palette:
pSurface->vSetDynamicModePalette();
if (ppdevOld->GdiInfo.flRaster & RC_PALETTE)
{
bHintCreated = FALSE;
if (pSurface->hpalHint() != 0)
{
EPALOBJ palDC(pSurface->hpalHint());
if ((palDC.bValid()) &&
(palDC.bIsPalDC()) &&
(!palDC.bIsPalDefault()) &&
(palDC.ptransFore() != NULL))
{
PALMEMOBJ palPerm;
XEPALOBJ palSurf(ppalOld);
if (palPerm.bCreatePalette(PAL_INDEXED,
256,
(ULONG*) palSurf.apalColorGet(),
0,
0,
0,
PAL_FREE))
{
ULONG nPhysChanged = 0;
ULONG nTransChanged = 0;
palPerm.ulNumReserved(palSurf.ulNumReserved());
bHintCreated = TRUE;
vMatchAPal(NULL,
palPerm,
palDC,
&nPhysChanged,
&nTransChanged);
palPerm.vKeepIt();
pSurface->ppal(palPerm.ppalGet());
// Keep a reference active:
palPerm.ppalSet(NULL);
}
}
}
if (!bHintCreated)
{
INC_SHARE_REF_CNT(ppalDefaultSurface8bpp);
pSurface->ppal(ppalDefaultSurface8bpp);
}
}
else
{
INC_SHARE_REF_CNT(ppalOld);
pSurface->ppal(ppalOld);
}
}
else if ((pSurface->ppal() == ppalOld) &&
(pSurface->flags() & PALETTE_SELECT_SET))
{
ASSERTGDI((pSurface->hdc() != 0) &&
(pSurface->cRef() != 0),
"Expected bitmap to be selected into a DC");
INC_SHARE_REF_CNT(pSurface->ppal());
pSurface->flags(pSurface->flags() & ~PALETTE_SELECT_SET);
}
// When switching back to palettized 8bpp, remove any palettes we
// had to add to 8bpp DDBs:
if (ppdevNew->GdiInfo.flRaster & RC_PALETTE)
{
if (pSurface->bDynamicModePalette())
{
ASSERTGDI(pSurface->ppal() != NULL,
"Should be a palette here");
XEPALOBJ pal(pSurface->ppal());
pal.vUnrefPalette();
pSurface->vClearDynamicModePalette();
pSurface->ppal(NULL);
}
}
}
/******************************Public*Routine******************************\
* bDynamicModeChange
*
* USER callable function that switches driver instances between two PDEVs.
*
* GDI's 'HDEV' and 'PDEV' stay the same; only the device's 'pSurface',
* 'dhpdev', GDIINFO, DEVINFO, and palette information change.
*
* The caller is ChangeDisplaySettings in USER, which is reponsible for:
*
* o Calling us with a valid devmode;
* o Ensuring that the device is not currently in full-screen mode;
* o Invalidating all of its SaveScreenBits buffers;
* o Changing the VisRgn's on all DCs;
* o Resetting the pointer shape;
* o Sending the appropriate messages to everyone;
* o Redrawing the desktop.
*
* Since CreateDC("DISPLAY") always gets mapped to GetDC(NULL), there are
* no DC's for which GDI is responsible for updating the VisRgn.
*
* Dynamically changes a display driver or mode.
*
* Rules of This Routine
* ---------------------
*
* o An important precept is that no drawing by any threads to any
* application's bitmaps should be affected by this routine. This means,
* for example, that we cannot exclusive lock any DCs.
*
* o While we keep GDI's 'HDEV' and 'PDEV' in place, we do have to modify
* fields like 'dhpdev' and 'pSurface'. Because we also have to update
* copies of these fields that are stashed in DC's, it means that *all*
* accesses to mode-dependent fields such as 'dhpdev,' 'pSurface,' and
* 'sizl' must be protected by holding a resource that this routine
* acquires -- such as the devlock or handle-manager lock.
*
* o If the function fails for whatever reason, the mode MUST be restored
* back to its original state.
*
* Returns: TRUE if successful, FALSE if the two PDEVs didn't match enough,
* or if we're out of memory.
*
* History:
* 8-Feb-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
*
* 27-Aug-1996 -by- J. Andrew Goossen [andrewgo]
* Added support for dynamic display driver changes.
*
\**************************************************************************/
ULONG gcModeChanges; // # of mode changes, for debugging even on free builds
#define SWAP(x, y, tmp) { (tmp) = (x); (x) = (y); (y) = (tmp); }
typedef union {
GDIINFO GdiInfo;
DEVINFO devinfo;
PFN apfn[INDEX_LAST];
LDEV* pldev;
HANDLE hSpooler;
PVOID pDesktopId;
PGRAPHICS_DEVICE pGraphicsDevice;
POINTL ptlOrigin;
PDEVMODEW ppdevDevmode;
PFN_DrvSetPointerShape pfnSet;
PFN_DrvMovePointer pfnMove;
PFN_DrvSynchronize pfnSync;
PFN_DrvSynchronizeSurface pfnSyncSurface;
PFN_DrvSetPalette pfnSetPalette;
PFN_DrvBitBlt pfnUnfilteredBitBlt;
PFN_DrvNotify pfnNotify;
BYTE dc[sizeof(DC)];
SIZEL sizlMeta;
HSURF ahsurf[HS_DDI_MAX];
HLFONT hlfnt;
DWORD dwDriverAccelerationLevel;
DWORD dwDriverCapableOverride;
#ifdef DDI_WATCHDOG
PWATCHDOG_DATA pWatchdogData;
#endif
} SWAPBUFFER;
BOOL
bDynamicModeChange(
HDEV hdevOld,
HDEV hdevNew
)
{
BOOL bRet;
BOOL bFormatChange;
PDEV* ppdevOld;
PDEV* ppdevNew;
DHPDEV dhpdevOld;
DHPDEV dhpdevNew;
SURFACE* pSurfaceOld;
SURFACE* pSurfaceNew;
PALETTE* ppalOld;
PALETTE* ppalNew;
ULONG cBppOld;
ULONG cBppNew;
SIZEL sizlOld;
SIZEL sizlNew;
SURFACE* pSurface; // Temporary surface pointer
DC* pdc; // Temporary DC pointer
DRVOBJ* pdo; // Temporary DRVOBJ pointer
HOBJ hobj; // Temporary object handle
BOOL bPermitModeChange;
BRUSH* pbrGrayPattern;
DC* pdcBuffer; // Points to temporary DC buffer
SWAPBUFFER* pswap;
BOOL bDisabledOld;
BOOL bDisabledNew;
PFN_DrvResetPDEV pfnDrvResetPDEV;
bRet = FALSE; // Assume failure
// We impose some restrictions upon what capabilities may change
// between drivers:
if (bDynamicMatchEnoughForModeChange(hdevOld, hdevNew))
{
ASSERTGDI(GreIsSemaphoreOwnedByCurrentThread(ghsemShareDevLock),
"ShareDevlock must held be before calling bDynamicModeChange");
// Allocate a temporary buffer for use swapping data:
pswap = (SWAPBUFFER*) PALLOCNOZ(sizeof(SWAPBUFFER), 'pmtG');
if (pswap)
{
PDEVOBJ poOld(hdevOld);
PDEVOBJ poNew(hdevNew);
bDisabledNew = poNew.bDisabled();
bDisabledOld = poOld.bDisabled();
// Disable timer-based synchronization for these PDEVs for now.
// This is mainly so that the PDEV_SYNCHRONIZE_ENABLED is set
// correctly.
vDisableSynchronize(hdevNew);
vDisableSynchronize(hdevOld);
cBppOld = poOld.GdiInfo()->cBitsPixel * poOld.GdiInfo()->cPlanes;
cBppNew = poNew.GdiInfo()->cBitsPixel * poNew.GdiInfo()->cPlanes;
// Ideally, we would check the palettes here, too:
bFormatChange = (cBppOld != cBppNew);
ppdevOld = (PDEV*) hdevOld;
ppdevNew = (PDEV*) hdevNew;
// PDEV shouldn't be clone
ASSERTGDI(!poOld.bCloneDriver(),"pdevOld is clone!");
ASSERTGDI(!poNew.bCloneDriver(),"pdevNew is clone!");
// The following lock rules must be abided, otherwise deadlocks may
// arise:
//
// o Pointer lock must be acquired after Devlock (GreSetPointer);
//
// And see drvsup.cxx, too.
//
// So we acquire locks in the following order (note that the
// vAssertDynaLock() routines should be modified if this list ever
// changes):
ASSERTGDI(GreIsSemaphoreOwnedByCurrentThread(poOld.hsemDevLock()),
"Devlock must be held before acquiring the pointer semaphore");
SEMOBJ soPointer(poOld.hsemPointer()); // No asynchronous pointer moves
ASSERTGDI(ppdevOld->pSurface != NULL, "Must be called on a completed PDEV");
ASSERTGDI(poOld.bDisplayPDEV(), "Must be called on a display PDEV");
ASSERTGDI((prgnDefault->cScans == 1) && (prgnDefault->rcl.right == 0),
"Someone changed prgnDefault; could cause driver access violations");
// Free all PDEV state that is dependent on or cached by the driver:
if (bDynamicRemoveAllDriverRealizations(hdevOld) &&
bDynamicRemoveAllDriverRealizations(hdevNew))
{
bPermitModeChange = TRUE;
sizlOld = poOld.sizl();
pSurfaceOld = ppdevOld->pSurface;
ppalOld = ppdevOld->ppalSurf;
dhpdevOld = ppdevOld->dhpdev;
sizlNew = poNew.sizl();
pSurfaceNew = ppdevNew->pSurface;
ppalNew = ppdevNew->ppalSurf;
dhpdevNew = ppdevNew->dhpdev;
// Make sure the VisRgns are immediately shrunk if necessary:
if ((sizlNew.cx < sizlOld.cx) || (sizlNew.cy < sizlOld.cy))
{
bPermitModeChange &= bDynamicIntersectVisRect(pSurfaceOld, sizlNew);
}
if ((sizlOld.cx < sizlNew.cx) || (sizlOld.cy < sizlNew.cy))
{
bPermitModeChange &= bDynamicIntersectVisRect(pSurfaceNew, sizlOld);
}
// Finally, if we're not switching drivers then let the driver know
// about the mode switch. This has to be the last step because we
// are implicitly telling the driver that it can transfer data from
// the old instance to the new instance with the assurance that the
// new instance won't later be abandoned.
pfnDrvResetPDEV = PPFNDRV(poNew, ResetPDEV);
if ((pfnDrvResetPDEV != NULL) &&
(pfnDrvResetPDEV == PPFNDRV(poOld, ResetPDEV)) &&
(poNew.pldev() == poOld.pldev()))
{
// The driver can refuse the mode switch if it wants:
if (bPermitModeChange)
{
GreEnterMonitoredSection(poOld.ppdev, WD_DEVLOCK);
bPermitModeChange = pfnDrvResetPDEV(dhpdevOld, dhpdevNew);
GreExitMonitoredSection(poOld.ppdev, WD_DEVLOCK);
}
}
if (bPermitModeChange)
{
/////////////////////////////////////////////////////////////
// At this point, we're committed to the mode change.
// Nothing below this point can be allowed to fail.
/////////////////////////////////////////////////////////////
// Traverse all DC's and update their surface information if
// they're associated with this device.
//
// Note that bDeleteDCInternal wipes some fields in the DC via
// bCleanDC before freeing the DC, but this is okay since the
// worst we'll do is update some fields just before the DC gets
// deleted.
hobj = 0;
while (pdc = (DC*) HmgSafeNextObjt(hobj, DC_TYPE))
{
hobj = (HOBJ) pdc->hGet();
if (!(pdc->fs() & DC_IN_CLONEPDEV))
{
// Note that we don't check that pdc->hdevOld() == hdevOld
// because the SaveDC stuff doesn't bother copying the hdevOld,
// but DOES copy the dclevel.
//
// Note that 'flbrushAdd()' is not an atomic operation.
// However, since we're holding the devlock and the palette
// lock, there shouldn't be any other threads alt-locking our
// DC and modifying these fields at the same time:
if (pdc->pSurface() == pSurfaceOld)
{
pdc->pSurface(pSurfaceNew);
pdc->sizl(sizlNew);
pdc->flbrushAdd(DIRTY_BRUSHES);
}
else if (pdc->pSurface() == pSurfaceNew)
{
pdc->pSurface(pSurfaceOld);
pdc->sizl(sizlOld);
pdc->flbrushAdd(DIRTY_BRUSHES);
}
if (pdc->dhpdev() == dhpdevOld)
{
pdc->dhpdev(dhpdevNew);
pdc->flGraphicsCaps(poNew.flGraphicsCaps());
pdc->flGraphicsCaps2(poNew.flGraphicsCaps2());
}
else if (pdc->dhpdev() == dhpdevNew)
{
pdc->dhpdev(dhpdevOld);
pdc->flGraphicsCaps(poOld.flGraphicsCaps());
pdc->flGraphicsCaps2(poOld.flGraphicsCaps2());
}
}
}
// Compatible bitmap palettes may have to be changed if the
// mode changes. Note that the sprite code handles any
// sprite surfaces:
hobj = 0;
while (pSurface = (SURFACE*) HmgSafeNextObjt(hobj, SURF_TYPE))
{
hobj = (HOBJ) pSurface->hGet();
if (pSurface->hdev() == hdevOld)
{
if (pSurface->bApiBitmap())
{
if ((bFormatChange) &&
(pSurface->iFormat() == pSurfaceOld->iFormat()))
{
vDynamicSwitchPalettes(pSurface, ppdevOld, ppdevNew);
}
}
// Surfaces private to the driver should be transferred
// along with the driver instance.
else if (pSurface->bDriverCreated() && !pSurface->bDirectDraw())
{
pSurface->hdev(hdevNew);
}
}
else if (pSurface->hdev() == hdevNew)
{
if (pSurface->bApiBitmap())
{
if ((bFormatChange) &&
(pSurface->iFormat() == pSurfaceNew->iFormat()))
{
vDynamicSwitchPalettes(pSurface, ppdevNew, ppdevOld);
}
}
// Surfaces private to the driver should be transferred
// along with the driver instance.
else if (pSurface->bDriverCreated() && !pSurface->bDirectDraw())
{
pSurface->hdev(hdevOld);
}
}
}
// DRIVEROBJs are transferred with the driver to the new PDEV:
hobj = 0;
while (pdo = (DRVOBJ*) HmgSafeNextObjt(hobj, DRVOBJ_TYPE))
{
hobj = (HOBJ) pdo->hGet();
if (pdo->hdev == hdevOld)
{
pdo->hdev = hdevNew;
poNew.vReferencePdev();
poOld.vUnreferencePdev();
}
else if (pdo->hdev == hdevNew)
{
pdo->hdev = hdevOld;
poOld.vReferencePdev();
poNew.vUnreferencePdev();
}
}
// Same with WNDOBJs:
vChangeWndObjs(pSurfaceOld, hdevOld, pSurfaceNew, hdevNew);
// Re-realize the gray pattern brush which is used for drag
// rectangles:
pbrGrayPattern = (BRUSH*) HmgShareLock((HOBJ)ghbrGrayPattern,
BRUSH_TYPE);
pdcBuffer = (DC*) &pswap->dc;
pdcBuffer->pDCAttr = &pdcBuffer->dcattr;
pdcBuffer->crTextClr(0x00000000);
pdcBuffer->crBackClr(0x00FFFFFF);
pdcBuffer->lIcmMode(DC_ICM_OFF);
pdcBuffer->hcmXform(NULL);
poOld.pbo()->vInitBrush(pdcBuffer,
pbrGrayPattern,
(XEPALOBJ) ppalDefault,
(XEPALOBJ) ppalNew,
pSurfaceNew);
poNew.pbo()->vInitBrush(pdcBuffer,
pbrGrayPattern,
(XEPALOBJ) ppalDefault,
(XEPALOBJ) ppalOld,
pSurfaceOld);
DEC_SHARE_REF_CNT(pbrGrayPattern);
/////////////////////////////////////////////////////////////
// Update all our PDEV fields:
/////////////////////////////////////////////////////////////
// Swap surface data between the two PDEVs:
ppdevNew->pSurface = pSurfaceOld;
ppdevNew->ppalSurf = ppalOld;
ppdevNew->dhpdev = dhpdevOld;
ppdevOld->pSurface = pSurfaceNew;
ppdevOld->ppalSurf = ppalNew;
ppdevOld->dhpdev = dhpdevNew;
if (!pSurfaceOld->bReadable())
{
pSurfaceNew->flags(pSurfaceNew->flags() | UNREADABLE_SURFACE);
SPRITESTATE *pStateOld = poOld.pSpriteState();
if (pStateOld)
{
if (pStateOld->flOriginalSurfFlags & UNREADABLE_SURFACE ||
pStateOld->flSpriteSurfFlags & UNREADABLE_SURFACE)
{
SPRITESTATE *pStateNew = poNew.pSpriteState();
if (pStateNew)
{
pStateNew->flOriginalSurfFlags |= UNREADABLE_SURFACE;
pStateNew->flSpriteSurfFlags |= UNREADABLE_SURFACE;
}
}
}
}
else if (!pSurfaceNew->bReadable())
{
pSurfaceOld->flags(pSurfaceOld->flags() | UNREADABLE_SURFACE);
SPRITESTATE *pStateNew = poNew.pSpriteState();
if (pStateNew)
{
if (pStateNew->flOriginalSurfFlags & UNREADABLE_SURFACE ||
pStateNew->flSpriteSurfFlags & UNREADABLE_SURFACE)
{
SPRITESTATE *pStateOld = poOld.pSpriteState();
if (pStateOld)
{
pStateOld->flOriginalSurfFlags |= UNREADABLE_SURFACE;
pStateOld->flSpriteSurfFlags |= UNREADABLE_SURFACE;
}
}
}
}
// WINBUG #365395 4-10-2001 jasonha Need to investigate old comments in bDynamicModeChange
//
// Old Comments:
// - vUnrefPalette of old palette for mirrored drivers?
// - Secondary palette fix?
SWAP(ppdevNew->pldev ,ppdevOld->pldev ,pswap->pldev );
SWAP(ppdevNew->devinfo ,ppdevOld->devinfo ,pswap->devinfo );
SWAP(ppdevNew->GdiInfo ,ppdevOld->GdiInfo ,pswap->GdiInfo );
SWAP(ppdevNew->hSpooler ,ppdevOld->hSpooler ,pswap->hSpooler );
SWAP(ppdevNew->pDesktopId ,ppdevOld->pDesktopId ,pswap->pDesktopId );
SWAP(ppdevNew->pGraphicsDevice,ppdevOld->pGraphicsDevice,pswap->pGraphicsDevice);
SWAP(ppdevNew->ptlOrigin ,ppdevOld->ptlOrigin ,pswap->ptlOrigin );
SWAP(ppdevNew->ppdevDevmode ,ppdevOld->ppdevDevmode ,pswap->ppdevDevmode );
SWAP(ppdevNew->pfnUnfilteredBitBlt,
ppdevOld->pfnUnfilteredBitBlt,
pswap->pfnUnfilteredBitBlt);
SWAP(ppdevNew->dwDriverCapableOverride,
ppdevOld->dwDriverCapableOverride,
pswap->dwDriverCapableOverride);
SWAP(ppdevNew->dwDriverAccelerationLevel,
ppdevOld->dwDriverAccelerationLevel,
pswap->dwDriverAccelerationLevel);
// Swap multimon data between the two PDEVs:
if (poOld.bMetaDriver() != poNew.bMetaDriver())
{
BOOL bMetaDriver = poOld.bMetaDriver();
poOld.bMetaDriver(poNew.bMetaDriver());
poNew.bMetaDriver(bMetaDriver);
}
SWAP(ppdevNew->sizlMeta, ppdevOld->sizlMeta, pswap->sizlMeta);
// Swap pattern brushes
RtlCopyMemory(pswap->ahsurf, ppdevNew->ahsurf, sizeof(HSURF)*HS_DDI_MAX);
RtlCopyMemory(ppdevNew->ahsurf, ppdevOld->ahsurf, sizeof(HSURF)*HS_DDI_MAX);
RtlCopyMemory(ppdevOld->ahsurf, pswap->ahsurf, sizeof(HSURF)*HS_DDI_MAX);
// Swap log fonts.
SWAP(ppdevNew->hlfntDefault, ppdevOld->hlfntDefault, pswap->hlfnt);
SWAP(ppdevNew->hlfntAnsiVariable, ppdevOld->hlfntAnsiVariable, pswap->hlfnt);
SWAP(ppdevNew->hlfntAnsiFixed, ppdevOld->hlfntAnsiFixed, pswap->hlfnt);
// Swap the dispatch tables and accelerators:
RtlCopyMemory(pswap->apfn, ppdevNew->apfn, sizeof(PFN)*INDEX_LAST);
RtlCopyMemory(ppdevNew->apfn, ppdevOld->apfn, sizeof(PFN)*INDEX_LAST);
RtlCopyMemory(ppdevOld->apfn, pswap->apfn, sizeof(PFN)*INDEX_LAST);
SWAP(ppdevNew->pfnDrvSetPointerShape, ppdevOld->pfnDrvSetPointerShape, pswap->pfnSet);
SWAP(ppdevNew->pfnDrvMovePointer, ppdevOld->pfnDrvMovePointer, pswap->pfnMove);
SWAP(ppdevNew->pfnSync, ppdevOld->pfnSync, pswap->pfnSync);
SWAP(ppdevNew->pfnSyncSurface, ppdevOld->pfnSyncSurface, pswap->pfnSyncSurface);
SWAP(ppdevNew->pfnSetPalette, ppdevOld->pfnSetPalette, pswap->pfnSetPalette);
SWAP(ppdevNew->pfnNotify, ppdevOld->pfnNotify, pswap->pfnNotify);
#ifdef DDI_WATCHDOG
SWAP(ppdevNew->pWatchdogData, ppdevOld->pWatchdogData, pswap->pWatchdogData);
#endif
// Inform the drivers of their new PDEVs:
(*PPFNDRV(poNew, CompletePDEV))(poNew.dhpdev(), poNew.hdev());
(*PPFNDRV(poOld, CompletePDEV))(poOld.dhpdev(), poOld.hdev());
// Transfer all the DirectDraw state:
DxDdDynamicModeChange(hdevOld, hdevNew, 0);
// Transfer the disabled state:
poOld.bDisabled(bDisabledNew);
poNew.bDisabled(bDisabledOld);
// Update the magic colours in the surface palette:
vResetSurfacePalette(hdevOld);
vResetSurfacePalette(hdevNew);
// Transfer all the sprites between the two PDEVs:
vSpDynamicModeChange(hdevOld, hdevNew);
// Update the gamma-ramp on the device only if a gamma-ramp
// existed in the old (but at this moment, old is "new") PDEV:
UpdateGammaRampOnDevice(hdevOld, FALSE);
// Update some handy debug information:
gcModeChanges++;
bRet = TRUE;
}
}
VFREEMEM(pswap);
vEnableSynchronize(hdevNew);
vEnableSynchronize(hdevOld);
}
}
return(bRet);
}