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

2019 lines
46 KiB
C++

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