windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/sdg.cpp
2020-09-26 16:20:57 +08:00

815 lines
29 KiB
C++

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