958 lines
31 KiB
C++
958 lines
31 KiB
C++
#include "precomp.h"
|
||
|
||
|
||
//
|
||
// UP.CPP
|
||
// Update Packager
|
||
//
|
||
// Copyright(c) Microsoft 1997-
|
||
//
|
||
|
||
#define MLZ_FILE_ZONE ZONE_NET
|
||
|
||
|
||
|
||
//
|
||
// UP_FlowControl()
|
||
// Checks if we've switched between slow and fast throughput
|
||
//
|
||
void ASHost::UP_FlowControl(UINT newBufferSize)
|
||
{
|
||
DebugEntry(ASHost::UP_FlowControl);
|
||
|
||
if (newBufferSize > (LARGE_ORDER_PACKET_SIZE / 2))
|
||
{
|
||
if (m_upfUseSmallPackets)
|
||
{
|
||
m_upfUseSmallPackets = FALSE;
|
||
TRACE_OUT(("UP_FlowControl: FAST; use large packets"));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!m_upfUseSmallPackets)
|
||
{
|
||
m_upfUseSmallPackets = TRUE;
|
||
TRACE_OUT(("UP_FlowControl: SLOW; use small packets"));
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASHost::UP_FlowControl);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// UP_Periodic()
|
||
//
|
||
// Called periodically, to send graphical updates as orders and/or screen
|
||
// data.
|
||
//
|
||
void ASHost::UP_Periodic(UINT currentTime)
|
||
{
|
||
BOOL fSendSD = FALSE;
|
||
BOOL fSendOrders = FALSE;
|
||
UINT tmpTime;
|
||
UINT timeSinceOrders;
|
||
UINT timeSinceSD;
|
||
UINT timeSinceTrying;
|
||
|
||
DebugEntry(ASHost::UP_Periodic);
|
||
|
||
//
|
||
// This is a
|
||
// performance critical part of the scheduling so we apply some
|
||
// heuristics to try and keep the overheads down.
|
||
//
|
||
// 1.If there was no back pressure last time then we check the
|
||
// rate of accumulation of screendata over the last period.
|
||
// If it was high then we apply a time slice to the sending
|
||
// of screendata.
|
||
//
|
||
// 2.If the rate of order accumulation was also high then we
|
||
// apply a timeslice to the order accumulation as well, just
|
||
// to avoid too high a CPU overhead trying to send orders
|
||
// when we will eventually fail to keep up. We keep this
|
||
// time period low because the objective is simply to avoid
|
||
// sending hundreds of packets containing few orders each.
|
||
// (On the other hand, we want to send the single textout
|
||
// following a keystoke ASAP so we must not timeslice all the
|
||
// time.)
|
||
//
|
||
// 3.If neither orders nor screendata is piling up quickly then
|
||
// we do a full send immediately.
|
||
//
|
||
// 4.If there was back pressure on the last send then we still
|
||
// send orders, but always on the time slice, independent of
|
||
// the order accumulation rate.
|
||
//
|
||
// Note that we cannot sample the accumulation rates for every
|
||
// pass because the app doing the drawing may be interrupted by
|
||
// us for a few hundred milliseconds. Therefore we only sample
|
||
// the bounds every VOLUME_SAMPLE milliseconds.
|
||
//
|
||
//
|
||
timeSinceSD = currentTime - m_upLastSDTime;
|
||
timeSinceOrders = currentTime - m_upLastOrdersTime;
|
||
timeSinceTrying = currentTime - m_upLastTrialTime;
|
||
|
||
//
|
||
// Sample the accumulation rates.
|
||
//
|
||
m_upSDAccum += BA_QueryAccumulation();
|
||
m_upOrdersAccum += OA_QueryOrderAccum();
|
||
|
||
//
|
||
// Sample the throughput over the last period to see whether we
|
||
// can operate in rapid respose mode or whether we should
|
||
// timeslice.
|
||
//
|
||
if (timeSinceTrying > DCS_VOLUME_SAMPLE)
|
||
{
|
||
//
|
||
// Take the newly accumulated deltas.
|
||
//
|
||
m_upDeltaSD = m_upSDAccum;
|
||
m_upDeltaOrders = m_upOrdersAccum;
|
||
|
||
//
|
||
// Store time of last retrieval.
|
||
//
|
||
m_upLastTrialTime = currentTime;
|
||
|
||
//
|
||
// Reset the running totals.
|
||
//
|
||
m_upSDAccum = 0;
|
||
m_upOrdersAccum = 0;
|
||
}
|
||
|
||
//
|
||
// If we are way out of line then send updates. Not that this
|
||
// will reset the update timer independent of whether the send
|
||
// works or not, so that we don't enter this arm continually
|
||
// when we time out but are in a back pressure situation
|
||
//
|
||
// The long stop timer is there to catch apps that keep a
|
||
// continual flow of orders/SD at above the suppression rate.
|
||
// We want to tune our heuristics to avoid this, but if it
|
||
// happens than we must send the data eventually. The problem
|
||
// is that this objective clashes with the scenario of the user
|
||
// paging down twenty times, where our most efficient approach
|
||
// is to let him run and snapshot the SD at the end, rather
|
||
// than every PERIOD_LONG milliseconds. (A screen snapshot
|
||
// will stop the host for a second!).
|
||
//
|
||
if (timeSinceSD > DCS_SD_UPDATE_LONG_PERIOD)
|
||
{
|
||
fSendSD = TRUE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We only disregard our time slicing if the rate of orders
|
||
// and screendata is low enough to warrant it. If the rate
|
||
// is too high then hold off so that we can do some packet
|
||
// consolidation. If we had no back pressure last time or
|
||
// the screendata rate is now low enough then try sending
|
||
// SD as well as orders.
|
||
//
|
||
// The order threshold is measured in number of orders over
|
||
// the period. Screendata is measured in the total area
|
||
// accumulated (prior to any spoiling).
|
||
//
|
||
if (!m_upBackPressure)
|
||
{
|
||
if (m_upDeltaOrders < DCS_ORDERS_TURNOFF_FREQUENCY)
|
||
{
|
||
fSendOrders = TRUE;
|
||
if (m_upDeltaSD < DCS_BOUNDS_TURNOFF_RATE)
|
||
{
|
||
if ((timeSinceSD < DCS_SD_UPDATE_SHORT_PERIOD) &&
|
||
(m_upDeltaSD > DCS_BOUNDS_IMMEDIATE_RATE))
|
||
{
|
||
fSendSD = FALSE;
|
||
}
|
||
else
|
||
{
|
||
fSendSD = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Even in a back pressure situation we try and send orders
|
||
// periodically to keep current. If we overflow the order
|
||
// buffer then we will constrain the buffer size to prevent
|
||
// sending too many non-productive orders. (But we dont
|
||
// turn orders off because we still want the user to see
|
||
// things happening.) Generally we send orders immediately,
|
||
// provided the rate of accumulation is within the limits.
|
||
// This test is to time slice orders if they are being
|
||
// generated at a high rate. The constant must be
|
||
// reasonably small otherwise we force the order buffer to
|
||
// overflow and order processing will be turned off.
|
||
//
|
||
if (!fSendSD && !fSendOrders)
|
||
{
|
||
if (timeSinceOrders > DCS_ORDER_UPDATE_PERIOD)
|
||
{
|
||
fSendOrders = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now we can go ahead and try sending! First look to see if
|
||
// we can do both screendata and orders
|
||
//
|
||
if (fSendSD)
|
||
{
|
||
//
|
||
// Indicate no back pressure (even if this send is
|
||
// triggered by a timout our initial assumption is no back
|
||
// pressure). Back pressure will be reinstated by
|
||
// SendUpdates if necessary.
|
||
//
|
||
m_upBackPressure = FALSE;
|
||
UPSendUpdates();
|
||
|
||
//
|
||
// Sending screendata can take a long time. It messes up
|
||
// our heuristics unless we adjust for it.
|
||
//
|
||
tmpTime = GetTickCount();
|
||
timeSinceTrying -= (tmpTime - currentTime);
|
||
m_pShare->m_dcsLastScheduleTime = tmpTime;
|
||
m_upLastSDTime = tmpTime;
|
||
m_upLastOrdersTime = tmpTime;
|
||
}
|
||
else
|
||
{
|
||
if (fSendOrders)
|
||
{
|
||
//
|
||
// Either the update rate is too high or we are
|
||
// experiencing back pressure so just send the orders
|
||
// and not the screendata. This is because we want to
|
||
// avoid entering screendata mode as a result of order
|
||
// back pressure for as long as we can. The screendata
|
||
// will come later, when things have settled down a bit
|
||
//
|
||
m_upLastOrdersTime = currentTime;
|
||
m_upBackPressure = TRUE;
|
||
if (!UPSendUpdates())
|
||
{
|
||
//
|
||
// This is the only real action so leave all the
|
||
// tracing separate for cleanliness. If there are
|
||
// orders in transit then everything is fine. If none
|
||
// are sent for a while then we want to break out of
|
||
// our SD back pressure wait. This is because we are
|
||
// only sampling the flow rates every DCS_VOLUME_SAMPLE msecs,
|
||
// but we dont want to have to wait that long to flush the SD.
|
||
// We cannot increase the flow sample rate because then
|
||
// it becomes too erratic because of system scheduling.
|
||
//
|
||
m_upBackPressure = FALSE;
|
||
UPSendUpdates();
|
||
m_upLastSDTime = currentTime;
|
||
}
|
||
}
|
||
}
|
||
|
||
DebugExitVOID(ASHost::UP_Periodic);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// UPSendUpdates()
|
||
// Actually tries to allocate and send orders + screen data. What it does
|
||
// depends on
|
||
// * Presence of back-pressure due to previous send failures
|
||
// * How much screen data & orders there are
|
||
// * Whether we're in serious spoiling mode and can't keep up
|
||
// * What packet size to send
|
||
//
|
||
// Returns:
|
||
// # of packets sent
|
||
//
|
||
UINT ASHost::UPSendUpdates(void)
|
||
{
|
||
BOOL synced;
|
||
BOOL ordersSent;
|
||
UINT numPackets = 0;
|
||
|
||
DebugEntry(ASHost::UPSendUpdates);
|
||
|
||
//
|
||
// If we actually have updates to send then try to send a sync token.
|
||
//
|
||
if ((OA_GetTotalOrderListBytes() > 0) ||
|
||
(m_sdgcLossy != 0) ||
|
||
(m_baNumRects > 0))
|
||
{
|
||
synced = UP_MaybeSendSyncToken();
|
||
|
||
//
|
||
// Only send updates if we have sent the sync token succesfully.
|
||
//
|
||
if (synced)
|
||
{
|
||
//
|
||
// There is no outstanding sync token waiting to be sent, so we
|
||
// can send the orders and screen data updates.
|
||
//
|
||
//
|
||
// Send accumulated orders. If this call fails (probably out
|
||
// of memory) then don't send any other updates - we'll try
|
||
// sending the whole lot later. The orders MUST be sent before
|
||
// the screen data.
|
||
//
|
||
if (PM_MaybeSendPalettePacket())
|
||
{
|
||
ordersSent = UPSendOrders(&numPackets);
|
||
if (!ordersSent)
|
||
{
|
||
m_upBackPressure = TRUE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Orders sent OK so go for the screendata, provided
|
||
// the caller wants us to.
|
||
//
|
||
if (!m_upBackPressure)
|
||
{
|
||
//
|
||
// We may now try and send screen data. However,
|
||
// we need to be careful not to do this too
|
||
// frequently, because DC-Share is now being
|
||
// scheduled to send as soon as network buffers
|
||
// become available. On the other hand, some
|
||
// apps respond to keystrokes with screendata so
|
||
// we cannot just slow it down!
|
||
//
|
||
// The approach is to have SendScreenDataArea
|
||
// return the amount of data sent, together with
|
||
// an indication as to whether we hit back pressure
|
||
//
|
||
// We return these to dcsapi which has control of
|
||
// when we are scheduled and passes the paramaters
|
||
// in again
|
||
//
|
||
//
|
||
TRACE_OUT(( "Sending SD"));
|
||
SDG_SendScreenDataArea(&m_upBackPressure, &numPackets);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// We sent the orders OK an so we must reset
|
||
// the back pressure indicator even though we
|
||
// were asked not to send screendata
|
||
//
|
||
TRACE_OUT(( "Orders sent and BP relieved"));
|
||
m_upBackPressure = FALSE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
m_upBackPressure = FALSE;
|
||
}
|
||
|
||
DebugExitDWORD(ASHost::UPSendUpdates, numPackets);
|
||
return(numPackets);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// UP_MaybeSendSyncToken()
|
||
//
|
||
BOOL ASHost::UP_MaybeSendSyncToken(void)
|
||
{
|
||
PUPSPACKET pUPSPacket;
|
||
#ifdef _DEBUG
|
||
UINT sentSize;
|
||
#endif // _DEBUG
|
||
|
||
DebugEntry(ASHost::UP_MaybeSendSyncToken);
|
||
|
||
//
|
||
// Check to see if we should send a sync token.
|
||
//
|
||
if (m_upfSyncTokenRequired)
|
||
{
|
||
//
|
||
// The sync packet consists of an updates packets as far as the end
|
||
// of the header.
|
||
//
|
||
pUPSPacket = (PUPSPACKET)m_pShare->SC_AllocPkt(PROT_STR_UPDATES,
|
||
g_s20BroadcastID, sizeof(UPSPACKET));
|
||
if (!pUPSPacket)
|
||
{
|
||
//
|
||
// We will try again later.
|
||
//
|
||
TRACE_OUT(("Failed to alloc UP sync packet"));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Fill in the packet contents.
|
||
//
|
||
pUPSPacket->header.header.data.dataType = DT_UP;
|
||
pUPSPacket->header.updateType = UPD_SYNC;
|
||
|
||
//
|
||
// Now send the packet to the remote application.
|
||
//
|
||
if (m_pShare->m_scfViewSelf)
|
||
m_pShare->UP_ReceivedPacket(m_pShare->m_pasLocal,
|
||
&(pUPSPacket->header.header));
|
||
|
||
#ifdef _DEBUG
|
||
sentSize =
|
||
#endif // _DEBUG
|
||
m_pShare->DCS_CompressAndSendPacket(PROT_STR_UPDATES,
|
||
g_s20BroadcastID, &(pUPSPacket->header.header),
|
||
sizeof(*pUPSPacket));
|
||
|
||
TRACE_OUT(("UP SYNC packet size: %08d, sent %08d",
|
||
sizeof(*pUPSPacket), sentSize));
|
||
|
||
//
|
||
// The sync packet was successfully sent.
|
||
//
|
||
m_upfSyncTokenRequired = FALSE;
|
||
}
|
||
}
|
||
|
||
DebugExitBOOL(ASHost::UP_MaybeSendSyncToken, (!m_upfSyncTokenRequired));
|
||
return(!m_upfSyncTokenRequired);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// UPSendOrders(..)
|
||
//
|
||
// Sends all accumulated orders.
|
||
//
|
||
// Returns:
|
||
// TRUE if all orders successfully sent
|
||
// FALSE if send failed (e.g. if unable to allocate network packet)
|
||
//
|
||
//
|
||
BOOL ASHost::UPSendOrders(UINT * pcPackets)
|
||
{
|
||
PORDPACKET pPacket = NULL;
|
||
UINT cbOrderBytes;
|
||
UINT cbOrderBytesRemaining;
|
||
UINT cbPacketSize;
|
||
BOOL rc = TRUE;
|
||
#ifdef _DEBUG
|
||
UINT sentSize;
|
||
#endif // _DEBUG
|
||
|
||
DebugEntry(ASHost::UPSendOrders);
|
||
|
||
//
|
||
// Find out how many bytes of orders there are in the Order List.
|
||
//
|
||
cbOrderBytesRemaining = UPFetchOrdersIntoBuffer(NULL, NULL, NULL);
|
||
|
||
//
|
||
// Process any orders on the list.
|
||
//
|
||
if (cbOrderBytesRemaining > 0)
|
||
{
|
||
TRACE_OUT(( "%u order bytes to fetch", cbOrderBytesRemaining));
|
||
|
||
//
|
||
// Keep sending packets while there are some orders to do.
|
||
//
|
||
while (cbOrderBytesRemaining > 0)
|
||
{
|
||
UINT cbMax;
|
||
//
|
||
|
||
// Make sure the order size does not exceed the max packet
|
||
// size.
|
||
//
|
||
cbMax = (m_upfUseSmallPackets) ? SMALL_ORDER_PACKET_SIZE :
|
||
LARGE_ORDER_PACKET_SIZE;
|
||
|
||
cbPacketSize = min(cbOrderBytesRemaining,
|
||
(cbMax - sizeof(ORDPACKET) + 1));
|
||
|
||
//
|
||
// Allocate a packet to send the data in.
|
||
//
|
||
pPacket = (PORDPACKET)m_pShare->SC_AllocPkt(PROT_STR_UPDATES, g_s20BroadcastID,
|
||
sizeof(ORDPACKET) + cbPacketSize - 1);
|
||
if (!pPacket)
|
||
{
|
||
//
|
||
// Failed to allocate a packet. We skip out immediately -
|
||
// we'll try again later.
|
||
//
|
||
TRACE_OUT(("Failed to alloc UP order packet, size %u",
|
||
sizeof(ORDPACKET) + cbPacketSize - 1));
|
||
rc = FALSE;
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Transfer as many orders into the packet as will fit.
|
||
//
|
||
cbOrderBytes = cbPacketSize;
|
||
cbOrderBytesRemaining = UPFetchOrdersIntoBuffer(
|
||
pPacket->data, &pPacket->cOrders, &cbOrderBytes);
|
||
|
||
TRACE_OUT(( "%u bytes fetched into %u byte pkt. %u remain.",
|
||
cbOrderBytes, cbPacketSize, cbOrderBytesRemaining));
|
||
|
||
//
|
||
// If no order bytes were transferred then try again with a
|
||
// Large Order Packet.
|
||
//
|
||
if (cbOrderBytes == 0)
|
||
{
|
||
//
|
||
// We need to use a larger packet to transfer the
|
||
// orders into. (The first order must be a very large
|
||
// order such as a large bitmap cache update).
|
||
//
|
||
S20_FreeDataPkt(&(pPacket->header.header));
|
||
|
||
//
|
||
// cbOrderBytesRemaining may not accurate if there are
|
||
// any MemBlt orders in the order heap. This is
|
||
// because we may have to insert a color table order
|
||
// and / or a bitmap bits order before the MemBlt.
|
||
//
|
||
// To avoid getting into an infinite loop if there is
|
||
// only a MemBlt remaining but we actually have to send
|
||
// a color table and / or a bitmap bits order
|
||
// (cbOrderBytesRemaining would never get set high
|
||
// enough to allow us to send the color table / bitmap
|
||
// bits order), make the buffer at least large enough
|
||
// to hold the largest amount of data required for all
|
||
// the parts of a MemBlt.
|
||
//
|
||
|
||
//
|
||
// The maximum number of bytes required to send a MemBlt order. This is
|
||
// The size of the largest possible color table order
|
||
// + the size of the largest possible bitmap bits order
|
||
// + the size of the largest MemBlt order.
|
||
//
|
||
cbPacketSize = sizeof(BMC_COLOR_TABLE_ORDER) +
|
||
(256 * sizeof(TSHR_RGBQUAD)) +
|
||
sizeof(BMC_BITMAP_BITS_ORDER_R2) +
|
||
sizeof(MEM3BLT_R2_ORDER) +
|
||
MP_CACHE_CELLSIZE(MP_LARGE_TILE_WIDTH, MP_LARGE_TILE_HEIGHT,
|
||
m_usrSendingBPP);
|
||
cbPacketSize = max(cbPacketSize, cbOrderBytesRemaining);
|
||
|
||
if (cbPacketSize > (UINT)(LARGE_ORDER_PACKET_SIZE -
|
||
sizeof(ORDPACKET) + 1))
|
||
{
|
||
TRACE_OUT(("Too many order bytes for large packet(%d)",
|
||
cbOrderBytesRemaining));
|
||
cbPacketSize = LARGE_ORDER_PACKET_SIZE -
|
||
sizeof(ORDPACKET) + 1;
|
||
}
|
||
|
||
pPacket = (PORDPACKET)m_pShare->SC_AllocPkt(PROT_STR_UPDATES,
|
||
g_s20BroadcastID, sizeof(ORDPACKET) + cbPacketSize - 1);
|
||
if (!pPacket)
|
||
{
|
||
TRACE_OUT(("Failed to alloc UP order packet, size %u",
|
||
sizeof(ORDPACKET) + cbPacketSize - 1));
|
||
rc = FALSE;
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Transfer as many orders into the packet as will
|
||
// fit.
|
||
//
|
||
cbOrderBytes = cbPacketSize;
|
||
cbOrderBytesRemaining = UPFetchOrdersIntoBuffer(
|
||
pPacket->data, &pPacket->cOrders, &cbOrderBytes );
|
||
|
||
//
|
||
// If no orders were transferred then something has
|
||
// gone wrong. Probably flow control kicked in or
|
||
// a dekstop switch occurred.
|
||
// Return failure now!
|
||
// Hopefully things will sort themselves out later
|
||
// or we will resort to sending updates as screen
|
||
// data once the order accumulation heap becomes
|
||
// full.
|
||
//
|
||
if (cbOrderBytes == 0)
|
||
{
|
||
WARNING_OUT(("No orders fetched into %u byte packet, %u bytes left",
|
||
cbPacketSize, cbOrderBytesRemaining));
|
||
S20_FreeDataPkt(&(pPacket->header.header));
|
||
rc = FALSE;
|
||
DC_QUIT;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fill in the packet header.
|
||
//
|
||
pPacket->header.header.data.dataType = DT_UP;
|
||
pPacket->header.updateType = UPD_ORDERS;
|
||
pPacket->sendBPP = (TSHR_UINT16)m_usrSendingBPP;
|
||
|
||
//
|
||
// If encoding is switched on, update the data size to reflect
|
||
// it with encoded orders
|
||
//
|
||
if (m_pShare->m_oefOE2EncodingOn)
|
||
{
|
||
pPacket->header.header.dataLength = sizeof(ORDPACKET) + cbOrderBytes - 1
|
||
- sizeof(S20DATAPACKET) + sizeof(DATAPACKETHEADER);
|
||
}
|
||
|
||
//
|
||
// Now send it.
|
||
//
|
||
if (m_pShare->m_scfViewSelf)
|
||
m_pShare->UP_ReceivedPacket(m_pShare->m_pasLocal,
|
||
&(pPacket->header.header));
|
||
|
||
#ifdef _DEBUG
|
||
sentSize =
|
||
#endif // _DEBUG
|
||
m_pShare->DCS_CompressAndSendPacket(PROT_STR_UPDATES, g_s20BroadcastID,
|
||
&(pPacket->header.header), sizeof(ORDPACKET) + cbOrderBytes - 1);
|
||
|
||
TRACE_OUT(("UP ORDERS packet size: %08d, sent %08d",
|
||
sizeof(ORDPACKET) + cbOrderBytes - 1, sentSize));
|
||
|
||
++(*pcPackets);
|
||
}
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(ASHost::UPSendOrders, rc);
|
||
return(rc);
|
||
}
|
||
|
||
//
|
||
//
|
||
// UPFetchOrdersIntoBuffer(..)
|
||
//
|
||
// Encodes orders from the Order List and copies them into the supplied
|
||
// buffer, then frees up the memory of each order copied.
|
||
//
|
||
// Orders are copied until the buffer is full or there are no more orders.
|
||
//
|
||
// Returns:
|
||
// The number of order bytes that were NOT returned.
|
||
// i.e. 0 if all orders were returned.
|
||
// A simple way to find out the total number of order bytes
|
||
// in the Order List is to call the function with a buffer length
|
||
// of zero.
|
||
//
|
||
// *pcbBufferSize is updated to contain the total number of bytes
|
||
// returned.
|
||
//
|
||
//
|
||
UINT ASHost::UPFetchOrdersIntoBuffer
|
||
(
|
||
LPBYTE pBuffer,
|
||
LPTSHR_UINT16 pcOrders,
|
||
LPUINT pcbBufferSize
|
||
)
|
||
{
|
||
LPINT_ORDER pListOrder;
|
||
LPINT_ORDER pCurrentOrder;
|
||
UINT cbFreeBytesInBuffer;
|
||
UINT cOrdersCopied;
|
||
LPBYTE pDst;
|
||
UINT cbOrderSize;
|
||
UINT ulRemainingOrderBytes;
|
||
BOOL processingMemBlt;
|
||
|
||
DebugEntry(ASHost::UPFetchOrdersIntoBuffer);
|
||
|
||
//
|
||
// Make a quick exit if the Order List length is being queried.
|
||
//
|
||
if ( (pcbBufferSize == NULL) ||
|
||
(*pcbBufferSize == 0) )
|
||
{
|
||
goto fetch_orders_exit;
|
||
}
|
||
|
||
//
|
||
// Initialize the buffer pointer and size.
|
||
//
|
||
pDst = pBuffer;
|
||
cbFreeBytesInBuffer = *pcbBufferSize;
|
||
|
||
//
|
||
// Keep a count of the number of orders we copy.
|
||
//
|
||
cOrdersCopied = 0;
|
||
|
||
//
|
||
// Return as many orders as possible.
|
||
//
|
||
pListOrder = OA_GetFirstListOrder();
|
||
TRACE_OUT(( "First order: 0x%08x", pListOrder));
|
||
while (pListOrder != NULL)
|
||
{
|
||
if (pListOrder->OrderHeader.Common.fOrderFlags & OF_INTERNAL)
|
||
{
|
||
//
|
||
// This is an internal order. Currently SBC is the only
|
||
// component to use internal orders, so get SBC to process it.
|
||
//
|
||
SBC_ProcessInternalOrder(pListOrder);
|
||
|
||
//
|
||
// Internal order must not get sent over the wire, so skip on
|
||
// to the next order
|
||
//
|
||
pListOrder = OA_RemoveListOrder(pListOrder);
|
||
continue;
|
||
}
|
||
|
||
if (ORDER_IS_MEMBLT(pListOrder) || ORDER_IS_MEM3BLT(pListOrder))
|
||
{
|
||
//
|
||
// This is a MEMBLT or a MEM3BLT so we have to do some extra
|
||
// processing... This function returns us a pointer to the
|
||
// next order which should be sent - this will often not be the
|
||
// MEMBLT, but a color table order or a bitmap bits order.
|
||
//
|
||
if (!SBC_ProcessMemBltOrder(pListOrder, &pCurrentOrder))
|
||
{
|
||
//
|
||
// This can fail if
|
||
// * we're low on memory
|
||
// * we changed from 8BPP to 24BPP sending, because
|
||
// somebody left the share, and we have queued up
|
||
// SBC orders that we can no longer process.
|
||
//
|
||
TRACE_OUT(("Failed to process SBC order, fall back to SDG"));
|
||
pListOrder = OA_RemoveListOrder(pListOrder);
|
||
continue;
|
||
}
|
||
|
||
processingMemBlt = TRUE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This isn't a MEMBLT or a MEM3BLT - just set pCurrentOrder to
|
||
// be pListOrder
|
||
//
|
||
pCurrentOrder = pListOrder;
|
||
processingMemBlt = FALSE;
|
||
}
|
||
|
||
if (m_pShare->m_oefOE2EncodingOn)
|
||
{
|
||
//
|
||
// Encoding is switched on.
|
||
// Encode the order into the next free space in the buffer
|
||
//
|
||
cbOrderSize = OE2_EncodeOrder( pCurrentOrder,
|
||
pDst,
|
||
(TSHR_UINT16)cbFreeBytesInBuffer );
|
||
TRACE_OUT(( "Encoded size, %u bytes", cbOrderSize));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Copy the order into the buffer.
|
||
//
|
||
cbOrderSize = COM_ORDER_SIZE(
|
||
((LPCOM_ORDER)(&(pCurrentOrder->OrderHeader.Common))));
|
||
|
||
if (cbOrderSize <= cbFreeBytesInBuffer)
|
||
{
|
||
memcpy(pDst,
|
||
(LPCOM_ORDER)(&(pCurrentOrder->OrderHeader.Common)),
|
||
cbOrderSize);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// No room for this order in this packet.
|
||
//
|
||
cbOrderSize = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check whether the order was copied into the buffer.
|
||
//
|
||
if (cbOrderSize == 0)
|
||
{
|
||
//
|
||
// The order was too big to fit in this buffer.
|
||
// Exit the loop - this order will go in the next packet.
|
||
//
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Update the buffer pointer past the encoded order.
|
||
//
|
||
pDst += cbOrderSize;
|
||
cbFreeBytesInBuffer -= cbOrderSize;
|
||
cOrdersCopied++;
|
||
|
||
if (processingMemBlt)
|
||
{
|
||
//
|
||
// If we are processing a MEMBLT order, we have to notify SBC
|
||
// that we've dealt with it successfully so that it returns us
|
||
// a different order next time.
|
||
//
|
||
SBC_OrderSentNotification(pCurrentOrder);
|
||
}
|
||
|
||
if (pCurrentOrder == pListOrder)
|
||
{
|
||
//
|
||
// We successfully copied the order into the buffer - on to the
|
||
// next one UNLESS we haven't processed the last one we picked
|
||
// out of the order list i.e. pCurrentOrder is not the same as
|
||
// pListOrder. This will happen if we just processed a color
|
||
// table order or a bitmap bits order returned from
|
||
// SBC_ProcessMemBltOrder (if we processed the MEMBLT itself,
|
||
// we can safely move on to the next order).
|
||
//
|
||
pListOrder = OA_RemoveListOrder(pListOrder);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fill in the packet header.
|
||
//
|
||
if (pcOrders != NULL)
|
||
{
|
||
*pcOrders = (TSHR_UINT16)cOrdersCopied;
|
||
}
|
||
|
||
//
|
||
// Update the buffer size to indicate how much data we have
|
||
// written.
|
||
//
|
||
*pcbBufferSize -= cbFreeBytesInBuffer;
|
||
|
||
TRACE_OUT(( "Returned %d orders in %d bytes",
|
||
cOrdersCopied,
|
||
*pcbBufferSize));
|
||
|
||
fetch_orders_exit:
|
||
//
|
||
// Return the number of bytes still to be processed
|
||
//
|
||
ulRemainingOrderBytes = OA_GetTotalOrderListBytes();
|
||
|
||
DebugExitDWORD(ASHost::UPFetchOrdersIntoBuffer, ulRemainingOrderBytes);
|
||
return(ulRemainingOrderBytes);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// UP_ReceivePacket()
|
||
//
|
||
void ASShare::UP_ReceivedPacket
|
||
(
|
||
ASPerson * pasPerson,
|
||
PS20DATAPACKET pPacket
|
||
)
|
||
{
|
||
PUPPACKETHEADER pUPPacket;
|
||
|
||
DebugEntry(ASShare::UP_ReceivedPacket);
|
||
|
||
ValidatePerson(pasPerson);
|
||
|
||
if (!pasPerson->m_pView)
|
||
{
|
||
//
|
||
// Updates for parties which we don't recognise as hosts are just
|
||
// discarded.
|
||
//
|
||
|
||
// NOTE:
|
||
// 2.0 Win95 does not have HET, where we kick off sharing/unsharing.
|
||
// But it did have TT, and the packet type/messages were defined
|
||
// cleverly for HET so that 2.0 Win95 works the same. When they
|
||
// start to share, we get a PT_TT packet with a non-zero count.
|
||
// The difference really is that the number is apps for Win95 2.0
|
||
// and HWNDs for everybody else.
|
||
//
|
||
WARNING_OUT(("UP_ReceivedUpdates: Ignoring updates from person [%d] not hosting",
|
||
pasPerson->mcsID));
|
||
|
||
DC_QUIT;
|
||
}
|
||
|
||
pUPPacket = (PUPPACKETHEADER)pPacket;
|
||
switch (pUPPacket->updateType)
|
||
{
|
||
case UPD_SCREEN_DATA:
|
||
SDP_ReceivedPacket(pasPerson, pPacket);
|
||
break;
|
||
|
||
case UPD_ORDERS:
|
||
OD_ReceivedPacket(pasPerson, pPacket);
|
||
break;
|
||
|
||
case UPD_PALETTE:
|
||
PM_ReceivedPacket(pasPerson, pPacket);
|
||
break;
|
||
|
||
case UPD_SYNC:
|
||
//
|
||
// We need to reset our INCOMING decoding info since the sender
|
||
// resets his OUTGOING encoding info for a sync.
|
||
//
|
||
OD2_SyncIncoming(pasPerson);
|
||
|
||
//
|
||
// NOTE:
|
||
// We do not need to reset INCOMING data for
|
||
// PM -- the host won't send us old palette references
|
||
// RBC -- the host won't send us old bitmap references.
|
||
// Even though it would be nice to delete the existing
|
||
// bitmaps, recreating the cache is a hassle.
|
||
// CM -- the host won't send us old cursor references
|
||
// SSI -- the host won't send us old savebits references
|
||
//
|
||
break;
|
||
|
||
default:
|
||
ERROR_OUT(("Unknown UP packet type %u from [%d]",
|
||
pUPPacket->updateType,
|
||
pasPerson->mcsID));
|
||
break;
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitVOID(ASShare::UP_ReceivedPacket);
|
||
}
|
||
|