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.
2018 lines
48 KiB
2018 lines
48 KiB
#include "precomp.h"
|
|
|
|
|
|
//
|
|
// DCS.CPP
|
|
// Sharing main (init/term plus communication to/from ASMaster)
|
|
//
|
|
// Copyright(c) Microsoft 1997-
|
|
//
|
|
|
|
#define MLZ_FILE_ZONE ZONE_CORE
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// DCS_Init()
|
|
//
|
|
BOOL DCS_Init(void)
|
|
{
|
|
WNDCLASS wc;
|
|
BOOL rc = FALSE;
|
|
HDC hdc;
|
|
|
|
DebugEntry(DCS_Init);
|
|
|
|
if (g_asOptions & AS_SERVICE)
|
|
{
|
|
WARNING_OUT(("AS is running as SERVICE"));
|
|
}
|
|
|
|
//
|
|
// Register with the DC-Groupware Utility Services
|
|
//
|
|
if (!UT_InitTask(UTTASK_DCS, &g_putAS))
|
|
{
|
|
ERROR_OUT(( "Failed to init DCS task"));
|
|
DC_QUIT;
|
|
}
|
|
UT_RegisterEvent(g_putAS, S20_UTEventProc, NULL, UT_PRIORITY_APPSHARING);
|
|
|
|
|
|
//
|
|
// Create the window
|
|
//
|
|
|
|
//
|
|
// Register the main window class.
|
|
//
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = DCSMainWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = g_asInstance;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = DCS_MAIN_WINDOW_CLASS;
|
|
|
|
if (!RegisterClass(&wc))
|
|
{
|
|
ERROR_OUT(("DCS_Init: couldn't register main window class"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Create the main window.
|
|
//
|
|
// We make the window topmost so that it is sent the WM_QUERYENDSESSION
|
|
// message before any other (non-topmost) windows. This lets us
|
|
// prevent the session from closing down if we are still in a share.
|
|
//
|
|
g_asMainWindow = CreateWindowEx(
|
|
WS_EX_TOPMOST, // Make the window topmost
|
|
DCS_MAIN_WINDOW_CLASS, // See RegisterClass() call.
|
|
NULL, // Text for window title bar.
|
|
0, // Invisible.
|
|
0, // Default horizontal position.
|
|
0, // Default vertical position.
|
|
200, // Default width.
|
|
100, // Default height.
|
|
NULL, // Overlapped windows have no parent.
|
|
NULL, // Use the window class menu.
|
|
g_asInstance,
|
|
NULL // Pointer not needed.
|
|
);
|
|
|
|
if (!g_asMainWindow)
|
|
{
|
|
ERROR_OUT(("DCS_Init: couldn't create main window"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Add a global atom for identifying hosted windows with.
|
|
//
|
|
g_asHostProp = GlobalAddAtom(HET_ATOM_NAME);
|
|
if (!g_asHostProp)
|
|
{
|
|
ERROR_OUT(("Failed to add global atom for hosting property"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that display driver is loaded (if it isn't we can't host)
|
|
//
|
|
hdc = GetDC(NULL);
|
|
g_usrScreenBPP = GetDeviceCaps(hdc, BITSPIXEL) *
|
|
GetDeviceCaps(hdc, PLANES);
|
|
g_usrPalettized = ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0);
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
g_usrCaptureBPP = g_usrScreenBPP;
|
|
|
|
ASSERT(!g_asCanHost);
|
|
ASSERT(!g_osiInitialized);
|
|
ASSERT(!g_asSharedMemory);
|
|
ASSERT(!g_poaData[0]);
|
|
ASSERT(!g_poaData[1]);
|
|
ASSERT(!g_lpimSharedData);
|
|
ASSERT(!g_sbcEnabled);
|
|
ASSERT(!g_asbcBitMasks[0]);
|
|
ASSERT(!g_asbcBitMasks[1]);
|
|
ASSERT(!g_asbcBitMasks[2]);
|
|
|
|
OSI_Init();
|
|
|
|
|
|
//
|
|
// If we can't get hold of a pointer to shared IM vars, we are hosed.
|
|
//
|
|
if (!g_lpimSharedData)
|
|
{
|
|
ERROR_OUT(("Failed to get shared IM data"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
ASSERT(g_lpimSharedData->cbSize == sizeof(IM_SHARED_DATA));
|
|
|
|
if (g_asOptions & AS_UNATTENDED)
|
|
{
|
|
// Let the input pieces (Win9x or NT) know we're in unattended mode
|
|
g_lpimSharedData->imUnattended = TRUE;
|
|
}
|
|
|
|
//
|
|
// Scheduler
|
|
//
|
|
if (!SCH_Init())
|
|
{
|
|
ERROR_OUT(("SCH Init failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Hosting
|
|
//
|
|
if (!HET_Init())
|
|
{
|
|
ERROR_OUT(("HET Init failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Viewing
|
|
//
|
|
if (!VIEW_Init())
|
|
{
|
|
ERROR_OUT(("VIEW Init failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// T.120 & T.128 Net
|
|
//
|
|
|
|
//
|
|
// Initialize the network layer last of all. This prevents us from
|
|
// getting requests before we've fully initialized our components.
|
|
//
|
|
if (!S20_Init())
|
|
{
|
|
ERROR_OUT(("S20 Init failed"));
|
|
DC_QUIT;
|
|
|
|
}
|
|
if (!SC_Init())
|
|
{
|
|
ERROR_OUT(("SC Init failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We are now initialized. Post a deferred message to get fonts.
|
|
//
|
|
PostMessage(g_asMainWindow, DCS_FINISH_INIT_MSG, 0, 0);
|
|
|
|
// All modules have successfully initialised. Return success.
|
|
// We are now ready to participate in sharing.
|
|
//
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(DCS_Init, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
//
|
|
// DCS_Term()
|
|
//
|
|
void DCS_Term(void)
|
|
{
|
|
DebugEntry(DCS_Term);
|
|
|
|
//
|
|
// Kill window. Do this FIRST so that any attempts to send us requests
|
|
// or notifications will fail.
|
|
//
|
|
if (g_asMainWindow)
|
|
{
|
|
DestroyWindow(g_asMainWindow);
|
|
g_asMainWindow = NULL;
|
|
}
|
|
|
|
UnregisterClass(DCS_MAIN_WINDOW_CLASS, g_asInstance);
|
|
|
|
|
|
//
|
|
// Network layer - terminate this early because it will handle
|
|
// termination in a call by generating approriate events.
|
|
//
|
|
S20_Term();
|
|
SC_Term();
|
|
|
|
//
|
|
// Scheduler.
|
|
//
|
|
SCH_Term();
|
|
|
|
//
|
|
// Viewing
|
|
//
|
|
VIEW_Term();
|
|
|
|
//
|
|
// Hosting
|
|
//
|
|
HET_Term();
|
|
|
|
//
|
|
// Fonts
|
|
//
|
|
FH_Term();
|
|
|
|
//
|
|
// Terminate OSI
|
|
//
|
|
OSI_Term();
|
|
|
|
//
|
|
// Free our atom.
|
|
//
|
|
if (g_asHostProp)
|
|
{
|
|
GlobalDeleteAtom(g_asHostProp);
|
|
g_asHostProp = 0;
|
|
}
|
|
|
|
//
|
|
// Deregister from the Groupware Utility Services
|
|
//
|
|
if (g_putAS)
|
|
{
|
|
UT_TermTask(&g_putAS);
|
|
}
|
|
|
|
DebugExitVOID(DCS_Term);
|
|
}
|
|
|
|
|
|
//
|
|
// DCS_FinishInit()
|
|
//
|
|
// This does slow font enumeration, and then tries to join a call if one
|
|
// has started up. Even if font enum fails, we can share/view shared, we
|
|
// just won't send text orders
|
|
//
|
|
void DCS_FinishInit(void)
|
|
{
|
|
DebugEntry(DCS_FinishInit);
|
|
|
|
//
|
|
// Determine what fonts we have locally.
|
|
// Done after the r11 caps field is filled in, since if we dont support
|
|
// some of the r11 caps, then we can reduce the amount of work we do
|
|
// when we get the font metrics etc.
|
|
//
|
|
g_cpcLocalCaps.orders.capsNumFonts = (TSHR_UINT16)FH_Init();
|
|
|
|
DebugExitVOID(DCS_FinishInit);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: DCS_PartyJoiningShare
|
|
//
|
|
BOOL ASShare::DCS_PartyJoiningShare(ASPerson * pasPerson)
|
|
{
|
|
BOOL rc = FALSE;
|
|
UINT iDict;
|
|
|
|
DebugEntry(ASShare::DCS_PartyJoiningShare);
|
|
|
|
ValidatePerson(pasPerson);
|
|
|
|
//
|
|
// Allocate dictionaries for GDC Persistent dictionary compression if
|
|
// this person supports it. We'll use them to decompress data
|
|
// received from this person. NOTE: Win95 2.0 does not support
|
|
// persistent pkzip.
|
|
//
|
|
if (pasPerson->cpcCaps.general.genCompressionType & GCT_PERSIST_PKZIP)
|
|
{
|
|
//
|
|
// Allocate persistent dictionaries (outgoing if us, incoming if
|
|
// others).
|
|
//
|
|
TRACE_OUT(( "Allocating receive dictionary set for [%d]", pasPerson->mcsID));
|
|
|
|
pasPerson->adcsDict = new GDC_DICTIONARY[GDC_DICT_COUNT];
|
|
if (!pasPerson->adcsDict)
|
|
{
|
|
ERROR_OUT(("Failed to allocate persistent dictionaries for [%d]", pasPerson->mcsID));
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Initialize cbUsed to zero
|
|
//
|
|
for (iDict = 0; iDict < GDC_DICT_COUNT; iDict++)
|
|
{
|
|
pasPerson->adcsDict[iDict].cbUsed = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(ASShare::DCS_PartyJoiningShare, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: DCS_PartyLeftShare
|
|
//
|
|
void ASShare::DCS_PartyLeftShare(ASPerson * pasPerson)
|
|
{
|
|
DebugEntry(ASShare::DCS_PartyLeftShare);
|
|
|
|
ValidatePerson(pasPerson);
|
|
|
|
//
|
|
// Free any dictionaries we allocated
|
|
//
|
|
if (pasPerson->adcsDict)
|
|
{
|
|
delete[] pasPerson->adcsDict;
|
|
pasPerson->adcsDict = NULL;
|
|
}
|
|
|
|
DebugExitVOID(ASShare::DCS_PartyLeftShare);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_RecalcCaps()
|
|
//
|
|
// Called when someone joins or leaves share.
|
|
//
|
|
void ASShare::DCS_RecalcCaps(BOOL fJoiner)
|
|
{
|
|
ASPerson * pasT;
|
|
|
|
DebugEntry(ASShare::DCS_RecalcCaps);
|
|
|
|
//
|
|
// The combined compression support is initialised to the local support
|
|
//
|
|
ValidatePerson(m_pasLocal);
|
|
m_dcsCompressionSupport = m_pasLocal->cpcCaps.general.genCompressionType;
|
|
m_dcsCompressionLevel = m_pasLocal->cpcCaps.general.genCompressionLevel;
|
|
|
|
//
|
|
// Loop through the remotes
|
|
//
|
|
for (pasT = m_pasLocal->pasNext; pasT != NULL; pasT = pasT->pasNext)
|
|
{
|
|
ValidatePerson(pasT);
|
|
|
|
m_dcsCompressionSupport &=
|
|
pasT->cpcCaps.general.genCompressionType;
|
|
|
|
m_dcsCompressionLevel = min(m_dcsCompressionLevel,
|
|
pasT->cpcCaps.general.genCompressionLevel);
|
|
}
|
|
|
|
TRACE_OUT(("DCS Combined compression level %u, support %#x",
|
|
m_dcsCompressionLevel,
|
|
m_dcsCompressionSupport));
|
|
|
|
DebugExitVOID(ASShare::DCS_RecalcCaps);
|
|
}
|
|
|
|
|
|
//
|
|
// SC_Periodic()
|
|
//
|
|
// The Scheduler runs a separate thread which is responsible for posting
|
|
// messages to our main thread, for which SC_Periodic() is the handler.
|
|
// Posted messages have the highest priority in GetMessage(), above input,
|
|
// paints, and timers.
|
|
//
|
|
// The Scheduler is in one of three states:
|
|
// asleep, normal or turbo. When it is asleep, this function is not
|
|
// called. When it is in normal mode, this function is called at least
|
|
// once, but the scheduler is a lazy guy, so will fall asleep again unless
|
|
// you keep prodding him. In turbo mode this function is called repeatedly
|
|
// and rapidly, but only for a relatively short time, after which the
|
|
// scheduler falls back into normal mode, and from there falls asleep.
|
|
//
|
|
void ASShare::SC_Periodic(void)
|
|
{
|
|
UINT currentTime;
|
|
|
|
DebugEntry(ASShare::SC_Periodic);
|
|
|
|
//
|
|
// We must get the time accurately.
|
|
//
|
|
currentTime = GetTickCount();
|
|
|
|
//
|
|
// Dont do a lot of work if this is an immediate reschedule due to
|
|
// multiple queued entries. Most processors will achieve this in
|
|
// less than 5 mS.
|
|
//
|
|
if ((currentTime - m_dcsLastScheduleTime) < 5)
|
|
{
|
|
WARNING_OUT(("Quit early"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
m_dcsLastScheduleTime = currentTime;
|
|
|
|
//
|
|
// Call the input manager event playback function frequently so that
|
|
// we keep the input queue empty. (Note that we do not want to just
|
|
// dump the input queue into USER because we would lose all the
|
|
// repeat keystroke packets we have so carefully sent across)
|
|
// To trigger input we just use a 0 personid and NULL packet.
|
|
//
|
|
if ((currentTime - m_dcsLastIMTime) > DCS_IM_PERIOD)
|
|
{
|
|
m_dcsLastIMTime = currentTime;
|
|
IM_ReceivedPacket(NULL, NULL);
|
|
}
|
|
|
|
//
|
|
// There are calls which are made periodically but don't have any
|
|
// dependencies. First call the ones we want to be called fairly
|
|
// frequently.
|
|
//
|
|
if ((currentTime - m_dcsLastFastMiscTime) > DCS_FAST_MISC_PERIOD )
|
|
{
|
|
m_dcsLastFastMiscTime = currentTime;
|
|
|
|
OE_Periodic();
|
|
HET_Periodic();
|
|
CA_Periodic();
|
|
IM_Periodic();
|
|
}
|
|
|
|
//
|
|
// Only send updates if we're hosting, and have managed to tell everyone
|
|
// we're hosting.
|
|
//
|
|
if (m_pHost && !m_hetRetrySendState)
|
|
{
|
|
UINT swlRc = 0;
|
|
BOOL fetchedBounds = FALSE;
|
|
|
|
m_pHost->CA_Periodic();
|
|
|
|
//
|
|
// See if we need to swap the buffers over. Only swap if we have
|
|
// sent all the data from the current orders.
|
|
//
|
|
if (m_pHost->OA_GetFirstListOrder() == NULL)
|
|
{
|
|
//
|
|
// Get the current bounds from the driver. This will fill in
|
|
// the share core's copy of the bounds.
|
|
//
|
|
m_pHost->BA_FetchBounds();
|
|
fetchedBounds = TRUE;
|
|
|
|
//
|
|
// Set up the new order list buffer
|
|
//
|
|
m_pHost->OA_ResetOrderList();
|
|
|
|
//
|
|
// Bounds data should be reset to a usable state by SDG once it
|
|
// has finished with them, so we just need to swap the buffers
|
|
// at this point.
|
|
//
|
|
SHM_SwitchReadBuffer();
|
|
}
|
|
|
|
//
|
|
// In this high frequency code path we only send SWP info if it
|
|
// is flagged as needed by the CBT hooks or if SWL determines a
|
|
// send is required. Only SWL knows if a send is required so
|
|
// pass the CBT indication into SWL and let it do the
|
|
// determination.
|
|
//
|
|
// The SWL window scan performs preemptable operations and we
|
|
// must detect the occurrence of preemption otherwise we find
|
|
// ourselves sending updates against an invalid window
|
|
// structure. Therefore we query OA and BA to see if any
|
|
// updates have been accumulated in the interim. We can tight
|
|
// loop trying to get a good SWL list because we really don't
|
|
// want to yield at this point - it is just that we cannot
|
|
// prevent it sometimes. (Sweeping through menus is a good way
|
|
// to exercise this code.)
|
|
//
|
|
|
|
//
|
|
// Synchronize the fast path data
|
|
//
|
|
SHM_SwitchFastBuffer();
|
|
|
|
swlRc = m_pHost->SWL_Periodic();
|
|
if (swlRc != SWL_RC_ERROR)
|
|
{
|
|
//
|
|
// Only send this stuff if we were able to send the window list
|
|
// packet.
|
|
//
|
|
m_pHost->AWC_Periodic();
|
|
|
|
//
|
|
// We've sent a window list and the current active window, now
|
|
// send drawing updates.
|
|
//
|
|
m_pHost->UP_Periodic(currentTime);
|
|
|
|
//
|
|
// See if the cursor has changed image or position
|
|
//
|
|
m_pHost->CM_Periodic();
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("SWL_Periodic waiting for visibility count"));
|
|
}
|
|
|
|
//
|
|
// If we got the bounds from the driver, we have to let the driver know
|
|
// how much of the bounds remain to be sent.
|
|
//
|
|
if (fetchedBounds)
|
|
{
|
|
m_pHost->BA_ReturnBounds();
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
SCH_ContinueScheduling(SCH_MODE_NORMAL);
|
|
|
|
DebugExitVOID(ASShare::SC_Periodic);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_CompressAndSendPacket()
|
|
//
|
|
#ifdef _DEBUG
|
|
UINT ASShare::DCS_CompressAndSendPacket
|
|
#else
|
|
void ASShare::DCS_CompressAndSendPacket
|
|
#endif // _DEBUG
|
|
(
|
|
UINT streamID,
|
|
UINT_PTR nodeID,
|
|
PS20DATAPACKET pPacket,
|
|
UINT packetLength
|
|
)
|
|
{
|
|
UINT cbSrcDataSize;
|
|
UINT cbDstDataSize;
|
|
UINT compression;
|
|
BOOL compressed;
|
|
UINT dictionary;
|
|
|
|
DebugEntry(ASShare::DCS_CompressAndSendPacket);
|
|
|
|
ASSERT(streamID >= SC_STREAM_LOW);
|
|
ASSERT(streamID <= SC_STREAM_HIGH);
|
|
|
|
ASSERT(!m_ascSynced[streamID-1]);
|
|
ASSERT(!m_scfInSync);
|
|
|
|
ASSERT(packetLength < TSHR_MAX_SEND_PKT);
|
|
|
|
//
|
|
// Decide which (if any) compression algorithm we are going to use to
|
|
// try and compress this packet.
|
|
//
|
|
compression = 0;
|
|
cbSrcDataSize = packetLength - sizeof(S20DATAPACKET);
|
|
|
|
//
|
|
// Is the data a compressable size?
|
|
//
|
|
if ((cbSrcDataSize >= DCS_MIN_COMPRESSABLE_PACKET) &&
|
|
(!m_dcsLargePacketCompressionOnly ||
|
|
(cbSrcDataSize >= DCS_MIN_FAST_COMPRESSABLE_PACKET)))
|
|
{
|
|
|
|
//
|
|
// If all nodes have genCompressionLevel 1 or above and all nodes
|
|
// support PERSIST_PKZIP we will use PERSIST_PKZIP (if we are
|
|
// ready).
|
|
//
|
|
// Otherwise, if all nodes support PKZIP and the packet is larger
|
|
// than a predefined minimum size we will use PKZIP.
|
|
//
|
|
// Otherwise, we don't compress
|
|
//
|
|
if ((m_dcsCompressionLevel >= 1) &&
|
|
(m_dcsCompressionSupport & GCT_PERSIST_PKZIP) &&
|
|
(cbSrcDataSize <= DCS_MAX_PDC_COMPRESSABLE_PACKET))
|
|
{
|
|
//
|
|
// Use PERSIST_PKZIP compression
|
|
//
|
|
compression = GCT_PERSIST_PKZIP;
|
|
}
|
|
else if (m_dcsCompressionSupport & GCT_PKZIP)
|
|
{
|
|
//
|
|
// Use PKZIP compression
|
|
//
|
|
compression = GCT_PKZIP;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Compress the packet
|
|
//
|
|
compressed = FALSE;
|
|
if (compression != 0)
|
|
{
|
|
PGDC_DICTIONARY pgdcSrc = NULL;
|
|
|
|
//
|
|
// We compress only the data and not the header of course
|
|
//
|
|
cbDstDataSize = cbSrcDataSize;
|
|
|
|
ASSERT(m_ascTmpBuffer != NULL);
|
|
|
|
//
|
|
// Compress the data following the packet header.
|
|
//
|
|
if (compression == GCT_PERSIST_PKZIP)
|
|
{
|
|
//
|
|
// Figure out what dictionary to use for the stream priority
|
|
//
|
|
switch (streamID)
|
|
{
|
|
case PROT_STR_UPDATES:
|
|
dictionary = GDC_DICT_UPDATES;
|
|
break;
|
|
|
|
case PROT_STR_MISC:
|
|
dictionary = GDC_DICT_MISC;
|
|
break;
|
|
|
|
case PROT_STR_INPUT:
|
|
dictionary = GDC_DICT_INPUT;
|
|
break;
|
|
}
|
|
|
|
pgdcSrc = &m_pasLocal->adcsDict[dictionary];
|
|
}
|
|
|
|
compressed = GDC_Compress(pgdcSrc, GDCCO_MAXCOMPRESSION,
|
|
m_agdcWorkBuf, (LPBYTE)(pPacket + 1),
|
|
cbSrcDataSize, m_ascTmpBuffer, &cbDstDataSize);
|
|
|
|
if (compressed)
|
|
{
|
|
//
|
|
// The data was successfully compressed, copy it back
|
|
//
|
|
ASSERT(cbDstDataSize <= cbSrcDataSize);
|
|
memcpy((pPacket+1), m_ascTmpBuffer, cbDstDataSize);
|
|
|
|
//
|
|
// The data length include the data header
|
|
//
|
|
pPacket->dataLength = (TSHR_UINT16)(cbDstDataSize + sizeof(DATAPACKETHEADER));
|
|
pPacket->data.compressedLength = pPacket->dataLength;
|
|
|
|
packetLength = cbDstDataSize + sizeof(S20DATAPACKET);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the packet header.
|
|
//
|
|
if (!compressed)
|
|
{
|
|
pPacket->data.compressionType = 0;
|
|
}
|
|
else
|
|
{
|
|
if (m_dcsCompressionLevel >= 1)
|
|
{
|
|
pPacket->data.compressionType = (BYTE)compression;
|
|
}
|
|
else
|
|
{
|
|
pPacket->data.compressionType = CT_OLD_COMPRESSED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send the packet.
|
|
//
|
|
S20_SendDataPkt(streamID, nodeID, pPacket);
|
|
|
|
#ifdef _DEBUG
|
|
DebugExitDWORD(ASShare::DCS_CompressAndSendPacket, packetLength);
|
|
return(packetLength);
|
|
#else
|
|
DebugExitVOID(ASShare::DCS_CompressAndSendPacket);
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
|
|
//
|
|
// DCS_FlowControl()
|
|
//
|
|
// This is called back from our flow control code. The parameter passed
|
|
// is the new bytes/second rate that data is flowing at. We turn small
|
|
// packet compression off when the rate is large, it means we're on a
|
|
// fast link so there's no need to bog down the CPU compressing small
|
|
// packets.
|
|
//
|
|
void ASShare::DCS_FlowControl
|
|
(
|
|
UINT DataBytesPerSecond
|
|
)
|
|
{
|
|
DebugEntry(ASShare::DCS_FlowControl);
|
|
|
|
if (DataBytesPerSecond < DCS_FAST_THRESHOLD)
|
|
{
|
|
//
|
|
// Throughput is slow
|
|
//
|
|
if (m_dcsLargePacketCompressionOnly)
|
|
{
|
|
m_dcsLargePacketCompressionOnly = FALSE;
|
|
TRACE_OUT(("DCS_FlowControl: SLOW; compress small packets"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Throughput is fast
|
|
//
|
|
if (!m_dcsLargePacketCompressionOnly)
|
|
{
|
|
m_dcsLargePacketCompressionOnly = TRUE;
|
|
TRACE_OUT(("DCS_FlowControl: FAST; don't compress small packets"));
|
|
}
|
|
}
|
|
|
|
DebugExitVOID(ASShare::DCS_FlowControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_SyncOutgoing() - see dcs.h
|
|
//
|
|
void ASShare::DCS_SyncOutgoing(void)
|
|
{
|
|
DebugEntry(ASShare::DCS_SyncOutgoing);
|
|
|
|
//
|
|
// Reset the send compression dictionaries
|
|
//
|
|
if (m_pasLocal->cpcCaps.general.genCompressionType & GCT_PERSIST_PKZIP)
|
|
{
|
|
UINT i;
|
|
|
|
ASSERT(m_pasLocal->adcsDict);
|
|
|
|
for (i = 0; i < GDC_DICT_COUNT; i++)
|
|
{
|
|
//
|
|
// Somebody has joined or left. We need to start over
|
|
// and wipe out any saved data.
|
|
//
|
|
m_pasLocal->adcsDict[i].cbUsed = 0;
|
|
}
|
|
}
|
|
|
|
DebugExitVOID(ASShare::DCS_SyncOutgoing);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// DCS_NotifyUI()
|
|
//
|
|
void DCS_NotifyUI
|
|
(
|
|
UINT eventID,
|
|
UINT parm1,
|
|
UINT parm2
|
|
)
|
|
{
|
|
DebugEntry(DCS_NotifyUI);
|
|
|
|
//
|
|
// Post event to Front End
|
|
//
|
|
UT_PostEvent(g_putAS, g_putUI, 0, eventID, parm1, parm2);
|
|
|
|
DebugExitVOID(DCS_NotifyUI);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCSLocalDesktopSizeChanged
|
|
//
|
|
// Routine called whenever the desktop size changes.
|
|
//
|
|
// Updates local desktop size stored in capabilities and informs all other
|
|
// machine in a share of the new size
|
|
//
|
|
void DCSLocalDesktopSizeChanged(UINT width, UINT height)
|
|
{
|
|
DebugEntry(DCSLocalDesktopSizeChanged);
|
|
|
|
//
|
|
// Check that the desktop has actually changed size
|
|
//
|
|
if ((g_cpcLocalCaps.screen.capsScreenHeight == height) &&
|
|
(g_cpcLocalCaps.screen.capsScreenWidth == width))
|
|
{
|
|
TRACE_OUT(( "Desktop size has not changed!"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the desktop size
|
|
//
|
|
g_cpcLocalCaps.screen.capsScreenWidth = (TSHR_UINT16)width;
|
|
g_cpcLocalCaps.screen.capsScreenHeight = (TSHR_UINT16)height;
|
|
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->CPC_UpdatedCaps((PPROTCAPS)&g_cpcLocalCaps.screen);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(DCSLocalDesktopSizeChanged);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Main window message procedure.
|
|
//
|
|
LRESULT CALLBACK DCSMainWndProc
|
|
(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
LRESULT rc = 0;
|
|
|
|
DebugEntry(DCSMainWndProc);
|
|
|
|
switch (message)
|
|
{
|
|
case DCS_FINISH_INIT_MSG:
|
|
{
|
|
DCS_FinishInit();
|
|
break;
|
|
}
|
|
|
|
case DCS_PERIODIC_SCHEDULE_MSG:
|
|
{
|
|
if (g_asSession.pShare)
|
|
{
|
|
//
|
|
// Call our periodic processing function if there's at least
|
|
// another person in the share with us.
|
|
//
|
|
g_asSession.pShare->ValidatePerson(g_asSession.pShare->m_pasLocal);
|
|
|
|
//
|
|
// NOTE:
|
|
// If we add record/playback capabilities, get rid of this
|
|
// or change the check. This prevents us from allocating,
|
|
// composing, and sending packets to nowhere when we are
|
|
// the only person in the share.
|
|
//
|
|
if (g_asSession.pShare->m_pasLocal->pasNext || g_asSession.pShare->m_scfViewSelf)
|
|
{
|
|
g_asSession.pShare->SC_Periodic();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Notify the Scheduler that we have processed the scheduling
|
|
// message, which signals that another one can be sent (only
|
|
// one is outstanding at a time).
|
|
//
|
|
SCH_SchedulingMessageProcessed();
|
|
}
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
{
|
|
//
|
|
// The wParam specifies whether the session is about to end.
|
|
//
|
|
if (wParam && !(g_asOptions & AS_SERVICE))
|
|
{
|
|
//
|
|
// Windows is about to terminate (abruptly!). Call our
|
|
// termination functions now - before Windows shuts down
|
|
// the hardware device drivers.
|
|
//
|
|
// We don't leave this job to the WEP because by the time
|
|
// it gets called the hardware device drivers have been
|
|
// shut down and some of the calls we make then fail (e.g.
|
|
// timeEndPeriod requires TIMER.DRV).
|
|
//
|
|
DCS_Term();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
{
|
|
ERROR_OUT(("DCS window received WM_CLOSE, this should never happen"));
|
|
}
|
|
break;
|
|
|
|
case WM_PALETTECHANGED:
|
|
case WM_PALETTEISCHANGING:
|
|
{
|
|
//
|
|
// Win95 patches the Palette DDIs which are more accurate,
|
|
// so only key off this message for NT.
|
|
//
|
|
if (!g_asWin95 && g_asSharedMemory)
|
|
{
|
|
g_asSharedMemory->pmPaletteChanged = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
{
|
|
//
|
|
// The desktop size is changing - we are passed the new size.
|
|
//
|
|
DCSLocalDesktopSizeChanged(LOWORD(lParam),
|
|
HIWORD(lParam));
|
|
}
|
|
break;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
case WM_USERCHANGED:
|
|
if (g_asSession.pShare && g_asSession.pShare->m_pHost)
|
|
{
|
|
WARNING_OUT(("AS: Reset effects on %s", (message == WM_SETTINGCHANGE)
|
|
? "SETTINGCHANGE" : "USERCHANGE"));
|
|
HET_SetGUIEffects(FALSE, &g_asSession.pShare->m_pHost->m_hetEffects);
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Private app sharing messages
|
|
//
|
|
case DCS_KILLSHARE_MSG:
|
|
SC_EndShare();
|
|
break;
|
|
|
|
case DCS_SHARE_MSG:
|
|
DCS_Share((HWND)lParam, (IAS_SHARE_TYPE)wParam);
|
|
break;
|
|
|
|
case DCS_UNSHARE_MSG:
|
|
DCS_Unshare((HWND)lParam);
|
|
break;
|
|
|
|
case DCS_ALLOWCONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->CA_AllowControl((BOOL)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_TAKECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_TakeControl((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_CANCELTAKECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_CancelTakeControl((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_RELEASECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_ReleaseControl((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_PASSCONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_PassControl((UINT)wParam, (UINT)lParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_GIVECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_GiveControl((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_CANCELGIVECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_CancelGiveControl((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_REVOKECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_RevokeControl((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_PAUSECONTROL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->DCS_PauseControl((UINT)lParam, (BOOL)wParam != 0);
|
|
}
|
|
break;
|
|
|
|
case DCS_NEWTOPLEVEL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->HET_HandleNewTopLevel((BOOL)wParam);
|
|
}
|
|
break;
|
|
|
|
case DCS_RECOUNTTOPLEVEL_MSG:
|
|
if (g_asSession.pShare)
|
|
{
|
|
g_asSession.pShare->HET_HandleRecountTopLevel((UINT)wParam);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rc = DefWindowProc(hwnd, message, wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
DebugExitDWORD(DCSMainWndProc, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
//
|
|
// DCS_Share()
|
|
//
|
|
void DCS_Share(HWND hwnd, IAS_SHARE_TYPE uType)
|
|
{
|
|
DWORD dwAppID = 0;
|
|
|
|
DebugEntry(DCS_Share);
|
|
|
|
if (!g_asSession.pShare)
|
|
{
|
|
//
|
|
// Create one.
|
|
//
|
|
if (!SC_CreateShare(S20_CREATE))
|
|
{
|
|
WARNING_OUT(("Failing share request; in wrong state"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
ASSERT(g_asSession.pShare);
|
|
|
|
//
|
|
// Figure out what to do.
|
|
//
|
|
if (hwnd == ::GetDesktopWindow())
|
|
{
|
|
g_asSession.pShare->HET_ShareDesktop();
|
|
}
|
|
else
|
|
{
|
|
DWORD dwThreadID;
|
|
DWORD dwProcessID;
|
|
|
|
dwThreadID = GetWindowThreadProcessId(hwnd, &dwProcessID);
|
|
if (!dwThreadID)
|
|
{
|
|
WARNING_OUT(("Failing share request, window %08lx is invalid", hwnd));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If caller didn't specify exactly what they want, figure it out
|
|
//
|
|
if (uType == IAS_SHARE_DEFAULT)
|
|
{
|
|
if (OSI_IsWOWWindow(hwnd))
|
|
uType = IAS_SHARE_BYTHREAD;
|
|
else
|
|
uType = IAS_SHARE_BYPROCESS;
|
|
}
|
|
|
|
if (uType == IAS_SHARE_BYPROCESS)
|
|
dwAppID = dwProcessID;
|
|
else if (uType == IAS_SHARE_BYTHREAD)
|
|
dwAppID = dwThreadID;
|
|
else if (uType == IAS_SHARE_BYWINDOW)
|
|
dwAppID = HandleToUlong(hwnd);
|
|
|
|
if (IsIconic(hwnd))
|
|
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
|
|
|
|
g_asSession.pShare->HET_ShareApp(uType, dwAppID);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(DCS_Share);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_Unshare()
|
|
//
|
|
void DCS_Unshare(HWND hwnd)
|
|
{
|
|
DebugEntry(DCS_Unshare);
|
|
|
|
if (!g_asSession.pShare || !g_asSession.pShare->m_pHost)
|
|
{
|
|
WARNING_OUT(("Failing unshare, nothing is shared by us"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if ((hwnd == HWND_BROADCAST) || (hwnd == ::GetDesktopWindow()))
|
|
{
|
|
// Unshare everything.
|
|
g_asSession.pShare->HET_UnshareAll();
|
|
}
|
|
else
|
|
{
|
|
DWORD idProcess;
|
|
DWORD idThread;
|
|
DWORD dwAppID;
|
|
UINT hostType;
|
|
|
|
hostType = (UINT)HET_GetHosting(hwnd);
|
|
if (!hostType)
|
|
{
|
|
WARNING_OUT(("Window %08lx is not shared", hwnd));
|
|
DC_QUIT;
|
|
}
|
|
|
|
idThread = GetWindowThreadProcessId(hwnd, &idProcess);
|
|
if (!idThread)
|
|
{
|
|
WARNING_OUT(("Window %08lx is gone", hwnd));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (hostType & HET_HOSTED_BYPROCESS)
|
|
{
|
|
hostType = IAS_SHARE_BYPROCESS;
|
|
dwAppID = idProcess;
|
|
}
|
|
else if (hostType & HET_HOSTED_BYTHREAD)
|
|
{
|
|
hostType = IAS_SHARE_BYTHREAD;
|
|
dwAppID = idThread;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(hostType & HET_HOSTED_BYWINDOW);
|
|
hostType = IAS_SHARE_BYWINDOW;
|
|
dwAppID = HandleToUlong(hwnd);
|
|
}
|
|
|
|
g_asSession.pShare->HET_UnshareApp(hostType, dwAppID);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(DCS_Unshare);
|
|
}
|
|
|
|
|
|
//
|
|
// DCSGetPerson()
|
|
//
|
|
// Validates GCC ID passed in, returns non-null ASPerson * if all is cool.
|
|
//
|
|
ASPerson * ASShare::DCSGetPerson(UINT gccID, BOOL fNull)
|
|
{
|
|
ASPerson * pasPerson = NULL;
|
|
|
|
//
|
|
// Special value?
|
|
//
|
|
if (!gccID)
|
|
{
|
|
if (fNull)
|
|
{
|
|
pasPerson = m_pasLocal->m_caInControlOf;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pasPerson = SC_PersonFromGccID(gccID);
|
|
}
|
|
|
|
if (!pasPerson)
|
|
{
|
|
WARNING_OUT(("Person [%d] not in share", gccID));
|
|
}
|
|
else if (pasPerson == m_pasLocal)
|
|
{
|
|
ERROR_OUT(("Local person [%d] was passed in", gccID));
|
|
pasPerson = NULL;
|
|
}
|
|
|
|
return(pasPerson);
|
|
}
|
|
|
|
//
|
|
// DCS_TakeControl()
|
|
//
|
|
void ASShare::DCS_TakeControl(UINT gccOf)
|
|
{
|
|
ASPerson * pasHost;
|
|
|
|
DebugEntry(ASShare::DCS_TakeControl);
|
|
|
|
pasHost = DCSGetPerson(gccOf, FALSE);
|
|
if (!pasHost)
|
|
{
|
|
WARNING_OUT(("DCS_TakeControl: ignoring, host [%d] not valid", gccOf));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_TakeControl(pasHost);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_TakeControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_CancelTakeControl()
|
|
//
|
|
void ASShare::DCS_CancelTakeControl(UINT gccOf)
|
|
{
|
|
ASPerson * pasHost;
|
|
|
|
DebugEntry(ASShare::DCS_CancelTakeControl);
|
|
|
|
if (!gccOf)
|
|
{
|
|
pasHost = m_caWaitingForReplyFrom;
|
|
}
|
|
else
|
|
{
|
|
pasHost = DCSGetPerson(gccOf, FALSE);
|
|
}
|
|
|
|
if (!pasHost)
|
|
{
|
|
WARNING_OUT(("DCS_CancelTakeControl: Ignoring, host [%d] not valid", gccOf));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_CancelTakeControl(pasHost, TRUE);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_CancelTakeControl);
|
|
}
|
|
|
|
|
|
//
|
|
// DCS_ReleaseControl()
|
|
//
|
|
void ASShare::DCS_ReleaseControl(UINT gccOf)
|
|
{
|
|
ASPerson * pasHost;
|
|
|
|
DebugEntry(ASShare::DCS_ReleaseControl);
|
|
|
|
//
|
|
// Validate host
|
|
//
|
|
pasHost = DCSGetPerson(gccOf, TRUE);
|
|
if (!pasHost)
|
|
{
|
|
WARNING_OUT(("DCS_ReleaseControl: ignoring, host [%d] not valid", gccOf));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_ReleaseControl(pasHost, TRUE);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_ReleaseControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_PassControl()
|
|
//
|
|
void ASShare::DCS_PassControl(UINT gccOf, UINT gccTo)
|
|
{
|
|
ASPerson * pasHost;
|
|
ASPerson * pasControllerNew;
|
|
|
|
DebugEntry(ASShare::DCS_PassControl);
|
|
|
|
//
|
|
// Validate host
|
|
//
|
|
pasHost = DCSGetPerson(gccOf, TRUE);
|
|
if (!pasHost)
|
|
{
|
|
WARNING_OUT(("DCS_PassControl: ignoring, host [%d] not valid", gccTo));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate new controller
|
|
//
|
|
pasControllerNew = DCSGetPerson(gccTo, FALSE);
|
|
if (!pasControllerNew)
|
|
{
|
|
WARNING_OUT(("DCS_PassControl: ignoring, viewer [%d] not valid", gccTo));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (pasControllerNew == pasHost)
|
|
{
|
|
ERROR_OUT(("DCS_PassControl: ignoring, pass of == pass to [%d]", pasControllerNew->mcsID));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_PassControl(pasHost, pasControllerNew);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_PassControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_GiveControl()
|
|
//
|
|
void ASShare::DCS_GiveControl(UINT gccTo)
|
|
{
|
|
ASPerson * pasViewer;
|
|
|
|
DebugEntry(ASShare::DCS_GiveControl);
|
|
|
|
//
|
|
// Validate viewer
|
|
//
|
|
pasViewer = DCSGetPerson(gccTo, FALSE);
|
|
if (!pasViewer)
|
|
{
|
|
WARNING_OUT(("DCS_GiveControl: ignoring, viewer [%d] not valid", gccTo));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_GiveControl(pasViewer);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_GiveControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_CancelGiveControl()
|
|
//
|
|
void ASShare::DCS_CancelGiveControl(UINT gccTo)
|
|
{
|
|
ASPerson * pasTo;
|
|
|
|
DebugEntry(ASShare::DCS_CancelGiveControl);
|
|
|
|
if (!gccTo)
|
|
{
|
|
pasTo = m_caWaitingForReplyFrom;
|
|
}
|
|
else
|
|
{
|
|
pasTo = DCSGetPerson(gccTo, FALSE);
|
|
}
|
|
|
|
if (!pasTo)
|
|
{
|
|
WARNING_OUT(("DCS_CancelGiveControl: Ignoring, person [%d] not valid", gccTo));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_CancelGiveControl(pasTo, TRUE);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_CancelGiveControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// DCS_RevokeControl()
|
|
//
|
|
void ASShare::DCS_RevokeControl(UINT gccController)
|
|
{
|
|
ASPerson * pasController;
|
|
|
|
DebugEntry(ASShare::DCS_RevokeControl);
|
|
|
|
if (!gccController)
|
|
{
|
|
// Special value: match whomever is controlling us
|
|
pasController = m_pasLocal->m_caControlledBy;
|
|
}
|
|
else
|
|
{
|
|
pasController = DCSGetPerson(gccController, FALSE);
|
|
}
|
|
|
|
if (!pasController)
|
|
{
|
|
WARNING_OUT(("DCS_RevokeControl: ignoring, controller [%d] not valid", gccController));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_RevokeControl(pasController, TRUE);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_RevokeControl);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// DCS_PauseControl()
|
|
//
|
|
void ASShare::DCS_PauseControl(UINT gccOf, BOOL fPause)
|
|
{
|
|
ASPerson * pasControlledBy;
|
|
|
|
DebugEntry(ASShare::DCS_PauseControl);
|
|
|
|
if (!gccOf)
|
|
{
|
|
pasControlledBy = m_pasLocal->m_caControlledBy;
|
|
}
|
|
else
|
|
{
|
|
pasControlledBy = DCSGetPerson(gccOf, FALSE);
|
|
}
|
|
|
|
if (!pasControlledBy)
|
|
{
|
|
WARNING_OUT(("DCS_PauseControl: ignoring, controller [%d] not valid", gccOf));
|
|
DC_QUIT;
|
|
}
|
|
|
|
CA_PauseControl(pasControlledBy, fPause, TRUE);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(ASShare::DCS_PauseControl);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_LaunchHostUI()
|
|
//
|
|
// Posts a message to start or activate the host UI.
|
|
//
|
|
HRESULT SHP_LaunchHostUI(void)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_LaunchHostUI);
|
|
|
|
if (g_asSession.hwndHostUI &&
|
|
PostMessage(g_asSession.hwndHostUI, HOST_MSG_OPEN, 0, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_LaunchHostUI, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// SHP_Share
|
|
//
|
|
BOOL SHP_Share
|
|
(
|
|
HWND hwnd,
|
|
IAS_SHARE_TYPE uType
|
|
)
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
DebugEntry(SHP_ShareApp);
|
|
|
|
if (g_asSession.hwndHostUI)
|
|
{
|
|
rc = PostMessage(g_asMainWindow, DCS_SHARE_MSG, uType, (LPARAM)hwnd);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("SHP_Share: not able to share"));
|
|
}
|
|
|
|
DebugExitBOOL(SHP_ShareApp, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_Unshare()
|
|
//
|
|
// For unsharing, we use a window. The window has all the information
|
|
// we need to stop sharing already set in its host prop.
|
|
//
|
|
HRESULT SHP_Unshare(HWND hwnd)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_Unshare);
|
|
|
|
if (g_asSession.hwndHostUI)
|
|
{
|
|
if (PostMessage(g_asMainWindow, DCS_UNSHARE_MSG, 0, (LPARAM)hwnd))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("SHP_Unshare: not able to share"));
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_Unshare, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_TakeControl()
|
|
// Request to take control of a remote host.
|
|
// PersonOf is the GCC id of the remote.
|
|
//
|
|
HRESULT SHP_TakeControl(IAS_GCC_ID PersonOf)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_TakeControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_TAKECONTROL_MSG, PersonOf, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_TakeControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_CancelTakeControl()
|
|
// Cancel request to take control of a remote host.
|
|
// PersonOf is the GCC id of the remote.
|
|
//
|
|
HRESULT SHP_CancelTakeControl(IAS_GCC_ID PersonOf)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_CancelTakeControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_CANCELTAKECONTROL_MSG, PersonOf, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_CancelTakeControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_ReleaseControl()
|
|
// Release control of a remote host.
|
|
// PersonOf is the GCC id of the remote we are currently controlling
|
|
// and wish to stop. Zero means "whomever" we are in control of
|
|
// at the time.
|
|
//
|
|
HRESULT SHP_ReleaseControl(IAS_GCC_ID PersonOf)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_ReleaseControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_RELEASECONTROL_MSG, PersonOf, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_ReleaseControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_PassControl()
|
|
// Pass control of a remote to another prerson.
|
|
// PersonOf is the GCC id of the remote we are currently controlling
|
|
// PersonTo is the GCC id of the remote we wish to pass control to
|
|
//
|
|
HRESULT SHP_PassControl(IAS_GCC_ID PersonOf, IAS_GCC_ID PersonTo)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_PassControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_PASSCONTROL_MSG, PersonOf, PersonTo))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_PassControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// SHP_AllowControl()
|
|
// Toggle the ability for remotes to control us (when we are sharing stuff)
|
|
//
|
|
HRESULT SHP_AllowControl(BOOL fAllowed)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_AllowControl);
|
|
|
|
if (!g_asSession.hwndHostUI)
|
|
{
|
|
ERROR_OUT(("SHP_AllowControl failing, can't host"));
|
|
DC_QUIT;
|
|
|
|
}
|
|
|
|
if (g_asPolicies & SHP_POLICY_NOCONTROL)
|
|
{
|
|
ERROR_OUT(("SHP_AllowControl failing. prevented by policy"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, fAllowed, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitHRESULT(SHP_AllowControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_GiveControl()
|
|
//
|
|
// Give control of our shared stuff to a remote.
|
|
//
|
|
HRESULT SHP_GiveControl(IAS_GCC_ID PersonTo)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_GiveControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_GIVECONTROL_MSG, PersonTo, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_GiveControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_CancelGiveControl()
|
|
//
|
|
// Cancel giving control of our shared stuff to a remote.
|
|
//
|
|
HRESULT SHP_CancelGiveControl(IAS_GCC_ID PersonTo)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_CancelGiveControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_CANCELGIVECONTROL_MSG, PersonTo, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_CancelGiveControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// SHP_RevokeControl()
|
|
// Take control away from a remote who is in control of us.
|
|
//
|
|
// NOTE:
|
|
// SHP_AllowControl(FALSE) will of course revoke control if someone is
|
|
// in control of us at the time.
|
|
//
|
|
HRESULT SHP_RevokeControl(IAS_GCC_ID PersonTo)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_RevokeControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, PersonTo, 0))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_RevokeControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// SHP_PauseControl()
|
|
// Pause or unpause control, when we are controlled by a remote
|
|
//
|
|
HRESULT SHP_PauseControl(IAS_GCC_ID PersonControlledBy, BOOL fPause)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DebugEntry(SHP_PauseControl);
|
|
|
|
if (g_asMainWindow &&
|
|
PostMessage(g_asMainWindow, DCS_PAUSECONTROL_MSG, fPause, PersonControlledBy))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DebugExitHRESULT(SHP_PauseControl, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// SHP_GetPersonStatus()
|
|
//
|
|
HRESULT SHP_GetPersonStatus(IAS_GCC_ID Person, IAS_PERSON_STATUS * pStatus)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
UINT cbSize;
|
|
|
|
DebugEntry(SHP_GetPersonStatus);
|
|
|
|
UT_Lock(UTLOCK_AS);
|
|
|
|
if (IsBadWritePtr(pStatus, sizeof(*pStatus)))
|
|
{
|
|
ERROR_OUT(("SHP_GetPersonStatus failing; IAS_PERSON_STATUS pointer is bogus"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that size field is filled in properly
|
|
//
|
|
cbSize = pStatus->cbSize;
|
|
if (cbSize != sizeof(*pStatus))
|
|
{
|
|
ERROR_OUT(("SHP_GetPersonStatus failing; cbSize field not right"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// First, clear the structure
|
|
//
|
|
::ZeroMemory(pStatus, cbSize);
|
|
pStatus->cbSize = cbSize;
|
|
|
|
//
|
|
// Is AS present?
|
|
//
|
|
if (!g_asMainWindow)
|
|
{
|
|
ERROR_OUT(("SHP_GetPersonStatus failing; AS not present"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Are we in a share?
|
|
//
|
|
if (g_asSession.pShare)
|
|
{
|
|
ASPerson * pasT;
|
|
|
|
//
|
|
// Find this person
|
|
//
|
|
if (!Person)
|
|
{
|
|
Person = g_asSession.gccID;
|
|
}
|
|
|
|
for (pasT = g_asSession.pShare->m_pasLocal; pasT != NULL; pasT = pasT->pasNext)
|
|
{
|
|
if (pasT->cpcCaps.share.gccID == Person)
|
|
{
|
|
ASPerson * pTemp;
|
|
|
|
//
|
|
// Found it
|
|
//
|
|
pStatus->InShare = TRUE;
|
|
|
|
switch (pasT->cpcCaps.general.version)
|
|
{
|
|
case CAPS_VERSION_20:
|
|
pStatus->Version = IAS_VERSION_20;
|
|
break;
|
|
|
|
case CAPS_VERSION_30:
|
|
pStatus->Version = IAS_VERSION_30;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Unknown version %d", pasT->cpcCaps.general.version));
|
|
break;
|
|
}
|
|
|
|
if (pasT->hetCount == HET_DESKTOPSHARED)
|
|
pStatus->AreSharing = IAS_SHARING_DESKTOP;
|
|
else if (pasT->hetCount)
|
|
pStatus->AreSharing = IAS_SHARING_APPLICATIONS;
|
|
else
|
|
pStatus->AreSharing = IAS_SHARING_NOTHING;
|
|
|
|
pStatus->Controllable = pasT->m_caAllowControl;
|
|
|
|
//
|
|
// We MUST assign to avoid faults.
|
|
//
|
|
pTemp = pasT->m_caInControlOf;
|
|
if (pTemp)
|
|
{
|
|
pStatus->InControlOf = pTemp->cpcCaps.share.gccID;
|
|
}
|
|
else
|
|
{
|
|
pTemp = pasT->m_caControlledBy;
|
|
if (pTemp)
|
|
{
|
|
pStatus->ControlledBy = pTemp->cpcCaps.share.gccID;
|
|
}
|
|
}
|
|
|
|
pStatus->IsPaused = pasT->m_caControlPaused;
|
|
|
|
//
|
|
// We MUST assign to avoid faults.
|
|
//
|
|
pTemp = g_asSession.pShare->m_caWaitingForReplyFrom;
|
|
if (pTemp)
|
|
{
|
|
if (pasT == g_asSession.pShare->m_pasLocal)
|
|
{
|
|
//
|
|
// We have an outstanding request to this dude.
|
|
//
|
|
switch (g_asSession.pShare->m_caWaitingForReplyMsg)
|
|
{
|
|
case CA_REPLY_REQUEST_TAKECONTROL:
|
|
pStatus->InControlOfPending = pTemp->cpcCaps.share.gccID;
|
|
break;
|
|
|
|
case CA_REPLY_REQUEST_GIVECONTROL:
|
|
pStatus->ControlledByPending = pTemp->cpcCaps.share.gccID;
|
|
break;
|
|
}
|
|
}
|
|
else if (pasT == pTemp)
|
|
{
|
|
//
|
|
// This dude has an outstanding request from us.
|
|
//
|
|
switch (g_asSession.pShare->m_caWaitingForReplyMsg)
|
|
{
|
|
case CA_REPLY_REQUEST_TAKECONTROL:
|
|
pStatus->ControlledByPending = g_asSession.pShare->m_pasLocal->cpcCaps.share.gccID;
|
|
break;
|
|
|
|
case CA_REPLY_REQUEST_GIVECONTROL:
|
|
pStatus->InControlOfPending = g_asSession.pShare->m_pasLocal->cpcCaps.share.gccID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
DC_EXIT_POINT:
|
|
UT_Unlock(UTLOCK_AS);
|
|
DebugExitHRESULT(SHP_GetPersonStatus, hr);
|
|
return(hr);
|
|
}
|
|
|
|
|