/****************************************************************************/ // nddapi.c // // RDP DD exported functions. // // Copyright (c) 1996-2000 Microsoft Corporation /****************************************************************************/ #include #define hdrstop #define TRC_FILE "nddapi" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DC_DEBUG /****************************************************************************/ /* Useful function for outputting lines to the debugger */ /****************************************************************************/ void DrvDebugPrint(char * str, ...) { va_list ap; va_start(ap, str); EngDebugPrint("RDPDD: ", str, ap); } void WDIcaBreakOnDebugger() { ULONG dummyBytesReturned; ULONG status; DC_BEGIN_FN("WDIcaBreakOnDebugger"); status = EngFileIoControl( ddWdHandle, IOCTL_WDTS_DD_ICABREAKONDEBUGGER, 0, 0, 0, 0, &dummyBytesReturned); if (STATUS_SUCCESS != status) { TRC_ERR((TB, "IOCTL_WDTS_DD_ICABREAKONDEBUGGER returned %lu", status )); } DC_END_FN(); } #endif /****************************************************************************/ /* DrvEnableDriver - see NT DDK documentation. */ /* */ /* This is the only directly exported entry point to the display driver. */ /* All other entry points are exported through the data returned from this */ /* function. */ /****************************************************************************/ BOOL DrvEnableDriver(ULONG iEngineVersion, ULONG cj, DRVENABLEDATA *pded) { DC_BEGIN_FN("DrvEnableDriver"); #ifdef DC_DEBUG // Initialize the trace level. ddTrcType = TT_API1 | TT_API2 | TT_API3 | TT_API4; DD_SET_STATE(DD_ENABLE_DRIVER); #endif #ifdef DDINT3 _asm int 3; #endif // Check that the engine version is correct - we refuse to load on // other versions because we will almost certainly not work. if (iEngineVersion < DDI_DRIVER_VERSION_SP3) return FALSE; // Fill in as much as we can. Start with the entry points. if (cj >= FIELDOFFSET(DRVENABLEDATA, pdrvfn) + FIELDSIZE(DRVENABLEDATA, pdrvfn)) { pded->pdrvfn = (DRVFN *)ddDriverFns; TRC_DBG((TB, "Passing back driver functions %p", pded->pdrvfn)); } // Size of our entry point array. if (cj >= FIELDOFFSET(DRVENABLEDATA, c) + FIELDSIZE(DRVENABLEDATA, c)) { pded->c = DD_NUM_DRIVER_INTERCEPTS; TRC_DBG((TB, "Passing back function count %lu", pded->c)); } // DDI version this driver was targeted for is passed back to engine. // Future graphics engines may break calls down to old driver format. if (cj >= FIELDOFFSET(DRVENABLEDATA, iDriverVersion) + FIELDSIZE(DRVENABLEDATA, iDriverVersion)) { pded->iDriverVersion = DDI_DRIVER_VERSION_SP3; TRC_DBG((TB, "Using driver type %lu", pded->iDriverVersion)); } TRC_NRM((TB, "Num driver intercepts: %d", DD_NUM_DRIVER_INTERCEPTS)); DC_END_FN(); return TRUE; } /****************************************************************************/ // DrvDisableDriver - see NT DDK documentation. /****************************************************************************/ VOID DrvDisableDriver(VOID) { DC_BEGIN_FN("DrvDisableDriver"); // Release any resources allocated in DrvEnableDriver. TRC_NRM((TB, "DrvDisableDriver")); DDTerm(); DC_END_FN(); } /****************************************************************************/ /* DrvEnablePDEV - see NT DDK documentation. */ /* */ /* Initializes a bunch of fields for GDI, based on the mode we've been */ /* asked to do. This is the first thing called after DrvEnableDriver, when */ /* GDI wants to get some information about us. */ /* */ /* (This function mostly returns back information; DrvEnableSurface is used */ /* for initializing the hardware and driver components.) */ /****************************************************************************/ DHPDEV DrvEnablePDEV( DEVMODEW *pdm, PWSTR pwszLogAddr, ULONG cPat, HSURF *phsurfPatterns, ULONG cjCaps, ULONG *pdevcaps, ULONG cjDevInfo, DEVINFO *pdi, HDEV hdev, PWSTR pwszDeviceName, HANDLE hDriver) { DHPDEV rc = NULL; PDD_PDEV pPDev = NULL; GDIINFO gdiInfoNew; INT32 cModes; PVIDEO_MODE_INFORMATION pVideoModeInformation = NULL; INT32 cbModeSize; DC_BEGIN_FN("DrvEnablePDEV"); // Make sure that we have large enough data to reference. if (cjCaps >= sizeof(GDIINFO) && cjDevInfo >= sizeof(DEVINFO)) { // Allocate a physical device structure; store the hDriver in it. pPDev = EngAllocMem(0, sizeof(DD_PDEV), DD_ALLOC_TAG); if (pPDev != NULL) { // Don't zero the palette since we'll be setting that up soon. memset(pPDev, 0, sizeof(DD_PDEV) - sizeof(pPDev->Palette)); pPDev->hDriver = hDriver; } else { TRC_ERR((TB, "DrvEnablePDEV - Failed EngAllocMem")); DC_QUIT; } } else { TRC_ERR((TB, "Buffer size too small %lu %lu", cjCaps, cjDevInfo)); DC_QUIT; } // Set up the current screen mode information based upon the supplied // mode settings. DDInitializeModeFields(pPDev, (GDIINFO *)pdevcaps, &gdiInfoNew, pdi, pdm); memcpy(pdevcaps, &gdiInfoNew, min(sizeof(GDIINFO), cjCaps)); // Since DrvGetModes is only called when the DD is test-loaded, we must // get a mode count here so that we can determine if we're loaded into // the console session. cModes = DDGetModes(hDriver, &pVideoModeInformation, &cbModeSize); if (cModes == -1) { TRC_NRM((TB, "We are a chained console driver")); ddConsole = TRUE; // see DDK : must be set for a mirror driver pdi->flGraphicsCaps |= GCAPS_LAYERED; // to support alpha cursor pdi->flGraphicsCaps2 |= GCAPS2_ALPHACURSOR; } else { if (cModes == 0) { TRC_ERR((TB, "Failed to get the video modes.")); DC_QUIT; } } #if 0 // Dump the returned GDIINFO details to the debugger. TRC_ALT((TB, "Returned GDIINFO:")); TRC_ALT((TB, " ulVersion %#x", gdiInfoNew.ulVersion)); TRC_ALT((TB, " ulTechnology %#x", gdiInfoNew.ulTechnology)); TRC_ALT((TB, " ulHorzSize %#x", gdiInfoNew.ulHorzSize)); TRC_ALT((TB, " ulVertSize %#x", gdiInfoNew.ulVertSize)); TRC_ALT((TB, " ulHorzRes %#x", gdiInfoNew.ulHorzRes)); TRC_ALT((TB, " ulVertRes %#x", gdiInfoNew.ulVertRes)); TRC_ALT((TB, " cBitsPixel %#x", gdiInfoNew.cBitsPixel)); TRC_ALT((TB, " cPlanes %#x", gdiInfoNew.cPlanes)); TRC_ALT((TB, " ulNumColors %#x", gdiInfoNew.ulNumColors)); TRC_ALT((TB, " flRaster %#x", gdiInfoNew.flRaster)); TRC_ALT((TB, " ulLogPixelsX %#x", gdiInfoNew.ulLogPixelsX)); TRC_ALT((TB, " ulLogPixelsY %#x", gdiInfoNew.ulLogPixelsY)); TRC_ALT((TB, " flTextCaps %#x", gdiInfoNew.flTextCaps)); TRC_ALT((TB, " ulDACRed %#x", gdiInfoNew.ulDACRed)); TRC_ALT((TB, " ulDACGreen %#x", gdiInfoNew.ulDACGreen)); TRC_ALT((TB, " ulDACBlue %#x", gdiInfoNew.ulDACBlue)); TRC_ALT((TB, " ulAspectX %#x", gdiInfoNew.ulAspectX)); TRC_ALT((TB, " ulAspectY %#x", gdiInfoNew.ulAspectY)); TRC_ALT((TB, " ulAspectXY %#x", gdiInfoNew.ulAspectXY)); TRC_ALT((TB, " xStyleStep %#x", gdiInfoNew.xStyleStep)); TRC_ALT((TB, " yStyleStep %#x", gdiInfoNew.yStyleStep)); TRC_ALT((TB, " denStyleStep %#x", gdiInfoNew.denStyleStep)); TRC_ALT((TB, " ptlPhysOffset.x %#x", gdiInfoNew.ptlPhysOffset.x)); TRC_ALT((TB, " ptlPhysOffset.y %#x", gdiInfoNew.ptlPhysOffset.y)); TRC_ALT((TB, " szlPhysSize.cx %#x", gdiInfoNew.szlPhysSize.cx)); TRC_ALT((TB, " szlPhysSize.cy %#x", gdiInfoNew.szlPhysSize.cy)); TRC_ALT((TB, " ulNumPalReg %#x", gdiInfoNew.ulNumPalReg)); TRC_ALT((TB, " ulVRefresh %#x", gdiInfoNew.ulVRefresh)); TRC_ALT((TB, " ulBltAlignment %#x", gdiInfoNew.ulBltAlignment)); TRC_ALT((TB, " ulPanningHorzRes %#x", gdiInfoNew.ulPanningHorzRes)); TRC_ALT((TB, " ulPanningVertRes %#x", gdiInfoNew.ulPanningVertRes)); #endif // Set the default palette. if (DDInitializePalette(pPDev, pdi)) { // We have successfully initialized - return the new PDEV. rc = (DHPDEV)pPDev; TRC_NRM((TB, "PDEV 0x%p screen format %lu", pPDev, pPDev->iBitmapFormat)); } else { TRC_ERR((TB, "Failed to initialize palette")); DC_QUIT; } DC_EXIT_POINT: // This is a temporary buffer. We use it to call DDGetModes in order // to find out if we are in chained mode or not. We always free it. if (pVideoModeInformation != NULL) { EngFreeMem(pVideoModeInformation); pVideoModeInformation = NULL; } // Release any resources if we failed to initialize. if (rc != NULL) { DD_UPD_STATE(DD_ENABLE_PDEV); } else { // In case pPDev is allocated this will free first try to free the // palette (if any) and then it will free pPDev. DrvDisablePDEV((DHPDEV)pPDev); DD_UPD_STATE(DD_ENABLE_PDEV_ERR); } TRC_DBG((TB, "Returning %p", rc)); DC_END_FN(); return rc; } /****************************************************************************/ // DrvDisablePDEV - see NT DDK documentation // // Release the resources allocated in DrvEnablePDEV. If a surface has been // enabled DrvDisableSurface will have already been called. Note that this // function will be called when previewing modes in the Display Applet, but // not at system shutdown. Note: In an error, we may call this before // DrvEnablePDEV is done. /****************************************************************************/ VOID DrvDisablePDEV(DHPDEV dhpdev) { PDD_PDEV pPDev = (PDD_PDEV)dhpdev; DC_BEGIN_FN("DrvDisablePDEV"); TRC_NRM((TB, "Disabling PDEV %p", dhpdev)); // Free the resources we allocated for the display. if (pPDev != NULL) { // Destroy the default palette, if created. if (pPDev->hpalDefault != 0) { EngDeletePalette(pPDev->hpalDefault); pPDev->hpalDefault = 0; } EngFreeMem(pPDev); } DC_END_FN(); } /****************************************************************************/ /* DrvCompletePDEV - see NT DDK documentation */ /* */ /* Stores the HPDEV, the engine's handle for this PDEV, in the DHPDEV. */ /****************************************************************************/ VOID DrvCompletePDEV(DHPDEV dhpdev, HDEV hdev) { DC_BEGIN_FN("DrvCompletePDEV"); // Store the device handle for our display handle. TRC_NRM((TB, "Completing PDEV %p", dhpdev)); ((PDD_PDEV)dhpdev)->hdevEng = hdev; DD_UPD_STATE(DD_COMPLETE_PDEV); DC_END_FN(); } /****************************************************************************/ /* DrvShadowConnect - called when the display driver should start */ /* shadowing. */ /* */ /* Primary job seems to be getting the shadow target WD up and running by */ /* pretending the display driver is coming up for the first time. Also */ /* */ /* Params: IN - pClientThinwireData (DD data from client) */ /* IN - ThinwireDataLength (length of data) */ /****************************************************************************/ BOOL DrvShadowConnect(PVOID pClientThinwireData, ULONG ThinwireDataLength) { TSHARE_DD_SHADOWSYNC_IN shadowSync; ULONG bytesReturned; NTSTATUS status; BOOL rc = FALSE; DC_BEGIN_FN("DrvShadowConnect"); DD_UPD_STATE(DD_SHADOW_SETUP); // Make sure we are still connected! TODO: Restrict to only one shadow for // now... TRC_ERR((TB, "Shadow Connect: %p [%ld]", pClientThinwireData, ThinwireDataLength)); #ifdef DC_DEBUG // NT BUG 539912 - track calls to DD fns. DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_SHADOWCONNECT, pClientThinwireData, ThinwireDataLength, ddConnected, pddTSWdShadow); #endif if ((ddConnected) && (pddTSWdShadow == NULL)) { // Drive the DD and WD into a disconnected state. Indicate this is in // preparation for a shadow session to disable saving the persistent key // database, etc. It also has the effect of destroying the SHM and // taking down all of the related cache information and encoding state. ddIgnoreShadowDisconnect = FALSE; TRC_ERR((TB, "Disconnecting stack prior to shadow")); DDDisconnect(TRUE); TRC_ERR((TB, "Done disconnecting")); // Reconnect to the WD to establish the shadow session TRC_ERR((TB, "Reinitializing primary/shadow stacks: ddConnected(%ld)", ddConnected)); // If both stacks connected successfully, reestablish the SHM and // recreate the caches and encoding state. if (DDInit(NULL, TRUE, FALSE, (PTSHARE_VIRTUAL_MODULE_DATA) pClientThinwireData, ThinwireDataLength)) { #ifdef DC_HICOLOR // Get the shadower caps - in particular, it may have changed its // cache caps because of a color depth change PTSHARE_VIRTUAL_MODULE_DATA pShadowCaps; ULONG dataLen = 256; // Supply a small amount of memory so the Wd can tell us how much // it actually needs - we can't just use the returned length from // EngFileIoControl since when the IOCTL gets passed to both the // primary and shadow stacks, the shadow's result overwrites the // primary's result. Doh! pShadowCaps = EngAllocMem(FL_ZERO_MEMORY, dataLen, DD_ALLOC_TAG); if (pShadowCaps) { // First pass tells us the size we need for the caps TRC_ERR((TB, "Getting shadow caps len...")); status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_QUERY_SHADOW_CAPS, NULL, 0, pShadowCaps, dataLen, &dataLen); if (pShadowCaps->capsLength) { TRC_ERR((TB, "Getting shadow caps...")); // remember this is the *caps* len - we need a bit // extra for the rest of a vurtual module data structure dataLen = pShadowCaps->capsLength + sizeof(unsigned); // Free the old memory! EngFreeMem(pShadowCaps); pShadowCaps = EngAllocMem(FL_ZERO_MEMORY, dataLen, DD_ALLOC_TAG); if (pShadowCaps) { // now we'll get the data status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_QUERY_SHADOW_CAPS, NULL, 0, pShadowCaps, dataLen, &dataLen); } else { TRC_ERR((TB, "Couldn't get memory for shadow caps")); status = STATUS_NO_MEMORY; } } else { TRC_ERR((TB, "Unexpected status %08lx", status)); status = STATUS_BUFFER_OVERFLOW; } } else { TRC_ERR((TB, "Couldn't get memory for shadow caps")); status = STATUS_NO_MEMORY; } if (status != STATUS_SUCCESS) { TRC_ERR((TB, "Couldn't get updated shadow caps")); DC_QUIT; } #endif // Tell the shadow target and shadow client(s) to synchronize TRC_ERR((TB, "Shadow Connect - WD Sync Start")); shadowSync.pShm = pddShm; #ifdef DC_HICOLOR shadowSync.capsLen = pShadowCaps->capsLength; shadowSync.pShadowCaps = &pShadowCaps->combinedCapabilities; #endif status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_SHADOW_SYNCHRONIZE, &shadowSync, sizeof(shadowSync), NULL, 0, &bytesReturned); TRC_ERR((TB, "Shadow Connect - WD Sync End")); #ifdef DC_HICOLOR // release the caps memory if (pShadowCaps) { EngFreeMem(pShadowCaps); } #endif // Free all pending orders. This is OK as we will get a full redraw // when the shadow starts BAResetBounds(); // With Direct Encoding, at this point the orders in the order // heap have already changed the encoding state, blowing away // orders at this point will cause inconsistent state of the encoding // table between the server and client. This is because we keep // the last order type sent, so blowing away orders here means // order type will not be sent to the client, but the server encoding // table and state still kept the last order state. It's almost // impossible to rewind the orders at this point. So, we simply have // to send the orders to the client to keep order encoding state // consistent. //OA_DDSyncUpdatesNow(); if (status != STATUS_SUCCESS) { TRC_ERR((TB,"Could not synchronize primary/shadow stacks: %lx", status)); } rc = NT_SUCCESS(status); } else { TRC_ERR((TB,"Could not connect to primary/shadow stacks")); } } // TODO: This is a temporary restriction until we allow n-way shadowing. // Rejecting this connection causes us to get an associated // DrvShadowDisconnect() which we need to ignore. See bug 229479 else { TRC_ERR((TB, "Shadow Connect: already shadowing -> reject!")); ddIgnoreShadowDisconnect = TRUE; rc = STATUS_CTX_SHADOW_DENIED; } #ifdef DC_HICOLOR DC_EXIT_POINT: #endif DD_CLR_STATE(DD_SHADOW_SETUP); DC_END_FN(); return rc; } /****************************************************************************/ /* DrvShadowDisconnect - called when the display driver should stop */ /* shadowing. */ /* */ /* Primary job seems to be telling the shadow target WD that shadowing is */ /* stopping and potentially restoring the former capability set for the */ /* target. */ /* */ /* Params: IN - pClientThinwireData (DD data from client) */ /* IN - ThinwireDataLength (length of data) */ /****************************************************************************/ BOOL DrvShadowDisconnect(PVOID pThinwireData, ULONG ThinwireDataLength) { NTSTATUS status; ULONG bytesReturned; TSHARE_DD_DISCONNECT_IN disconnIn; DC_BEGIN_FN("DrvShadowDisconnect"); // Now tell the WD we're disconnecting. We don't do anything with a // failure here - there's no point - we're already disconnecting! TRC_ERR((TB, "Shadow Disconnect: %p [%ld]", pThinwireData, ThinwireDataLength)); #ifdef DC_DEBUG // NT BUG 539912 - track calls to DD fns. DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_SHADOWDISCONNECT, pThinwireData, ThinwireDataLength, ddConnected, ddIgnoreShadowDisconnect); #endif if (ddConnected) { // For now we are limited to one shadow per session. Any subsequent // attempts will be rejected, but we must ignore the associated // and unnecessary disconnect! if (!ddIgnoreShadowDisconnect) { pddShm->pShadowInfo = NULL; disconnIn.pShm = pddShm; disconnIn.bShadowDisconnect = FALSE; status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_SHADOW_DISCONNECT, &disconnIn, sizeof(disconnIn), NULL, 0, &bytesReturned); TRC_ERR((TB, "Status on Shadow Disc IOCtl to WD %lu", status)); pddTSWdShadow = NULL; // Update capabilities now that a party left the share TRC_ERR((TB, "Updating new capabilities")); // Initiate a disconnect for shadow exiting DDDisconnect(TRUE); TRC_ERR((TB, "Done disconnecting")); // Reconnect to the WD to establish the primary session TRC_ERR((TB, "Reinitializing primary stack: ddConnected(%ld)", ddConnected)); // If primary stack connected successfully, reestablish the SHM and // recreate the caches and encoding state. if (DDInit(NULL, TRUE, FALSE, NULL, 0)) { TRC_NRM((TB, "Reintialized the DD")); status = STATUS_SUCCESS; } else { TRC_ERR((TB, "Failed to initialize DD Components")); status = STATUS_UNSUCCESSFUL; } } else { ddIgnoreShadowDisconnect = FALSE; status = STATUS_SUCCESS; } } // else we have already been disconnected so just return an error else { status = STATUS_FILE_CLOSED; } DC_END_FN(); return NT_SUCCESS(status); } /****************************************************************************/ /* DrvEnableSurface - see NT DDK documentation */ /* */ /* Creates the drawing surface and initializes driver components. This */ /* function is called after DrvEnablePDEV, and performs the final device */ /* initialization. */ /****************************************************************************/ HSURF DrvEnableSurface(DHPDEV dhpdev) { PDD_PDEV pPDev = (PDD_PDEV)dhpdev; SIZEL sizl, tempSizl; HSURF rc = 0; ULONG memSize; PBYTE newFrameBuf; HANDLE SectionObject = NULL; DC_BEGIN_FN("DrvEnableSurface"); TRC_NRM((TB, "Enabling surface for %p", dhpdev)); DD_UPD_STATE(DD_ENABLE_SURFACE_IN); // Have GDI create the actual SURFOBJ. sizl.cx = pPDev->cxScreen; sizl.cy = pPDev->cyScreen; /************************************************************************/ /* An RDP display driver has a bitmap where GDI does all its drawing, */ /* since it is the only driver in the IWS. We need to allocate the */ /* bitmap ourselves in order to know its address. */ /* */ /* We allocate a Frame Buffer at DrvEnableSurface time to make */ /* sure that the frame buffer surface is same as the device surface */ /* GDI thinks. This will prevent a lot of mismatch reconnect condition */ /************************************************************************/ #ifdef DC_HICOLOR if ((pPDev->cClientBitsPerPel != ddFrameBufBpp + 1) || (pddFrameBuf == NULL) || (ddFrameBufX < sizl.cx) || (ddFrameBufY < sizl.cy)) #else if ((pPDev->cClientBitsPerPel != ddFrameBufBpp) || (ddFrameBufX != sizl.cx) || (ddFrameBufY != sizl.cy)) #endif { // Allocate a new one. Note that we do not free the old one here - // that's done in DrvDisableSurface. memSize = TS_BYTES_IN_BITMAP(pPDev->cxScreen, pPDev->cyScreen, pPDev->cClientBitsPerPel); newFrameBuf = (PBYTE)EngAllocSectionMem(&SectionObject, FL_ZERO_MEMORY, memSize, DD_ALLOC_TAG); if (newFrameBuf == NULL) { TRC_ERR((TB, "DrvEnableSurface - " "Failed FrameBuf EngAllocSectionMem for %lu bytes", memSize)); newFrameBuf = (PBYTE)EngAllocMem(FL_ZERO_MEMORY, memSize, DD_ALLOC_TAG); SectionObject = NULL; } #ifdef DC_DEBUG // NT BUG 539912 - Instance count section memory objects else { dbg_ddSectionAllocs++; TRC_DBG(( TB, "DrvEnableSurface - %d outstanding surfaces allocated", dbg_ddSectionAllocs )); DBG_DD_FNCALL_HIST_ADD( DBG_DD_ALLOC_SECTIONOBJ, dbg_ddSectionAllocs, 0, newFrameBuf, SectionObject); } #endif TRC_NRM((TB, "Reallocate Frame Buffer %p, SectionObject %p", newFrameBuf, SectionObject)); if (newFrameBuf == NULL) { TRC_ERR((TB, "DrvEnableSurface - " "Failed FrameBuf EngAllocMem for %lu bytes", memSize)); if (pddFrameBuf == NULL) { // Reset the frame buffer size back to 0. ddFrameBufX = ddFrameBufY = 0; } DC_QUIT; } pddFrameBuf = newFrameBuf; ddFrameBufX = sizl.cx; ddFrameBufY = sizl.cy; ddFrameBufBpp = pPDev->cClientBitsPerPel; ddFrameIFormat = pPDev->iBitmapFormat; ddSectionObject = SectionObject; } // Create the frame buffer surface. tempSizl.cx = ddFrameBufX; tempSizl.cy = ddFrameBufY; pPDev->hsurfFrameBuf = (HSURF)EngCreateBitmap(tempSizl, TS_BYTES_IN_SCANLINE(ddFrameBufX, ddFrameBufBpp), ddFrameIFormat, BMF_TOPDOWN, (PVOID)pddFrameBuf); if (pPDev->hsurfFrameBuf == 0) { TRC_ERR((TB, "Could not allocate surface")); DC_QUIT; } // Update Frame Buffer pointers in PDEV. pPDev->pFrameBuf = pddFrameBuf; pPDev->SectionObject = ddSectionObject; // Associate the frame buffer with the pdev. if (EngAssociateSurface(pPDev->hsurfFrameBuf, pPDev->hdevEng, 0)) { // Get a pointer to the frame buffer SURFOBJ. pPDev->psoFrameBuf = EngLockSurface(pPDev->hsurfFrameBuf); } else { TRC_ERR((TB, "EngAssociateSurface failed: hsurfFrameBuf(%p)", pPDev->hsurfFrameBuf)); DC_QUIT; } /************************************************************************/ /* Create a device surface. This is what we will pass back to the */ /* Graphics Engine. The fact that it is a device surface forces all */ /* drawing to come through the display driver. */ /* */ /* We pass the Frame Buffer SURFOBJ pointer as the DHSURF, so we can */ /* easily convert the (SURFOBJ *) parameters in the Drv... functions */ /* into real Frame Buffer SURFOBJ pointers: */ /* */ /* psoFrameBuf = (SURFOBJ *)(psoTrg->dhsurf); */ /************************************************************************/ pPDev->hsurfDevice = EngCreateDeviceSurface((DHSURF)pPDev->psoFrameBuf, sizl, pPDev->iBitmapFormat); // Now associate the device surface and the PDEV. if (!EngAssociateSurface(pPDev->hsurfDevice, pPDev->hdevEng, pPDev->flHooks)) { TRC_ERR((TB, "DrvEnableSurface - Failed EngAssociateSurface")); DC_QUIT; } TRC_NRM((TB, "hsurfFrameBuf(%p) hsurfDevice(%p) psoFrameBuf(%p)", pPDev->hsurfFrameBuf, pPDev->hsurfDevice, pPDev->psoFrameBuf)); // Finally initialize the DD components, if necessary. if (ddInitPending) { TRC_NRM((TB, "DD init pending")); ddInitPending = FALSE; if (!DDInit(pPDev, FALSE, FALSE, NULL, 0)) { TRC_ERR((TB, "Failed to initialize DD Components")); DC_QUIT; } } else { // Don't do this is we're not connected. if (ddConnected && pddShm != NULL) { TRC_ALT((TB, "Re-enable surface")); // Initialization not pending - this must be a desktop change. // Flush the SDA & Order Heap. TRC_ALT((TB, "New surface")); BAResetBounds(); // With Direct Encoding, at this point the orders in the order // heap have already changed the encoding state, blowing away // orders at this point will cause inconsistent state of the encoding // table between the server and client. This is because we keep // the last order type sent, so blowing away orders here means // order type will not be sent to the client, but the server encoding // table and state still kept the last order state. It's almost // impossible to rewind the orders at this point. So, we simply have // to send the orders to the client to keep order encoding state // consistent. //OA_DDSyncUpdatesNow(); // SBC_DDSync(); // TODO: Determine how this affects shadowing!!! DD_UPD_STATE(DD_REINIT); } else { TRC_ALT((TB, "Not connected")); } } // We have successfully associated the surface so return it to the GDI. rc = pPDev->hsurfDevice; DD_UPD_STATE(DD_ENABLE_SURFACE_OUT); TRC_NRM((TB, "Enabled surface for %p, FB %p", pPDev, pPDev->pFrameBuf)); DC_EXIT_POINT: // Tidy up any resources if we failed. if (rc == 0) { DrvDisableSurface((DHPDEV) pPDev); DD_UPD_STATE(DD_ENABLE_SURFACE_ERR); } DC_END_FN(); return rc; } /****************************************************************************/ /* DrvDisableSurface - see NT DDK documentation */ /* */ /* Free resources allocated by DrvEnableSurface. Release the surface. */ /* */ /* Note that this function will be called when previewing modes in the */ /* Display Applet, but not at system shutdown. If you need to reset the */ /* hardware at shutdown, you can do it in the miniport by providing a */ /* 'HwResetHw' entry point in the VIDEO_HW_INITIALIZATION_DATA structure. */ /* */ /* Note: In an error case, we may call this before DrvEnableSurface is */ /* completely done. */ /****************************************************************************/ VOID DrvDisableSurface(DHPDEV dhpdev) { BOOL rc; PDD_PDEV pPDev = (PDD_PDEV)dhpdev; DC_BEGIN_FN("DrvDisableSurface"); TRC_NRM((TB, "Disabling surface for %p", dhpdev)); if (pPDev->psoFrameBuf != NULL) { EngUnlockSurface(pPDev->psoFrameBuf); pPDev->psoFrameBuf = NULL; } if (pPDev->hsurfDevice != 0) { TRC_DBG((TB, "Deleting device surface")); EngDeleteSurface(pPDev->hsurfDevice); pPDev->hsurfDevice = 0; } // Delete the Frame Buffer only if it is not still in use. if (pPDev->hsurfFrameBuf != 0) { TRC_DBG((TB, "Deleting frame buffer surface")); EngDeleteSurface(pPDev->hsurfFrameBuf); pPDev->hsurfFrameBuf = 0; } if ((pPDev->pFrameBuf != NULL) && (pPDev->pFrameBuf != pddFrameBuf)) { if (pPDev->SectionObject != NULL) { TRC_NRM((TB, "Freeing section frame buffer %p", pPDev->pFrameBuf)); rc = EngFreeSectionMem(pPDev->SectionObject, (PVOID)pPDev->pFrameBuf); if (!rc) { TRC_ABORT((TB, "EngFreeSectionMem failed, section object will " "leak")); #ifdef DC_DEBUG WDIcaBreakOnDebugger(); #endif // DC_DEBUG } #ifdef DC_DEBUG else { // NT BUG 539912 - Instance count section memory objects dbg_ddSectionAllocs--; TRC_DBG(( TB, "DrvDisableSurface - %d outstanding surfaces allocated", dbg_ddSectionAllocs )); DBG_DD_FNCALL_HIST_ADD( DBG_DD_FREE_SECTIONOBJ_SURFACE, dbg_ddSectionAllocs, 0, pddFrameBuf, ddSectionObject); } #endif pPDev->SectionObject = NULL; } else { TRC_NRM((TB, "Freeing frame buffer %p", pPDev->pFrameBuf)); EngFreeMem((PVOID)pPDev->pFrameBuf); } pPDev->pFrameBuf = NULL; } DC_END_FN(); } /****************************************************************************/ // DDHandleWDSync // // Moves rare WD SHM data update notifications out of the perf path. /****************************************************************************/ void DDHandleWDSync() { ULONG bytesReturned; NTSTATUS Status; DC_BEGIN_FN("DDHandleWDSync"); // Now look for any updated fields that might be available. if (pddShm->oe.newCapsData) { TRC_DBG((TB, "Update for OE, %d", pddShm->oe.newCapsData)); OE_Update(); } if (pddShm->sbc.newCapsData) { TRC_NRM((TB, "newCapsData for SBC")); SBC_Update(NULL); } if (pddShm->sbc.syncRequired) { TRC_NRM((TB, "syncRequired for SBC")); SBC_DDSync(FALSE); } if (pddShm->sbc.fClearCache) { unsigned i; // reset the flag pddShm->sbc.fClearCache = FALSE; // walk through each cache to determine if that cache // needs to be cleared for (i = 0; i < pddShm->sbc.NumBitmapCaches; i++) { if (pddShm->sbc.bitmapCacheInfo[i].fClearCache) { TRC_NRM((TB, "clear cache with cacheID=%d", i)); // clear the entries in the cache CH_ClearCache(pddShm->sbc.bitmapCacheInfo[i]. cacheHandle); // reset the clear cache flag in SBC pddShm->sbc.bitmapCacheInfo[i].fClearCache = FALSE; } } // send an IOCTL to RDPWD for screen redraw Status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_REDRAW_SCREEN, NULL, 0, NULL, 0, &bytesReturned); if (Status != STATUS_SUCCESS) { TRC_ERR((TB, "Redraw Screen IOCtl returned %lu", Status)); } } if (pddShm->sbc.fDisableOffscreen) { // reset the flag pddShm->sbc.fDisableOffscreen = FALSE; // disable offscreen rendering support pddShm->sbc.offscreenCacheInfo.supportLevel = TS_OFFSCREEN_DEFAULT; // send an IOCTL to RDPWD for screen redraw Status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_REDRAW_SCREEN, NULL, 0, NULL, 0, &bytesReturned); if (Status != STATUS_SUCCESS) { TRC_ERR((TB, "Redraw Screen IOCtl returned %lu", Status)); } } #ifdef DRAW_NINEGRID if (pddShm->sbc.fDisableDrawNineGrid) { // reset the flag pddShm->sbc.fDisableDrawNineGrid = FALSE; // disable offscreen rendering support pddShm->sbc.drawNineGridCacheInfo.supportLevel = TS_DRAW_NINEGRID_DEFAULT; // send an IOCTL to RDPWD for screen redraw Status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_REDRAW_SCREEN, NULL, 0, NULL, 0, &bytesReturned); if (Status != STATUS_SUCCESS) { TRC_ERR((TB, "Redraw Screen IOCtl returned %lu", Status)); } } #endif #ifdef DRAW_GDIPLUS if (pddShm->sbc.fDisableDrawGdiplus) { // reset the flag pddShm->sbc.fDisableDrawGdiplus = FALSE; // disable gdiplus support pddShm->sbc.drawGdiplusInfo.supportLevel = TS_DRAW_GDIPLUS_DEFAULT; // send an IOCTL to RDPWD for screen redraw Status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_REDRAW_SCREEN, NULL, 0, NULL, 0, &bytesReturned); if (Status != STATUS_SUCCESS) { TRC_ERR((TB, "Redraw Screen IOCtl returned %lu", Status)); } } #endif // Check SSI flags. if (pddShm->ssi.saveBitmapSizeChanged || pddShm->ssi.resetInterceptor) { TRC_DBG((TB, "Update for SSI, %d:%d", pddShm->ssi.saveBitmapSizeChanged, pddShm->ssi.resetInterceptor)); SSI_Update(FALSE); } DC_END_FN(); } /****************************************************************************/ // DrvEscape - see NT DDK documentation. /****************************************************************************/ ULONG DrvEscape( SURFOBJ *pso, ULONG iEsc, ULONG cjIn, PVOID pvIn, ULONG cjOut, PVOID pvOut) { ULONG rc = FALSE; ULONG escCode = 0; ULONG bytesReturned; NTSTATUS status; TSHARE_DD_TIMER_INFO timerInfo; TSHARE_DD_OUTPUT_IN outputIn; PDD_PDEV pPDev; DC_BEGIN_FN("DrvEscape"); // DrvEscape sometimes gets called after the driver has terminated, // especially with ESC_TIMEROBJ_SIGNALLED. if (ddConnected) { pPDev = (PDD_PDEV)pso->dhpdev; // Performance path in this function is the desktop thread timer // trigger. if (iEsc == ESC_TIMEROBJ_SIGNALED) { TRC_DBG((TB, "Got a timer kick - IOCtl to WD")); TRC_ASSERT((NULL != pso), (TB, "NULL pso")); rc = TRUE; // Race condition: we got output (or, more likely, a timer pop) // after a disconnect. Just ignore it. if (NULL != pddShm) { status = SCH_DDOutputAvailable(pPDev, TRUE); // If this fails, either // - the failure was in the WD, and it's up to the WD to // correct it (or quit the session) // - the failure was in the infrastructure carrying the // IOCtl to the WD. There's nothing we can do in this // case other than try on the next output call. if (status != STATUS_SUCCESS) { TRC_ERR((TB, "Error on sending output IOCtl, status %lu", status)); } if (!pddShm->fShmUpdate) { DC_QUIT; } else { DDHandleWDSync(); pddShm->fShmUpdate = FALSE; } } DC_QUIT; } } else { TRC_ERR((TB, "DrvEscape %s (%d) called after DD terminated", iEsc == QUERYESCSUPPORT ? "QUERYESCSUPPORT " : iEsc == ESC_TIMEROBJ_SIGNALED ? "ESC_TIMEROBJ_SIGNALED" : iEsc == ESC_SET_WD_TIMEROBJ ? "ESC_SET_WD_TIMEROBJ " : "- Unknown -", iEsc)); // Return FALSE for QUERYESCSUPPORT, TRUE for others (otherwise // USER asserts). rc = (iEsc == QUERYESCSUPPORT ? FALSE : TRUE); DC_QUIT; } // Process the non-performance-path escape codes. switch (iEsc) { case QUERYESCSUPPORT: // Do we support the function? If so, mark the function as OK. escCode = *((PUINT32)pvIn); TRC_DBG((TB, "Query for escape code %lu", escCode)); if ((escCode == ESC_TIMEROBJ_SIGNALED) || (escCode == ESC_SET_WD_TIMEROBJ)) { // Supported functions - return TRUE. TRC_DBG((TB, "We support escape code %lu", escCode)); rc = TRUE; } break; case ESC_SET_WD_TIMEROBJ: { DD_UPD_STATE(DD_TIMEROBJ); // We have been given the timer details from Win32: pass them // to the WD. Note, only allow this to occur once to prevent // evil apps from trying to fake this call. if (pddWdTimer == NULL) { if (cjIn != sizeof(PKTIMER)) { TRC_ERR((TB, "Unexpected size %lu arrived", cjIn)); } else { // Got the timer object OK. Save the handle here, and // then IOCtl across to the WD to tell it the handle. TRC_DBG((TB, "Timer object %p arrived", pvIn)); pddWdTimer = (PKTIMER)pvIn; TRC_ASSERT((ddWdHandle != NULL), (TB, "NULL WD handle")); timerInfo.pKickTimer = pddWdTimer; status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_TIMER_INFO, &timerInfo, sizeof(TSHARE_DD_TIMER_INFO), NULL, 0, &bytesReturned); if (status != STATUS_SUCCESS) { TRC_ERR((TB, "Timer Info IOCtl returned %lu", status)); // Looking at the current NT code, there is NO WAY of // reporting an error on this operation. If we return // 0 from DrvEscape, then USER will assert. Great. } } } rc = TRUE; } break; #ifdef DC_DEBUG // This event is generated by Bungle, a test app which displays the // contents of the frame buffer. Here we return it the address of // the frame buffer so it can do the displaying. case 3: { ULONG cBytes; TRC_ALT((TB, "copy frame buffer requested")); pPDev = (PDD_PDEV)pso->dhpdev; cBytes = (ULONG)(pPDev->cxScreen * pPDev->cyScreen); if (cjOut != cBytes) { TRC_ERR((TB, "Wrong memory block size")); } else { memcpy(pvOut, pPDev->pFrameBuf, cBytes); rc = TRUE; } } break; #ifdef i386 // This event will be generated by the DbgBreak program. It forces // us to break to the kernel debugger in the right WinStation // context and in the DD, thus letting us set break points in a // sensible fashion! case 4: TRC_ALT((TB, "break to debugger requested")); _asm int 3; break; #endif #endif // DC_DEBUG case ESC_GET_DEVICEBITMAP_SUPPORT: { SIZEL bitmapSize; if (cjIn >= sizeof(ICA_DEVICE_BITMAP_INFO)) { if (cjOut >= sizeof(ULONG)) { bitmapSize.cx = (*((PICA_DEVICE_BITMAP_INFO)pvIn)).cx; bitmapSize.cy = (*((PICA_DEVICE_BITMAP_INFO)pvIn)).cy; rc = TRUE; if (OEDeviceBitmapCachable(pPDev, bitmapSize, pPDev->iBitmapFormat)) { *((PULONG)pvOut) = TRUE; } else { *((PULONG)pvOut) = FALSE; } } else { TRC_ERR((TB, "Wrong output block size")); } } else { TRC_ERR((TB, "Wrong input block size")); } } break; default: TRC_ERR((TB, "Unrecognised request %lu", iEsc)); break; } DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ /* DrvGetModes - see NT DDK documentation */ /* */ /* Returns the list of available modes for the device. */ /****************************************************************************/ ULONG DrvGetModes(HANDLE hDriver, ULONG cjSize, DEVMODEW *pdm) { INT32 cModes; INT32 cbOutputSize = 0; PVIDEO_MODE_INFORMATION pVideoModeInformation = NULL; PVIDEO_MODE_INFORMATION pVideoTemp; INT32 cOutputModes = cjSize / sizeof(DEVMODEW); INT32 cbModeSize; DC_BEGIN_FN("DrvGetModes"); TRC_NRM((TB, "DrvGetModes")); // Get the list of valid modes. cModes = DDGetModes(hDriver, &pVideoModeInformation, &cbModeSize); // Should only ever return zero modes or one mode: // If we're chained into the console session, we'll see zero modes so // we return 0 to indicate that we will do whatever was set up in the // registry before we got loaded. Otherwise, if we got more than one mode // we bail out now. if (cModes == -1) { TRC_NRM((TB, "DrvGetModes returning 0 modes")); ddConsole = TRUE; DC_QUIT; } if (cModes != 1) { TRC_ERR((TB, "DrvGetModes failed to get mode information")); ddConsole = FALSE; DC_QUIT; } if (pdm == NULL) { // Return the size of the buffer required to receive all our modes. cbOutputSize = cModes * sizeof(DEVMODEW); TRC_DBG((TB, "Require %ld bytes for data", cbOutputSize)); } else { // Now copy the information for the supported modes back into the // output buffer. cbOutputSize = 0; pVideoTemp = pVideoModeInformation; do { if (pVideoTemp->Length != 0) { // Check we still have room in the buffer. if (cOutputModes == 0) { TRC_DBG((TB, "No more room %ld modes left", cModes)); break; } // Clear the structure. memset(pdm, 0, sizeof(DEVMODEW)); // Set the name of the device to the name of the DLL. memcpy(pdm->dmDeviceName, DD_DLL_NAME, sizeof(DD_DLL_NAME)); // Fill in the rest of the mode info. pdm->dmSpecVersion = DM_SPECVERSION; pdm->dmDriverVersion = DM_SPECVERSION; pdm->dmSize = sizeof(DEVMODEW); pdm->dmDriverExtra = 0; pdm->dmBitsPerPel = pVideoTemp->BitsPerPlane; pdm->dmPelsWidth = pVideoTemp->VisScreenWidth; pdm->dmPelsHeight = pVideoTemp->VisScreenHeight; pdm->dmDisplayFrequency = pVideoTemp->Frequency; pdm->dmDisplayFlags = 0; pdm->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS ; TRC_NRM((TB, "Returned mode info:")); TRC_NRM((TB, " pdm->dmBitsPerPel: %u", pdm->dmBitsPerPel)); TRC_NRM((TB, " pdm->dmPelsWidth: %u", pdm->dmPelsWidth)); TRC_NRM((TB, " pdm->dmPelsHeight: %u", pdm->dmPelsHeight)); TRC_NRM((TB, " pdm->dmDisplayFrequency: %u", pdm->dmDisplayFrequency)); // Go to the next DEVMODE entry in the buffer. cOutputModes--; pdm = (LPDEVMODEW) ( ((UINT_PTR)pdm) + sizeof(DEVMODEW)); cbOutputSize += sizeof(DEVMODEW); } pVideoTemp = (PVIDEO_MODE_INFORMATION) (((PCHAR)pVideoTemp) + cbModeSize); } while (--cModes); } DC_EXIT_POINT: if (pVideoModeInformation != NULL) { TRC_DBG((TB, "Freeing mode list")); EngFreeMem(pVideoModeInformation); } DC_END_FN(); return cbOutputSize; } /****************************************************************************/ // DrvAssertMode - see NT DDK documentation. /****************************************************************************/ BOOL DrvAssertMode(DHPDEV dhpdev, BOOL bEnable) { PDD_PDEV pPDev = (PDD_PDEV)dhpdev; BOOL bRc; SURFOBJ *psoFrameBuf; SURFOBJ *psoDevice; DC_BEGIN_FN("DrvAssertMode"); TRC_NRM((TB, "pPDev %p, bEnable %d", pPDev, bEnable)); #ifdef DC_DEBUG // NT BUG 539912 - track calls to DD fns. DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_ASSERTMODE, dhpdev, bEnable, pddFrameBuf, ddSectionObject); #endif if (bEnable) { // The surface is being re-enabled. TRC_ALT((TB, "Enabling pPDev %p", pPDev)); // Re-associate the surface handles with the device handle. if (!EngAssociateSurface(pPDev->hsurfFrameBuf, pPDev->hdevEng, 0)) { TRC_ERR((TB, "Failed to associate surface %p and dev %p", pPDev->hsurfFrameBuf, pPDev->hdevEng)); bRc = FALSE; DC_QUIT; } if (!EngAssociateSurface(pPDev->hsurfDevice, pPDev->hdevEng, pPDev->flHooks)) { TRC_ERR((TB, "Failed to associate surface %p and dev %p", pPDev->hsurfDevice, pPDev->hdevEng)); bRc = FALSE; DC_QUIT; } TRC_ALT((TB, "Associated surfaces %p & %p with dev %p", pPDev->hsurfDevice, pPDev->hsurfFrameBuf, pPDev->hdevEng)); TRC_ASSERT((pddFrameBuf != NULL), (TB, "NULL frame buffer")); // Fixup the Frame Buffer surface object to point to the current // Frame Buffer. psoFrameBuf = pPDev->psoFrameBuf; TRC_ASSERT((psoFrameBuf != NULL), (TB,"NULL psoFrameBuf")); TRC_ASSERT((psoFrameBuf->iType == STYPE_BITMAP), (TB, "Wrong FB surface iType, %d", psoFrameBuf->iType)); psoFrameBuf->sizlBitmap.cx = ddFrameBufX; psoFrameBuf->sizlBitmap.cy = ddFrameBufY; psoFrameBuf->cjBits = TS_BYTES_IN_BITMAP(ddFrameBufX, ddFrameBufY, ddFrameBufBpp); psoFrameBuf->pvBits = pddFrameBuf; psoFrameBuf->pvScan0 = pddFrameBuf; psoFrameBuf->lDelta = TS_BYTES_IN_SCANLINE(ddFrameBufX, ddFrameBufBpp); #ifdef DC_HICOLOR TRC_ERR((TB, "New DD frameBufBpp %d", ddFrameBufBpp)); switch (ddFrameBufBpp) { case 4: psoFrameBuf->iBitmapFormat = BMF_4BPP; break; case 8: psoFrameBuf->iBitmapFormat = BMF_8BPP; break; case 15: case 16: psoFrameBuf->iBitmapFormat = BMF_16BPP; break; case 24: psoFrameBuf->iBitmapFormat = BMF_24BPP; break; default: TRC_ERR((TB, "Unsupported frame buf bpp %u - default to 8", ddFrameBufBpp)); psoFrameBuf->iBitmapFormat = BMF_8BPP; break; } #else psoFrameBuf->iBitmapFormat = ddFrameBufBpp == 8 ? BMF_8BPP : BMF_4BPP; #endif // Fixup the device surface object with the characteristics of the // current Frame Buffer. psoDevice = EngLockSurface(pPDev->hsurfDevice); TRC_ASSERT((psoDevice != NULL), (TB,"Null device surfac")); TRC_ASSERT((psoDevice->iType == STYPE_DEVICE), (TB, "Wrong device surface iType, %d", psoDevice->iType)); TRC_ASSERT((psoDevice->pvBits == NULL), (TB, "Device surface has bits, %p", psoDevice->pvBits)); TRC_ASSERT((psoDevice->dhsurf == (DHSURF)psoFrameBuf), (TB, "Wrong dhSurf, expect/is %p/%p", psoFrameBuf, psoDevice->dhsurf)); // We assert now since we should always get the same iBitmapFormat // as 8BPP. This will change once we have 24bit color support. // Then this needs to be looked at it and fix anything as necessary TRC_ASSERT((psoDevice->iBitmapFormat == psoFrameBuf->iBitmapFormat), (TB, "iBitmapFormat has changed")); // We shouldn't change the device surface size. This has already // been advertised to GDI, changing this will cause AV in GDI, // since GDI has cached the surface size. //psoDevice->sizlBitmap = psoFrameBuf->sizlBitmap; //psoDevice->iBitmapFormat = psoFrameBuf->iBitmapFormat; EngUnlockSurface(psoDevice); // We should never overwrite the frame-buffer pointer or section // object; This could cause a memory leak. If we hit this assert, // we can investigate if this does in fact lead to a memory leak. TRC_ASSERT(((pPDev->pFrameBuf == pddFrameBuf) && (pPDev->SectionObject == ddSectionObject)), (TB, "Frame buffer or section object pointer overwritten")); #ifdef DC_DEBUG // NT BUG 539912 - because the above assert is not hit in stress, we // change this case to produce an IcaBreakOnDebugger if (pPDev->pFrameBuf != pddFrameBuf || pPDev->SectionObject != ddSectionObject) { WDIcaBreakOnDebugger(); } #endif // Make sure the PDev points to the current Frame Buffer. pPDev->pFrameBuf = pddFrameBuf; TRC_ALT((TB, "Pointed PDev %p to Frame Buf %p", pPDev, pPDev->pFrameBuf)); // Make sure the pDev points to the current SectionObject pPDev->SectionObject = ddSectionObject; TRC_ALT((TB, "Pointed PDev %p to Section Object %p", pPDev, pPDev->SectionObject)); } bRc = TRUE; DC_EXIT_POINT: DC_END_FN(); return bRc; } /****************************************************************************/ /* Name: DrvDisconnect */ /* */ /* Purpose: Process a disconnect from W32 - clean up the output capture */ /* code and the connection to the WD. */ /* */ /* Returns: TRUE if all is well */ /* */ /* Params: IN - channel handle */ /* IN - file object for channel */ /* */ /* Operation: Gives all sub-components notice of the disconnect, and then */ /* IOCtls to the WD to tell it that we're going. */ /****************************************************************************/ BOOL DrvDisconnect(HANDLE channelHandle, PVOID pChannelFileObject) { DC_BEGIN_FN("DrvDisconnect"); TRC_NRM((TB, "DrvDisconnect called")); #ifdef DC_DEBUG // NT BUG 539912 - track calls to DD fns. DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_DISCONNECT, channelHandle, pChannelFileObject, ddConnected, 0); #endif // Check that we're connected. if (ddConnected) { // Terminate the dependent components. DDDisconnect(FALSE); } else { TRC_ERR((TB, "Disconnect called when not connected")); DD_UPD_STATE(DD_DISCONNECT_ERR); } DC_END_FN(); return TRUE; } /* DrvDisconnect */ /****************************************************************************/ /* Name: DrvConnect - see Citrix documentation/code */ /* */ /* Purpose: Called when a Winstation is first connected */ /* */ /* Returns: TRUE if all is well */ /* */ /* Params: IN - channel handle to use to IOCtl to WD */ /* IN - file object for channel - used on EngFileWrite */ /* IN - video file object */ /* IN - cache statistics memory. NB This is doc'd as OUT, but */ /* the code actually passes a ptr in. */ /* */ /* Operation: Save the key parameters */ /* */ /* Note that this function is called before DrvEnablePDEV and */ /* DrvEnableSurface, hence it is not a good place to initialize */ /* TShare components. This is done later, in DDInit, called */ /* from DrvEnableSurface. */ /****************************************************************************/ BOOL DrvConnect( HANDLE channelHandle, PVOID pChannelFileObject, PVOID pVideoFileObject, PVOID pThinWireCache) { PCACHE_STATISTICS pPerformanceCounters; DC_BEGIN_FN("DrvConnect"); TRC_NRM((TB, "DrvConnect")); #ifdef DC_DEBUG // NT BUG 539912 - track calls to DD fns. DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_CONNECT, channelHandle, pChannelFileObject, pVideoFileObject, pThinWireCache); #endif /************************************************************************/ /* Check for sensible values - the chained DD could get loaded other */ /* than on a connect call */ /************************************************************************/ if ((channelHandle == NULL) || (pChannelFileObject == NULL) || (pVideoFileObject == NULL) || (pThinWireCache == NULL)) { TRC_ERR((TB, "Null input params")); #ifdef DC_DEBUG TRC_ALT((TB, "But load anyway!")); return TRUE; #endif return FALSE; } #ifdef DC_DEBUG if (ddState & DD_DISCONNECT_OUT) DD_SET_STATE(DD_WAS_DISCONNECTED); DD_UPD_STATE(DD_CONNECT); #endif // Save the channel handle, and perf counters for later - the other // params are currently not needed. ddWdHandle = pChannelFileObject; pPerformanceCounters = pThinWireCache; pPerformanceCounters->ProtocolType = PROTOCOL_ICA; pPerformanceCounters->Length = sizeof(ICA_CACHE); pddCacheStats = pPerformanceCounters->Specific.IcaCacheStats.ThinWireCache; // Note that init is pending. ddInitPending = TRUE; // Note that we're connected. ddConnected = TRUE; DC_END_FN(); return TRUE; } /* DrvConnect */ /****************************************************************************/ /* Name: DrvReconnect */ /* */ /* Pass the IOCtl to the WD, and save off the returned values, as on */ /* connect. */ /****************************************************************************/ BOOL DrvReconnect(HANDLE channelHandle, PVOID pChannelFileObject) { BOOL rc; DC_BEGIN_FN("DrvReconnect"); TRC_NRM((TB, "DrvReconnect")); #ifdef DC_DEBUG // NT BUG 539912 - track calls to DD fns. DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_RECONNECT, channelHandle, pChannelFileObject, ddConnected, ddConsole); #endif // In case the dd has not been unloaded at the end of the previous // console shadow, we're getting called to handle this. if (ddConsole && ddConnected) { // no need to reconnect rc = TRUE; TRC_ASSERT((ddWdHandle == pChannelFileObject), (TB,"Reconnecting with different WD handle for Console Shadow)")); DC_QUIT; } #ifdef DC_DEBUG if (ddState & DD_DISCONNECT_OUT) DD_SET_STATE(DD_WAS_DISCONNECTED); DD_UPD_STATE(DD_RECONNECT_IN); #endif // Save the channel handle for later - the other params are currently // not needed. ddWdHandle = pChannelFileObject; // Note that we're connected. Do this whether we reconnect // successfully or not, as DrvDisconnect is called if we fail to */ // reconnect. ddConnected = TRUE; // Reinitialize RDPDD. rc = DDInit(NULL, TRUE, ddConsole?TRUE: FALSE, NULL, 0); if (!rc) { TRC_ERR((TB, "Failed to reinitialize DD")); } DD_UPD_STATE(DD_RECONNECT_OUT); DC_EXIT_POINT: DC_END_FN(); return rc; } /* DrvReconnect */ /****************************************************************************/ /* DrvResetPDEV - see NT DDK documentation */ /* */ /* Allows us to reject dynamic screen changes if necessary */ /****************************************************************************/ BOOL DrvResetPDEV(DHPDEV dhpdevOld, DHPDEV dhpdevNew) { BOOL rc = TRUE; ULONG bytesReturned; NTSTATUS Status; ICA_CHANNEL_END_SHADOW_DATA Data; DC_BEGIN_FN("DrvResetPDEV"); // On the console, we can only allow the display driver to change modes // while the connection is not up. if (ddConsole && ddConnected) { TRC_ALT((TB, "Mode change during console shadow: ending console shadow now")); Data.StatusCode = STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE; Data.bLogError = TRUE; Status = EngFileIoControl(ddWdHandle, IOCTL_ICA_CHANNEL_END_SHADOW, &Data, sizeof(Data), NULL, 0, &bytesReturned); } else { TRC_ALT((TB, "Allowing mode change")); } DC_END_FN(); return rc; } /****************************************************************************/ /* DrvGetDirectDrawInfo - see NT DDK documentation. */ /* */ /* Function called by DirectDraw to returns the capabilities of the */ /* graphics hardware */ /* */ /****************************************************************************/ BOOL DrvGetDirectDrawInfo( DHPDEV dhpdev, DD_HALINFO* pHalInfo, DWORD* pdwNumHeaps, VIDEOMEMORY* pvmList, // Will be NULL on first call DWORD* pdwNumFourCC, DWORD* pdwFourCC) // Will be NULL on first call { BOOL rc = TRUE; PDD_PDEV pPDev = (PDD_PDEV)dhpdev; BOOL bCanFlip=0; DC_BEGIN_FN("DrvGetDirectDrawInfo"); TRC_NRM((TB, "DrvGetDirectDrawInfo")); // DirectDraw only supports 8, 16, 24 or 32 bpp if ( (8 != pPDev->cClientBitsPerPel) && (16 != pPDev->cClientBitsPerPel) && (24 != pPDev->cClientBitsPerPel) && (32 != pPDev->cClientBitsPerPel) ) { rc = FALSE; DC_QUIT; } // DirectDraw is not supported if our frame buffer is not allocated as // section mem. if (pPDev->SectionObject == NULL) { TRC_ERR((TB, "The section object is null.")); rc = FALSE; DC_QUIT; } pHalInfo->dwSize = sizeof(*pHalInfo); // Current primary surface attributes. Since HalInfo is zero-initialized // by GDI, we only have to fill in the fields which should be non-zero: pHalInfo->vmiData.pvPrimary = pPDev->pFrameBuf; pHalInfo->vmiData.dwDisplayWidth = pPDev->cxScreen; pHalInfo->vmiData.dwDisplayHeight = pPDev->cyScreen; pHalInfo->vmiData.lDisplayPitch = pPDev->psoFrameBuf->lDelta; pHalInfo->vmiData.ddpfDisplay.dwSize = sizeof(DDPIXELFORMAT); pHalInfo->vmiData.ddpfDisplay.dwFlags = DDPF_RGB; pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = pPDev->cClientBitsPerPel; if (pPDev->iBitmapFormat == BMF_8BPP) { pHalInfo->vmiData.ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8; } // These masks will be zero at 8bpp: pHalInfo->vmiData.ddpfDisplay.dwRBitMask = pPDev->flRed; pHalInfo->vmiData.ddpfDisplay.dwGBitMask = pPDev->flGreen; pHalInfo->vmiData.ddpfDisplay.dwBBitMask = pPDev->flBlue; if (pPDev->iBitmapFormat == BMF_32BPP) { pHalInfo->vmiData.ddpfDisplay.dwRGBAlphaBitMask = ~(pPDev->flRed | pPDev->flGreen | pPDev->flBlue); } else { pHalInfo->vmiData.ddpfDisplay.dwRGBAlphaBitMask = 0; } //We don't support flip bCanFlip = FALSE; // We don't have any video memory for offscreen use *pdwNumHeaps = 0; // Capabilities supported: pHalInfo->ddCaps.dwFXCaps = 0; // No hardware support pHalInfo->ddCaps.dwCaps = DDCAPS_NOHARDWARE; pHalInfo->ddCaps.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; if (bCanFlip) { pHalInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_FLIP; } // FourCCs supported: *pdwNumFourCC = 0; // We see rdpdd passes 4bpp to directx in stress, which it does't support // so we assert here TRC_ASSERT(((pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount != 4) && (pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount != 15)), (TB, "RDPDD shoould not pass bpp %d to DirectX", pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount)); DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ /* DrvEnableDirectDraw - see NT DDK documentation. */ /* */ /* GDI calls DrvEnableDirectDraw to obtain pointers to the DirectDraw */ /* callbacks that the driver supports. */ /* */ /****************************************************************************/ BOOL DrvEnableDirectDraw( DHPDEV dhpdev, DD_CALLBACKS* pCallBacks, DD_SURFACECALLBACKS* pSurfaceCallBacks, DD_PALETTECALLBACKS* pPaletteCallBacks) { BOOL rc = TRUE; PDD_PDEV pPDev = (PDD_PDEV)dhpdev; DC_BEGIN_FN("DrvEnableDirectDraw"); TRC_NRM((TB, "DrvEnableDirectDraw")); #ifdef DC_DEBUG DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_ENABLEDIRECTDRAW, 0, 0, pPDev->SectionObject, ddSectionObject); #endif // DirectDraw is not supported if our frame buffer is not allocated as // section mem. if (pPDev->SectionObject == NULL ) { TRC_ERR((TB, "The section object is NULL!")); rc = FALSE; DC_QUIT; } pCallBacks->MapMemory = DdMapMemory; pCallBacks->dwFlags = DDHAL_CB32_MAPMEMORY; pSurfaceCallBacks->Lock = DdLock; pSurfaceCallBacks->Unlock = DdUnlock; pSurfaceCallBacks->dwFlags = DDHAL_SURFCB32_LOCK | DDHAL_SURFCB32_UNLOCK; DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ /* DrvDisableDirectDraw - see NT DDK documentation. */ /* */ /****************************************************************************/ VOID DrvDisableDirectDraw( DHPDEV dhpdev) { DC_BEGIN_FN("DrvDisableDirectDraw"); TRC_NRM((TB, "DrvDisableDirectDraw")); #ifdef DC_DEBUG DBG_DD_FNCALL_HIST_ADD( DBG_DD_FNCALL_DRV_DISABLEDIRECTDRAW, 0, 0, 0, ddSectionObject); #endif //Do nothing here DC_END_FN(); }