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