#include "precomp.h" // // SDG.CPP // Screen Data Grabber // Sends OUTGOING screen data when hosting // // Copyright(c) Microsoft 1997- // #define MLZ_FILE_ZONE ZONE_CORE // // SDG_SendScreenDataArea() // void ASHost::SDG_SendScreenDataArea(LPBOOL pBackPressure, UINT * pcPackets) { UINT i; BOOL fBltOK = TRUE; RECT sdaRect[BA_NUM_RECTS]; UINT cRects; BOOL backPressure = FALSE; BOOL secondaryTransmit = FALSE; DebugEntry(ASHost::SDG_SendScreenDataArea); // // Get the bounds of the screen data area. At entry this is always // our primary transmission area. Even if we had already flushed // the primary region and were in the middle of the secondary region // we will switch back to the primary region if any more SD // accumulates. In this way we keep our spoiling of the secondary // screendata maximized. // BA_CopyBounds(sdaRect, &cRects, TRUE); // // If there is a pending rectangle that was unable to be sent on a // previous transmission then try to send it first. // // Leave the lossy flag as it was at the last pass // if (m_sdgRectIsPending) { TRACE_OUT(( "Sending pending rectangle")); m_sdgRectIsPending = FALSE; // // Try to send the pending rectangle. SDGSplitBlt... will remove // any portions of it that are sent successfully. We will add all // the rest of the SDA back to the bounds in the loop below // if (!SDGSplitBltToNetwork(&m_sdgPendingRect, pcPackets)) { fBltOK = FALSE; m_sdgRectIsPending = TRUE; } else { // // The pending rectangle was successfully sent. // TRACE_OUT(( "Sent pending rect")); } } // // We have copied the primary transmit region so now move the secondary // transmit bounds into the screendata bounds because when we send data // in the primary transmission we want to accumulate any rectangles // that need subsequent retransmission. The retransmit bounds are // generally different from the original SD bounds because the // compression function is permitted to override our lossy request for // any portion of the data if it finds that the data is pretty // compressible anyway. In this way we end up with retransmission of // embedded photos etc, but the toolbars/buttons are only sent once. // // For the non-lossy case the secondary bounds will always be null, // so there is no point in special casing here. // if (fBltOK) { for (i = 0; i < m_sdgcLossy; i++) { TRACE_OUT(("Setting up pseudo-primary bounds {%d, %d, %d, %d}", m_asdgLossyRect[i].left, m_asdgLossyRect[i].top, m_asdgLossyRect[i].right, m_asdgLossyRect[i].bottom )); // // Add the rectangle into the bounds. // BA_AddRect(&m_asdgLossyRect[i]); } // // If there is no primary bitmap data to send then send the // secondary data. If none of that either then just exit // if (cRects == 0) { BA_CopyBounds(sdaRect, &cRects, TRUE); if (cRects == 0) { DC_QUIT; } else { TRACE_OUT(("Starting secondary transmission now")); secondaryTransmit = TRUE; } } } // // Process each of the supplied rectangles in turn. // TRACE_OUT(( "%d SDA rectangles", cRects)); for (i = 0; i < cRects; i++) { TRACE_OUT(("Rect %d: {%d, %d, %d, %d}", i, sdaRect[i].left, sdaRect[i].top, sdaRect[i].right, sdaRect[i].bottom )); // // Clip the rectangle to the physical screen and reject totally // any rectangle that refers to data that has now been scrolled off // the physical screen as a result of a desktop scroll between the // time the rectangle was accumulated and now. // if (sdaRect[i].left < 0) { if (sdaRect[i].right < 0) { // // This was scrolled off the physical screen by a desktop // scroll. // continue; } // // Partially off screen - just clip the left edge. // sdaRect[i].left = 0; } if (sdaRect[i].top < 0) { if (sdaRect[i].bottom < 0) { // // This was scrolled off the physical screen by a desktop // scroll. // continue; } // // Partially off screen - just clip the top edge. // sdaRect[i].top = 0; } if (sdaRect[i].right >= m_pShare->m_pasLocal->cpcCaps.screen.capsScreenWidth) { if (sdaRect[i].left >= m_pShare->m_pasLocal->cpcCaps.screen.capsScreenWidth) { // // This was scrolled off the physical screen by a desktop // scroll. // continue; } // // Partially off screen - just clip the right edge. // sdaRect[i].right = m_pShare->m_pasLocal->cpcCaps.screen.capsScreenWidth-1; } if (sdaRect[i].bottom >= m_pShare->m_pasLocal->cpcCaps.screen.capsScreenHeight) { if (sdaRect[i].top >= m_pShare->m_pasLocal->cpcCaps.screen.capsScreenHeight) { // // This was scrolled off the physical screen by a desktop // scroll. // continue; } // // Partially off screen - just clip the bottom edge. // sdaRect[i].bottom = m_pShare->m_pasLocal->cpcCaps.screen.capsScreenHeight-1; } // // If all of the previous rectangles have been successfully sent // then try to send this rectangle. // If a previous rectangle failed to be sent then we don't bother // trying to send the rest of the rectangles in the same batch - // they are added back into the SDA so that they will be sent later. // if (fBltOK) { fBltOK = SDGSplitBltToNetwork(&(sdaRect[i]), pcPackets); // // On the first blit failure we must transfer the posible // secondary transmit bounds over to the save area for next // time because down below we are going to add back any unsent // transmit rectangles to the primary SD area. // // Dont worry if this was a secondary transmit because these // bounds will be zero and will be overwritten when we copy // the current SDA region to the secondary area for our next // pass // if (!fBltOK && !secondaryTransmit) { TRACE_OUT(("Send failed so getting new secondary bounds")); BA_CopyBounds(m_asdgLossyRect, &m_sdgcLossy, TRUE); } } if (!fBltOK) { // // The blt to network failed - probably because a network // packet could not be allocated. // We add the rectangle back into the SDA so that we will try // to retransmit the area later. // TRACE_OUT(("Blt failed - add back rect {%d, %d, %d, %d}", sdaRect[i].left, sdaRect[i].top, sdaRect[i].right, sdaRect[i].bottom )); // // Add the rectangle into the bounds. // BA_AddRect(&(sdaRect[i])); backPressure = TRUE; } } // // If all went fine and we were sending primary transmit data then we // should just go back and send secondary data, using the bounds which // are located in the SD area at the moment. We can do this by copying // these secondary bounds to the save area, where they will be used at // the next schedule pass. It is a good idea to yield, because we may // be about to accumulate some more primary data. // if (fBltOK || secondaryTransmit) { TRACE_OUT(("Done with the primary bounds so getting pseudo-primary to secondary")); BA_CopyBounds(m_asdgLossyRect, &m_sdgcLossy, TRUE); } DC_EXIT_POINT: *pBackPressure = backPressure; DebugExitVOID(ASHost::SDG_SendScreenDataArea); } // // SDGSplitBltToNetwork(..) // // Sends the specified rectangle over the network. // // The Screen Data rects that we can send over the network are limited // to certain sizes (determined by the sizes of the Transfer Bitmaps). // If necessary, this function splits the rect into smaller sub-rectangles // for transmission. // // PARAMETERS: // // pRect - pointer to the rectangle to send. // // RETURNS: // // TRUE - rectangle was successfully sent. Supplied rectangle is updated // to be NULL. // // FALSE - rectangle was not completely sent. Supplied rectangle is // updated to contain the area that was NOT sent. // // BOOL ASHost::SDGSplitBltToNetwork(LPRECT pRect, UINT * pcPackets) { RECT smallRect; UINT maxHeight; BOOL rc; DebugEntry(ASHost::SDGSplitBltToNetwork); // // Loop processing strips. // while (pRect->top <= pRect->bottom) { smallRect = *pRect; // // Split the given rectangles into multiple smaller rects if // necessary. If it is wider than our 256 byte work bitmap then // switch to the mega 1024 byte one first. // // Note that the calculations don't work for VGA... maxHeight = max(8, m_usrSendingBPP); if ((smallRect.right-smallRect.left+1) > MEGA_X_SIZE) { maxHeight = MaxBitmapHeight(MEGA_WIDE_X_SIZE, maxHeight); } else { maxHeight = MaxBitmapHeight(MEGA_X_SIZE, maxHeight); } if ((unsigned)(smallRect.bottom - smallRect.top + 1) > maxHeight) { // // Split the rectangle to bring the height within the correct // range. // TRACE_OUT(( "Split Y size(%d) by maxHeight(%d)", smallRect.bottom - smallRect.top, maxHeight)); smallRect.bottom = smallRect.top + maxHeight - 1; } while ((pRect->right - smallRect.left + 1) > 0) { if (!SDGSmallBltToNetwork(&smallRect)) { TRACE_OUT(( "Small blt failed")); rc = FALSE; DC_QUIT; } else { ++(*pcPackets); } } // // Move to the next strip. // pRect->top = smallRect.bottom+1; } rc = TRUE; DC_EXIT_POINT: if (!rc) { // // A small blt failed. If we return FALSE then the supplied // rectangle will be added back into the SDA bounds so that it will // be retransmitted later. // // However, we want to avoid the situation where we have sent the // top left-hand corner of a rectangle and then add the remainder // back into the SDA bounds, because this could cause the original // bounding rectangle to be regenerated (because the bounds are // stored in a fixed number of rectangles). // // Therefore if we are not on the last strip of the rectangle then // we keep the current strip as a "pending" rectangle. The // supplied rectangle is adjusted to remove the whole of this // strip. Next time this function is called the pending rectangle // will be sent before anything else. // // If we are on the last strip (which will be the normal case - // there will usually only be one strip) then we update the // supplied rectangle to be the area that has not been sent and // return FALSE to indicate that it must be added back into the // SDA. // if (m_sdgRectIsPending) { ERROR_OUT(( "Unexpected small blt failure with pending rect")); } else { if (smallRect.bottom == pRect->bottom) { // // This is the last strip. Adjust the supplied rect to // contain the area that has not been sent. // pRect->top = smallRect.top; pRect->left = smallRect.left; } else { // // This is not the last strip Copy the remainder of the // current strip into the pending rect. // smallRect.right = pRect->right; m_sdgPendingRect = smallRect; m_sdgRectIsPending = TRUE; // // Adjust the supplied rectangle to contain the remaining // area that we have not sent and is not covered by the // pending rect. // pRect->top = smallRect.bottom+1; } } } DebugExitBOOL(ASHost::SDGSplitBltToNetwork, rc); return(rc); } // // FUNCTION: SDGSmallBltToNetwork // // DESCRIPTION: // // Sends the screen data within the specified rectangle across the network. // // PARAMETERS: // // pRect - pointer to the rectangle (in screen coords) to send as Screen // Data. // // RETURNS: // // TRUE - screen data successfully sent // // FALSE - screen data could not be sent. Caller should retry later. // // // // BOGUS BUGBUG LAURABU! // This function affects the results on the screen! If drawing happens // between the time we realize the palette then unrealize it, it will look // messed up. You can easily see this in Visio 4.5 when it is in the // foreground (in the background, NM controls the colors so no effect). // BOOL ASHost::SDGSmallBltToNetwork(LPRECT pRect) { BOOL fLossy = FALSE; HDC hdcDesktop; HBITMAP hBitmap = NULL; HBITMAP hOldBitmap = NULL; UINT width; UINT height; UINT fixedWidth; PSDPACKET pSDPacket = NULL; BITMAPINFO_ours bitmapInfo; UINT sizeBitmapPkt; UINT sizeBitmap; HPALETTE hpalOldDIB = NULL; HPALETTE hpalOldDesktop = NULL; HPALETTE hpalLocal; BOOL fPacketSent = FALSE; RECT smallRect; int useWidth; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(ASHost::SDGSmallBltToNetwork); hdcDesktop = GetDC(NULL); if (!hdcDesktop) { DC_QUIT; } width = pRect->right - pRect->left + 1; height = pRect->bottom - pRect->top + 1; // // Determine the width of the work buffer and the width that we // will be sending this time // fixedWidth = ((width + 15) / 16) * 16; useWidth = width; if (fixedWidth > MAX_X_SIZE) { if (fixedWidth > MEGA_X_SIZE) { fixedWidth = MEGA_WIDE_X_SIZE; if (width > MEGA_WIDE_X_SIZE) { useWidth = fixedWidth; } } else { fixedWidth = MEGA_X_SIZE; if (width > MEGA_X_SIZE) { useWidth = fixedWidth; } } } switch (fixedWidth) { case 16: hBitmap = m_pShare->m_usrBmp16; break; case 32: hBitmap = m_pShare->m_usrBmp32; break; case 48: hBitmap = m_pShare->m_usrBmp48; break; case 64: hBitmap = m_pShare->m_usrBmp64; break; case 80: hBitmap = m_pShare->m_usrBmp80; break; case 96: hBitmap = m_pShare->m_usrBmp96; break; case 112: hBitmap = m_pShare->m_usrBmp112; break; case 128: hBitmap = m_pShare->m_usrBmp128; break; case 256: hBitmap = m_pShare->m_usrBmp256; break; case 1024: hBitmap = m_pShare->m_usrBmp1024; break; default: ERROR_OUT(( "Invalid bitmap size(%d)", fixedWidth)); break; } // // Initialise the BITMAPINFO_ours local structure header contents. // This structure will be used in the GetDIBits calls but only the // header part of the structure will be sent across the network, the // color table is sent via the Palette Manager. // m_pShare->USR_InitDIBitmapHeader((BITMAPINFOHEADER *)&bitmapInfo, m_usrSendingBPP); bitmapInfo.bmiHeader.biWidth = fixedWidth; bitmapInfo.bmiHeader.biHeight = height; // // Calculate the size of the bitmap packet in BYTES. // sizeBitmap = BYTES_IN_BITMAP(fixedWidth, height, bitmapInfo.bmiHeader.biBitCount); sizeBitmapPkt = sizeof(SDPACKET) + sizeBitmap - 1; ASSERT(sizeBitmapPkt <= TSHR_MAX_SEND_PKT); // // Allocate a packet for the bitmap data. // // *** NB. This assumes that this code is called ONLY when there *** // *** no unacknowledged bitmaps packets floating around the *** // *** network layer. This means, at the moment, if this code is *** // *** called due to anything other than a WM_TIMER *** // *** message we're in trouble. *** // // pSDPacket = (PSDPACKET)m_pShare->SC_AllocPkt(PROT_STR_UPDATES, g_s20BroadcastID, sizeBitmapPkt); if (!pSDPacket) { // // Failed to allocate the bitmap packet - clear up and exit adding // the rectangle back into the bounds. // TRACE_OUT(("Failed to alloc SDG packet, size %u", sizeBitmapPkt)); DC_QUIT; } // // Since we are bltting off the screen, which by definition is using // the system palette, we place the system palette in both DC's (so // that the bitblt we are about to do will not do any color // conversion). // // // This will determine if the palette changed since the last time we // sent one to the remote. // if (m_usrSendingBPP <= 8) { hpalLocal = PM_GetLocalPalette(); } hOldBitmap = SelectBitmap(m_usrWorkDC, hBitmap); if (m_usrSendingBPP <= 8) { hpalOldDIB = SelectPalette(m_usrWorkDC, hpalLocal, FALSE); RealizePalette(m_usrWorkDC); } // // We can now do a bitblt from the screen (hpDesktop) to memory and the // bits are untranslated. // // We then do a GetDIBits using the local palette which returns us the // bits at the correct bits per pel, (and with properly translated // colors) in order to transmit the data. // BitBlt(m_usrWorkDC, 0, 0, useWidth, height, hdcDesktop, pRect->left, pRect->top, SRCCOPY); // // Zero any unused space on the right side to aid compression. // if (width < fixedWidth) { PatBlt(m_usrWorkDC, width, 0, fixedWidth - width, height, BLACKNESS); } // // Do a GetDIBits into our global stash of memory for now. We will try // and compress this data into our packet after. // GetDIBits(m_usrWorkDC, hBitmap, 0, height, m_pShare->m_usrPBitmapBuffer, (PBITMAPINFO)&bitmapInfo, DIB_RGB_COLORS); // // Deselect the bitmap // SelectBitmap(m_usrWorkDC, hOldBitmap); // // Get the color table directly from the system since we can't trust // any palette realization color stuff via the messages at this stage. // We only need to do this on an 8bpp host sending 8bpp data. // if ((g_usrScreenBPP == 8) && (m_usrSendingBPP == 8)) { PM_GetSystemPaletteEntries(bitmapInfo.bmiColors); } if (m_usrSendingBPP <= 8) { // // Whack old palettes back. // SelectPalette(m_usrWorkDC, hpalOldDIB, FALSE); } // // Fill in packet contents and send it. // pSDPacket->header.header.data.dataType = DT_UP; pSDPacket->header.updateType = UPD_SCREEN_DATA; // // Send Virtual desktop coordinates. // pSDPacket->position.left = (TSHR_INT16)(pRect->left); pSDPacket->position.right = (TSHR_INT16)(pRect->left + useWidth - 1); pSDPacket->position.top = (TSHR_INT16)(pRect->top); pSDPacket->position.bottom = (TSHR_INT16)(pRect->bottom); pSDPacket->realWidth = (TSHR_UINT16)fixedWidth; pSDPacket->realHeight = (TSHR_UINT16)height; pSDPacket->format = (TSHR_UINT16)m_usrSendingBPP; pSDPacket->compressed = FALSE; // // Compress the bitmap data // if (m_pShare->BC_CompressBitmap(m_pShare->m_usrPBitmapBuffer, pSDPacket->data, &sizeBitmap, fixedWidth, height, bitmapInfo.bmiHeader.biBitCount, &fLossy) ) { // // We have successfully compressed the bitmap data into our packet // data buffer. // pSDPacket->compressed = TRUE; // // Write the updated size of the data into the header. // pSDPacket->dataSize = (TSHR_UINT16)sizeBitmap; // // Now update the size of the total data (including header) // sizeBitmapPkt = sizeof(SDPACKET) + sizeBitmap - 1; pSDPacket->header.header.dataLength = sizeBitmapPkt - sizeof(S20DATAPACKET) + sizeof(DATAPACKETHEADER); } else { // // The compression failed so just copy the uncompressed data from // the global buffer to the packet and send it uncompressed. // TRACE_OUT(("Failed to compress bitmap of size %d cx(%d) cy(%d) bpp(%d)", sizeBitmap, fixedWidth, height, bitmapInfo.bmiHeader.biBitCount)); memcpy(pSDPacket->data, m_pShare->m_usrPBitmapBuffer, sizeBitmap ); // // Write the size of the data into the header. // pSDPacket->dataSize = (TSHR_UINT16)sizeBitmap; } TRACE_OUT(("Sending %d bytes of screen data", sizeBitmap)); if (m_pShare->m_scfViewSelf) m_pShare->UP_ReceivedPacket(m_pShare->m_pasLocal, &(pSDPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_UPDATES, g_s20BroadcastID, &(pSDPacket->header.header), sizeBitmapPkt); TRACE_OUT(("SDG packet size: %08d, sent: %08d", sizeBitmapPkt, sentSize)); // // We have sent the packet. // fPacketSent = TRUE; // // If it was lossy then we must accumulate the area for resend. We // accumulate it into the current SDA because we know this was cleared // before the transmission started. We will then move the accumulated // non-lossy rectangles to a save area before we return. // if (fLossy) { // // Convert the rect back into Virtual Desktop coords. // smallRect = *pRect; smallRect.right = smallRect.left + useWidth - 1; WARNING_OUT(( "Lossy send so add non-lossy area (%d,%d)(%d,%d)", smallRect.left, smallRect.top, smallRect.right, smallRect.bottom )); // // Add the rectangle into the bounds. // BA_AddRect(&(smallRect)); } // // Now we modify the supplied rectangle to exclude the area just sent // pRect->left = pRect->left + useWidth; TRACE_OUT(("Rect now {%d, %d, %d, %d}", pRect->left, pRect->top, pRect->right, pRect->bottom )); DC_EXIT_POINT: if (hdcDesktop != NULL) { ReleaseDC(NULL, hdcDesktop); } DebugExitBOOL(ASHost::SDGSmallBltToNetwork, fPacketSent); return(fPacketSent); }