You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
958 lines
32 KiB
958 lines
32 KiB
#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);
|
|
}
|
|
|