/******************************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); }