|
|
#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); }
|