#include "precomp.h" // // HOST.CPP // Hosting, local and remote // // Copyright(c) Microsoft 1997- // #define MLZ_FILE_ZONE ZONE_CORE // // HET_Init() // // Initialization for hosting // * window tracking // * capabilities // * host UI // BOOL HET_Init(void) { BOOL rc = FALSE; int property; UINT i; LOGFONT lf; DebugEntry(HET_Init); // // Initialize T.128 capabilities, whether we can host or not. // ZeroMemory(&g_cpcLocalCaps, sizeof(g_cpcLocalCaps)); g_cpcLocalCaps.header.numCapabilities = PROTCAPS_COUNT; // PROTCAPS_GENERAL // Check for compression setting (useful to debug protocol) // You can set CT_PKZIP (1) or none (0) instead of persistent PKZIP, // which is the default. // g_cpcLocalCaps.general.header.capID = CAPS_ID_GENERAL; g_cpcLocalCaps.general.header.capSize = sizeof(g_cpcLocalCaps.general); COM_ReadProfInt(DBG_INI_SECTION_NAME, GDC_INI_COMPRESSION, GCT_DEFAULT, &property); g_cpcLocalCaps.general.genCompressionType = (TSHR_UINT16)property; g_cpcLocalCaps.general.genCompressionLevel = CAPS_GEN_COMPRESSION_LEVEL_1; g_cpcLocalCaps.general.OS = CAPS_WINDOWS; g_cpcLocalCaps.general.OSVersion = (g_asWin95 ? CAPS_WINDOWS_95 : CAPS_WINDOWS_NT); g_cpcLocalCaps.general.typeFlags = 0; if (g_asOptions & AS_SERVICE) { g_cpcLocalCaps.general.typeFlags |= AS_SERVICE; } if (g_asOptions & AS_UNATTENDED) { g_cpcLocalCaps.general.typeFlags |= AS_UNATTENDED; } g_cpcLocalCaps.general.version = CAPS_VERSION_CURRENT; g_cpcLocalCaps.general.supportsDOS6Compression = CAPS_UNSUPPORTED; g_cpcLocalCaps.general.supportsCapsUpdate = CAPS_SUPPORTED; g_cpcLocalCaps.general.supportsRemoteUnshare = CAPS_UNSUPPORTED; // // PROTCAPS_SCREEN // g_cpcLocalCaps.screen.header.capID = CAPS_ID_SCREEN; g_cpcLocalCaps.screen.header.capSize = sizeof(g_cpcLocalCaps.screen); g_cpcLocalCaps.screen.capsSupports1BPP = CAPS_UNSUPPORTED; g_cpcLocalCaps.screen.capsSupports4BPP = CAPS_SUPPORTED; g_cpcLocalCaps.screen.capsSupports8BPP = CAPS_SUPPORTED; g_cpcLocalCaps.screen.capsSupports24BPP = CAPS_SUPPORTED; g_cpcLocalCaps.screen.capsScreenWidth = (TSHR_UINT16)GetSystemMetrics(SM_CXSCREEN); g_cpcLocalCaps.screen.capsScreenHeight = (TSHR_UINT16)GetSystemMetrics(SM_CYSCREEN); g_cpcLocalCaps.screen.capsSupportsDesktopResize = CAPS_SUPPORTED; // // Set up the V1 and/or V2 Bitmap Compression capabilities. For the // V2.0 protocol, both are supported by default (supporting V1 // compression allows for negotiation down to V1 protocol systems), but // can be overidden in the INI file. // g_cpcLocalCaps.screen.capsSupportsV1Compression = CAPS_UNSUPPORTED; g_cpcLocalCaps.screen.capsSupportsV2Compression = CAPS_SUPPORTED; g_cpcLocalCaps.screen.capsBPP = (TSHR_UINT16)g_usrScreenBPP; // PROTCAPS_SC g_cpcLocalCaps.share.header.capID = CAPS_ID_SC; g_cpcLocalCaps.share.header.capSize = sizeof(g_cpcLocalCaps.share); g_cpcLocalCaps.share.gccID = 0; // PROTCAPS_CM g_cpcLocalCaps.cursor.header.capID = CAPS_ID_CM; g_cpcLocalCaps.cursor.header.capSize = sizeof(g_cpcLocalCaps.cursor); g_cpcLocalCaps.cursor.capsSupportsColorCursors = CAPS_SUPPORTED; g_cpcLocalCaps.cursor.capsCursorCacheSize = TSHR_CM_CACHE_ENTRIES; // PROTCAPS_PM g_cpcLocalCaps.palette.header.capID = CAPS_ID_PM; g_cpcLocalCaps.palette.header.capSize = sizeof(g_cpcLocalCaps.palette); g_cpcLocalCaps.palette.capsColorTableCacheSize = TSHR_PM_CACHE_ENTRIES; // // PROTCAPS_BITMAPCACHE // g_cpcLocalCaps.bitmaps.header.capID = CAPS_ID_BITMAPCACHE; g_cpcLocalCaps.bitmaps.header.capSize = sizeof(g_cpcLocalCaps.bitmaps); // // SEND BITMAP CACHE // // The cache is now more in line with what the display driver is doing. // The memory size for medium/large is the same. But large bitmaps are // 4x bigger, so there are 1/4 as many. The # of small bitmaps is the // same as the # of medium bitmaps. Since small bitmaps are 1/4 the // size, only 1/4 as much memory is used. // if (g_sbcEnabled) { UINT maxSendBPP; ASSERT(g_asbcShuntBuffers[SBC_MEDIUM_TILE_INDEX]); ASSERT(g_asbcShuntBuffers[SBC_LARGE_TILE_INDEX]); g_cpcLocalCaps.bitmaps.sender.capsSmallCacheNumEntries = (TSHR_UINT16)g_asbcShuntBuffers[SBC_MEDIUM_TILE_INDEX]->numEntries; g_cpcLocalCaps.bitmaps.sender.capsMediumCacheNumEntries = (TSHR_UINT16)g_asbcShuntBuffers[SBC_MEDIUM_TILE_INDEX]->numEntries; g_cpcLocalCaps.bitmaps.sender.capsLargeCacheNumEntries = (TSHR_UINT16)g_asbcShuntBuffers[SBC_LARGE_TILE_INDEX]->numEntries; if (g_usrScreenBPP >= 24) { maxSendBPP = 24; } else { maxSendBPP = 8; } g_cpcLocalCaps.bitmaps.sender.capsSmallCacheCellSize = MP_CACHE_CELLSIZE(MP_SMALL_TILE_WIDTH, MP_SMALL_TILE_WIDTH, maxSendBPP); g_cpcLocalCaps.bitmaps.sender.capsMediumCacheCellSize = MP_CACHE_CELLSIZE(MP_MEDIUM_TILE_WIDTH, MP_MEDIUM_TILE_HEIGHT, maxSendBPP); g_cpcLocalCaps.bitmaps.sender.capsLargeCacheCellSize = MP_CACHE_CELLSIZE(MP_LARGE_TILE_WIDTH, MP_LARGE_TILE_HEIGHT, maxSendBPP); } else { // // We can't use sizes of zero, 2.x nodes will fail if we do. But // we can use a tiny number so they don't allocate huge hunks of // memory for no reason. And 3.0 will treat '1' like '0'. // g_cpcLocalCaps.bitmaps.sender.capsSmallCacheNumEntries = 1; g_cpcLocalCaps.bitmaps.sender.capsSmallCacheCellSize = 1; g_cpcLocalCaps.bitmaps.sender.capsMediumCacheNumEntries = 1; g_cpcLocalCaps.bitmaps.sender.capsMediumCacheCellSize = 1; g_cpcLocalCaps.bitmaps.sender.capsLargeCacheNumEntries = 1; g_cpcLocalCaps.bitmaps.sender.capsLargeCacheCellSize = 1; } TRACE_OUT(("SBC small cache: %d entries, size %d", g_cpcLocalCaps.bitmaps.sender.capsSmallCacheNumEntries, g_cpcLocalCaps.bitmaps.sender.capsSmallCacheCellSize)); TRACE_OUT(("SBC medium cache: %d entries, size %d", g_cpcLocalCaps.bitmaps.sender.capsMediumCacheNumEntries, g_cpcLocalCaps.bitmaps.sender.capsMediumCacheCellSize)); TRACE_OUT(("SBC large cache: %d entries, size %d", g_cpcLocalCaps.bitmaps.sender.capsLargeCacheNumEntries, g_cpcLocalCaps.bitmaps.sender.capsLargeCacheCellSize)); // // RECEIVE caps are obsolete with 3.0; receivers simply look at the // sender's attributes. So just fill in the MAX possible. 2.x remotes // will take the min of themselves and everybody else's receiver caps. // g_cpcLocalCaps.bitmaps.receiver.capsSmallCacheNumEntries = 0x7FFF; g_cpcLocalCaps.bitmaps.receiver.capsSmallCacheCellSize = 0x7FFF; g_cpcLocalCaps.bitmaps.receiver.capsMediumCacheNumEntries = 0x7FFF; g_cpcLocalCaps.bitmaps.receiver.capsMediumCacheCellSize = 0x7FFF; g_cpcLocalCaps.bitmaps.receiver.capsLargeCacheNumEntries = 0x7FFF; g_cpcLocalCaps.bitmaps.receiver.capsLargeCacheCellSize = 0x7FFF; // // PROTCAPS_ORDERS // g_cpcLocalCaps.orders.header.capID = CAPS_ID_ORDERS; g_cpcLocalCaps.orders.header.capSize = sizeof(g_cpcLocalCaps.orders); // // Fill in the SaveBitmap capabilities. // g_cpcLocalCaps.orders.capsSaveBitmapSize = TSHR_SSI_BITMAP_SIZE; g_cpcLocalCaps.orders.capsSaveBitmapXGranularity = TSHR_SSI_BITMAP_X_GRANULARITY; g_cpcLocalCaps.orders.capsSaveBitmapYGranularity = TSHR_SSI_BITMAP_Y_GRANULARITY; g_cpcLocalCaps.orders.capsSendSaveBitmapSize = g_cpcLocalCaps.orders.capsSaveBitmapSize; g_cpcLocalCaps.orders.capsReceiveSaveBitmapSize = g_cpcLocalCaps.orders.capsSaveBitmapSize; // // We support // * R20 Signatures (cell heights, better matching) // * Aspect matching // * Charset/code page matching // * Baseline text orders // * Em Heights // * DeltaX arrays for simulation if font not on remote // // // BOGUS LAURABU BUGBUG // // Baseline text orders not yet supported in Win95. But that's OK, // we don't mark any orders we generate on that platform with // NF_BASELINE, so they aren't treated as such. // g_cpcLocalCaps.orders.capsfFonts = CAPS_FONT_R20_SIGNATURE | CAPS_FONT_ASPECT | CAPS_FONT_CODEPAGE | CAPS_FONT_ALLOW_BASELINE | CAPS_FONT_EM_HEIGHT | CAPS_FONT_OLD_NEED_X | CAPS_FONT_NEED_X_SOMETIMES; // // Fill in which orders we support. // for (i = 0; i < ORD_NUM_LEVEL_1_ORDERS; i++) { // // Order indices for desktop-scrolling and memblt variants are not // to be negotiated by this mechanism... these currently consume // 3 order indices which must be excluded from this negotiation. // if ( (i == ORD_RESERVED_INDEX ) || (i == ORD_MEMBLT_R2_INDEX ) || (i == ORD_UNUSED_INDEX ) || (i == ORD_MEM3BLT_R2_INDEX) ) { continue; } g_cpcLocalCaps.orders.capsOrders[i] = ORD_LEVEL_1_ORDERS; } g_cpcLocalCaps.orders.capsMaxOrderlevel = ORD_LEVEL_1_ORDERS; // // Fill in encoding capabilities // // // Keep the "encoding disabled" option, it's handy for using our // protocol analyzer // COM_ReadProfInt(DBG_INI_SECTION_NAME, OE2_INI_2NDORDERENCODING, CAPS_ENCODING_DEFAULT, &property); g_cpcLocalCaps.orders.capsEncodingLevel = (TSHR_UINT16)property; g_cpcLocalCaps.orders.capsfSendScroll = FALSE; // // Get the app and desktop icons, big and small // g_hetASIcon = LoadIcon(g_asInstance, MAKEINTRESOURCE(IDI_SHAREICON)); if (!g_hetASIcon) { ERROR_OUT(("HET_Init: Failed to load app icon")); DC_QUIT; } g_hetDeskIcon = LoadIcon(g_asInstance, MAKEINTRESOURCE(IDI_DESKTOPICON)); if (!g_hetDeskIcon) { ERROR_OUT(("HET_Init: failed to load desktop icon")); DC_QUIT; } // Get the small icon, created, that we paint on the window bar items g_hetASIconSmall = (HICON)LoadImage(g_asInstance, MAKEINTRESOURCE(IDI_SHAREICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); if (!g_hetASIconSmall) { ERROR_OUT(("HET_Init: Failed to load app small icon")); DC_QUIT; } g_hetDeskIconSmall = (HICON)LoadImage(g_asInstance, MAKEINTRESOURCE(IDI_DESKTOPICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); if (!g_hetDeskIconSmall) { ERROR_OUT(("HET_Init: Failed to load desktop small icon")); DC_QUIT; } // // Get the checkmark image // g_hetCheckBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK)); if (!g_hetCheckBitmap) { ERROR_OUT(("HET_Init: Failed to load checkmark bitmap")); DC_QUIT; } // // Create a bolded font for shared items in the host list // GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf); lf.lfWeight += FW_LIGHT; g_hetSharedFont = CreateFontIndirect(&lf); if (!g_hetSharedFont) { ERROR_OUT(("HET_Init: Failed to create shared item font")); DC_QUIT; } if (g_asCanHost && !(g_asPolicies & SHP_POLICY_NOSHARING)) { HET_Clear(); // // Create the host UI dialog. // ASSERT(!g_asSession.hwndHostUI); ASSERT(!g_asSession.fHostUI); ASSERT(!g_asSession.fHostUIFrozen); g_asSession.hwndHostUI = CreateDialogParam(g_asInstance, MAKEINTRESOURCE(IDD_HOSTUI), NULL, HostDlgProc, 0); if (!g_asSession.hwndHostUI) { ERROR_OUT(("Failed to create hosting UI dialog")); DC_QUIT; } } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(HET_Init, rc); return(rc); } // // HET_Term() // // Cleanup hosting objects // void HET_Term(void) { DebugEntry(HET_Term); if (g_asSession.hwndHostUI) { DestroyWindow(g_asSession.hwndHostUI); g_asSession.hwndHostUI = NULL; } g_asSession.fHostUIFrozen = FALSE; g_asSession.fHostUI = FALSE; if (g_hetSharedFont != NULL) { DeleteFont(g_hetSharedFont); g_hetSharedFont = NULL; } if (g_hetCheckBitmap != NULL) { DeleteBitmap(g_hetCheckBitmap); g_hetCheckBitmap = NULL; } if (g_hetDeskIconSmall != NULL) { DestroyIcon(g_hetDeskIconSmall); g_hetDeskIconSmall = NULL; } if (g_hetDeskIcon != NULL) { DestroyIcon(g_hetDeskIcon); g_hetDeskIcon = NULL; } if (g_hetASIconSmall != NULL) { DestroyIcon(g_hetASIconSmall); g_hetASIconSmall = NULL; } if (g_hetASIcon != NULL) { DestroyIcon(g_hetASIcon); g_hetASIcon = NULL; } DebugExitVOID(HET_Term); } // // HET_IsShellThread() // Returns TRUE if thread is one of shell's special threads // BOOL HET_IsShellThread(DWORD threadID) { BOOL rc; DebugEntry(HET_IsShellThread); if ((threadID == GetWindowThreadProcessId(HET_GetShellDesktop(), NULL)) || (threadID == GetWindowThreadProcessId(HET_GetShellTray(), NULL))) { rc = TRUE; } else { rc = FALSE; } DebugExitBOOL(HET_IsShellThread, rc); return(rc); } // // HET_IsShellWindow() // Returns TRUE if window is in same thread as tray or desktop // BOOL HET_IsShellWindow(HWND hwnd) { BOOL rc; DWORD threadID; DebugEntry(HET_IsShellWindow); threadID = GetWindowThreadProcessId(hwnd, NULL); rc = HET_IsShellThread(threadID); DebugExitBOOL(HET_IsShellWindow, rc); return(rc); } // // HET_ShareApp() // This shares an app. We have 3 types of sharing, only two // of which are supported currently: // (1) By process (normal) // (2) By thread (ConsoleNT or possibly Explorer) // (3) By window // // For the first two types, we enumerate all top level windows and share // them also. // void ASShare::HET_ShareApp ( WPARAM uType, LPARAM dwID ) { HET_SHARE_INFO si; DebugEntry(ASShare::HET_ShareApp); // // If we're sharing the desktop, ignore this. // if (m_pasLocal->hetCount == HET_DESKTOPSHARED) { WARNING_OUT(("Can't share app; already sharing desktop")); DC_QUIT; } si.cWnds = 0; si.uType = (UINT)uType; si.dwID = (DWORD)dwID; // // We need to get setup for sharing if we aren't hosting. // if (m_pasLocal->hetCount == 0) { if (!HETStartHosting(FALSE)) { ERROR_OUT(("Can't start sharing")); DC_QUIT; } } if (uType == IAS_SHARE_BYWINDOW) { HETShareCallback((HWND)dwID, (LPARAM)&si); } else { EnumWindows(HETShareCallback, (LPARAM)&si); } if (!si.cWnds) { // // Nothing happened. We couldn't find any top level windows. // if (m_pasLocal->hetCount == 0) { HETStopHosting(FALSE); } } else { HETUpdateLocalCount(m_pasLocal->hetCount + si.cWnds); } DC_EXIT_POINT: DebugExitVOID(HET_ShareApp); } // // HETShareCallback() // // This is the enumerator callback from HETShareApp(). We look for windows // matching the thread/process. // BOOL CALLBACK HETShareCallback ( HWND hwnd, LPARAM lParam ) { LPHET_SHARE_INFO lpsi = (LPHET_SHARE_INFO)lParam; DWORD idProcess; DWORD idThread; UINT hostType; char szClass[HET_CLASS_NAME_SIZE]; DebugEntry(HETShareCallback); ASSERT(!IsBadWritePtr(lpsi, sizeof(HET_SHARE_INFO))); // // Does this window match? // idThread = GetWindowThreadProcessId(hwnd, &idProcess); // NOTE: If the window is bogus now, dwThread/dwProcess will be zero, // and will not match the ones passed in. if (lpsi->uType == IAS_SHARE_BYPROCESS) { if (idProcess != lpsi->dwID) { DC_QUIT; } TRACE_OUT(("Found window 0x%08x on process 0x%08x", hwnd, idProcess)); } else if (lpsi->uType == IAS_SHARE_BYTHREAD) { if (idThread != lpsi->dwID) { DC_QUIT; } TRACE_OUT(("Found window 0x%08x on thread 0x%08x", hwnd, idThread)); } // // Always skip special shell thread windows (the tray, the desktop, etc.) // if (HET_IsShellThread(idThread)) { TRACE_OUT(("Skipping shell threads")); DC_QUIT; } // // Always skip menus and system tooltips, those are temporarily shared // when shown then unshared when hidden. That's because USER creates // global windows that move threads/processes as needed to use them. // // New menus being created are different, those never change task and // are treating like other windows in a shared app. // if (!GetClassName(hwnd, szClass, sizeof(szClass))) { TRACE_OUT(("Can't get class name for window 0x%08x", hwnd)); DC_QUIT; } if (!lstrcmp(szClass, HET_MENU_CLASS)) { TRACE_OUT(("Skipping menu popup window 0x%08x", hwnd)); DC_QUIT; } if (!lstrcmp(szClass, HET_TOOLTIPS98_CLASS) || !lstrcmp(szClass, HET_TOOLTIPSNT5_CLASS)) { TRACE_OUT(("Skipping system tooltip %08lx", hwnd)); DC_QUIT; } if (HET_GetHosting(hwnd)) { WARNING_OUT(("Window %08lx already shared", hwnd)); DC_QUIT; } hostType = HET_HOSTED_PERMANENT; if (lpsi->uType == IAS_SHARE_BYPROCESS) { hostType |= HET_HOSTED_BYPROCESS; } else if (lpsi->uType == IAS_SHARE_BYTHREAD) { hostType |= HET_HOSTED_BYTHREAD; } else if (lpsi->uType == IAS_SHARE_BYWINDOW) { hostType |= HET_HOSTED_BYWINDOW; } // // See if we can share it. This returns TRUE if success. // if (OSI_ShareWindow(hwnd, hostType, TRUE, FALSE)) { lpsi->cWnds++; } DC_EXIT_POINT: DebugExitBOOL(HET_ShareCallback, TRUE); return(TRUE); } // // HET_UnshareApp() // This unshares an app. We have 3 types of sharing, only two // of which are supported currently: // (1) By process (normal) // (2) By thread (ConsoleNT or possibly Explorer) // (3) By window (temporary) // // For the first two types, we enumerate all top level windows and share // them also. // void ASShare::HET_UnshareApp ( WPARAM uType, LPARAM dwID ) { HET_SHARE_INFO si; DebugEntry(ASShare::HET_UnshareApp); // // If we aren't sharing apps (not sharing anything or sharing the // dekstop), ignore this. // if ((m_pasLocal->hetCount == 0) || (m_pasLocal->hetCount == HET_DESKTOPSHARED)) { WARNING_OUT(("Can't unshare app; not sharing any")); DC_QUIT; } si.cWnds = 0; si.uType = (UINT)uType; si.dwID = (DWORD)dwID; if (uType == IAS_SHARE_BYWINDOW) { // // No enumeration, just this window. // HETUnshareCallback((HWND)dwID, (LPARAM)&si); } else { // // Stop sharing all windows in it. // EnumWindows(HETUnshareCallback, (LPARAM)&si); } if (si.cWnds) { HETUpdateLocalCount(m_pasLocal->hetCount - si.cWnds); } DC_EXIT_POINT: DebugExitVOID(ASShare::HET_UnshareApp); } // // HETUnshareCallback() // // This is the enumerator callback from HET_UnshareApp(). We look for windows // matching the thread/process. In this case, we don't care about menus // or explorer windows, since we assume that, from the time we shared and it // was set up properly, the window/task tracking code did the right thing. // If not, we'll wipe it out here anyway. // BOOL CALLBACK HETUnshareCallback ( HWND hwnd, LPARAM lParam ) { LPHET_SHARE_INFO lpsi = (LPHET_SHARE_INFO)lParam; DWORD dwProcess; DWORD dwThread; DebugEntry(HETUnshareCallback); ASSERT(!IsBadWritePtr(lpsi, sizeof(HET_SHARE_INFO))); // // Does this window match? If by window, always. // if (lpsi->uType != IAS_SHARE_BYWINDOW) { dwThread = GetWindowThreadProcessId(hwnd, &dwProcess); // NOTE: If the window is bogus now, dwThread/dwProcess will be zero, // and will not match the ones passed in. if (lpsi->uType == IAS_SHARE_BYPROCESS) { if (dwProcess != lpsi->dwID) { DC_QUIT; } TRACE_OUT(("Found window 0x%08x on process 0x%08x", hwnd, dwProcess)); } else if (lpsi->uType == IAS_SHARE_BYTHREAD) { if (dwThread != lpsi->dwID) { DC_QUIT; } TRACE_OUT(("Found window 0x%08x on thread 0x%08x", hwnd, dwThread)); } } // // This returns TRUE if we unshared a shared window. // if (OSI_UnshareWindow(hwnd, FALSE)) { lpsi->cWnds++; } DC_EXIT_POINT: DebugExitBOOL(HETUnshareCallback, TRUE); return(TRUE); } // // HET_ShareDesktop() // void ASShare::HET_ShareDesktop(void) { ASPerson * pasT; DebugEntry(ASShare:HET_ShareDesktop); // // If we're sharing apps, ignore this. // if (m_pasLocal->hetCount != 0) { WARNING_OUT(("Ignoring share desktop request, sharing apps")); DC_QUIT; } TRACE_OUT(("HET_ShareDesktop: starting share")); if (!HETStartHosting(TRUE)) { ERROR_OUT(("HET_ShareDesktop cannot start sharing desktop")); DC_QUIT; } // // Update the count of hosted entities (ie user-hosted windows) // HETUpdateLocalCount(HET_DESKTOPSHARED); // // Get the desktop(s) repainted if anybody's viewing it. // ASSERT(m_pHost); m_pHost->HET_RepaintAll(); DC_EXIT_POINT: DebugExitVOID(ASShare::HET_ShareDesktop); } // // HET_UnshareAll() // Unshares everything including the desktop. If we had been sharing // apps before, we will unshare them all. // void ASShare::HET_UnshareAll(void) { DebugEntry(ASShare::HET_UnshareAll); if (m_pasLocal->hetCount != 0) { HETUpdateLocalCount(0); } DebugExitVOID(ASShare::HET_UnshareAll); } // // HET_PartyJoiningShare() // BOOL ASShare::HET_PartyJoiningShare(ASPerson * pasPerson) { BOOL rc = TRUE; DebugEntry(ASShare::HET_PartyJoiningShare); HET_CalcViewers(NULL); DebugExitBOOL(ASShare::HET_PartyJoiningShare, rc); return(rc); } // // HET_PartyLeftShare() // void ASShare::HET_PartyLeftShare(ASPerson * pasPerson) { DebugEntry(ASShare::HET_PartyLeftShare); // This guy is leaving the share, cleanup if he was sharing. ValidatePerson(pasPerson); if (pasPerson->hetCount != 0) { // This person is hosting if (pasPerson == m_pasLocal) { HETUpdateLocalCount(0); } else { HETUpdateRemoteCount(pasPerson, 0); } } // // If we're hosting, stop viewing if this is the last person in the share. // HET_CalcViewers(pasPerson); DebugExitVOID(ASShare::HET_PartyLeftShare); } // // HET_CalcViewers() // // If we or a remote is viewing our shared stuff, then we must accumulate // graphic output. If not, don't other, but keep the app tracked as necessary. // // This is called when we start to host, when somebody joins, or somebody // leaves the conference. // void ASShare::HET_CalcViewers(ASPerson * pasLeaving) { BOOL fViewers; DebugEntry(ASShare::HET_CalcViewers); fViewers = FALSE; if (m_pHost) { if (m_scfViewSelf) { fViewers = TRUE; } else if (!pasLeaving) { // // Nobody is leaving, so just check if anybody else is in the // share. // if (m_pasLocal->pasNext) { fViewers = TRUE; } } else if (pasLeaving->pasNext || (m_pasLocal->pasNext != pasLeaving)) { // // Sombody is leaving. // The person leaving isn't the only other one besides us in the // share, since there are others after it or before it in the // members linked list. // fViewers = TRUE; } } if (fViewers != m_hetViewers) { HET_VIEWER viewer; m_hetViewers = fViewers; viewer.viewersPresent = fViewers; OSI_FunctionRequest(HET_ESC_VIEWER, (LPOSI_ESCAPE_HEADER)&viewer, sizeof(viewer)); } DebugExitVOID(ASShare::HET_CalcViewers); } // // HET_ReceivedPacket() // void ASShare::HET_ReceivedPacket ( ASPerson * pasPerson, PS20DATAPACKET pPacket ) { PHETPACKET pHETPacket; DebugEntry(ASShare:;HET_ReceivedPacket); ValidatePerson(pasPerson); pHETPacket = (PHETPACKET)pPacket; switch (pHETPacket->msg) { case HET_MSG_NUMHOSTED: HETUpdateRemoteCount(pasPerson, pHETPacket->hostState); break; default: ERROR_OUT(("Unknown HET packet type %u from [%d]", pHETPacket->msg, pasPerson->mcsID)); break; } DebugExitVOID(ASShare::HET_ReceivedPacket); } // // HET_SyncCommon() // // Called when somebody joins a share, after it is fully joined. We repaint // all shared windows and send the current hosted top-level count. // // Also called when sharing, and somebody joins later. // // NOTE that some of the resets don't do anything when are just starting to // share. But all are quick and benign. // void ASHost::HET_SyncCommon(void) { OSI_ESCAPE_HEADER osi; DebugEntry(ASHost::HET_SyncCommon); m_upfSyncTokenRequired = TRUE; BA_SyncOutgoing(); OE2_SyncOutgoing(); // To reset order encoding OA_SyncOutgoing(); // To clear pending orders SBC_SyncOutgoing(); // To clear bitmap cache PM_SyncOutgoing(); // To clear palette cache SSI_SyncOutgoing(); // To reset savebits orders SWL_SyncOutgoing(); // To reset shared window list AWC_SyncOutgoing(); // To send active window CM_SyncOutgoing(); // To send cursor shape/pos // // Tell the driver we are syncing // OSI_FunctionRequest(OSI_ESC_SYNC_NOW, &osi, sizeof(osi)); DebugExitVOID(ASHost::HET_SyncCommon); } // // HET_SyncAlreadyHosting() // Called in a sync when we are already hosting and somebody joins call // void ASHost::HET_SyncAlreadyHosting(void) { DebugEntry(ASHost::HET_SyncAlreadyHosting); HET_RepaintAll(); // Send out the current hosted count m_pShare->m_hetRetrySendState = TRUE; DebugExitVOID(ASHost::HET_SyncAlreadyHosting); } // // HET_RepaintAll() // // Repaints all shared stuff if there's at least two people in the share... // void ASHost::HET_RepaintAll(void) { DebugEntry(ASHost::HET_RepaintAll); ASSERT(m_pShare); ASSERT(m_pShare->m_pasLocal); if (m_pShare->m_hetViewers) { // // Only repaint if somebody's viewing // if (m_pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED) { // Desktop sharing, so repaint desktop(s) USR_RepaintWindow(NULL); OSI_RepaintDesktop(); //special repaint for winlogon desktop } else { // App sharing, so repaint shared apps EnumWindows(HETRepaintWindow, (LPARAM)m_pShare); } } DebugExitVOID(ASHost::HET_RepaintAll); } // // HET_Periodic() // void ASShare::HET_Periodic(void) { DebugEntry(ASShare::HET_Periodic); if (m_hetRetrySendState) { TRACE_OUT(( "Retry sending hosted count")); HETSendLocalCount(); } DebugExitVOID(ASShare::HET_Periodic); } // // HET_WindowIsHosted - see het.h // BOOL ASShare::HET_WindowIsHosted(HWND hwnd) { BOOL rc = FALSE; HWND hwndParent; DebugEntry(ASShare::HET_WindowIsHosted); // // Desktop sharing: everything is shared // if (m_pasLocal->hetCount == HET_DESKTOPSHARED) { rc = TRUE; DC_QUIT; } if (!hwnd) { TRACE_OUT(("NULL window passed to HET_WindowIsHosted")); DC_QUIT; } // // Walk up to the top level window this one is part of // while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD) { hwndParent = GetParent(hwnd); if (hwndParent == GetDesktopWindow()) break; hwnd = hwndParent; } rc = (BOOL)HET_GetHosting(hwnd); DC_EXIT_POINT: DebugExitBOOL(ASShare::HET_WindowIsHosted, rc); return(rc); } // // HET_HandleNewTopLevel() // Called when a shared top level window is shown or hidden. We update // our local top level count. // void ASShare::HET_HandleNewTopLevel(BOOL fShown) { DebugEntry(ASShare::HET_HandleNewTopLevel); // // If we aren't sharing any apps (not sharing at all or sharing the // desktop), ignore this. // if ((m_pasLocal->hetCount == 0) || (m_pasLocal->hetCount == HET_DESKTOPSHARED)) { WARNING_OUT(("Ignoring new hosted notification; count is 0x%04x", m_pasLocal->hetCount)); DC_QUIT; } if (fShown) HETUpdateLocalCount(m_pasLocal->hetCount + 1); else HETUpdateLocalCount(m_pasLocal->hetCount - 1); DC_EXIT_POINT: DebugExitVOID(ASShare::HET_HandleNewTopLevel); } // // HET_HandleRecountTopLevel() // Called when a massive change in the top level visible count occurs, so // that we can just set the new total at once, rather than handle // individual inc/dec messages. // void ASShare::HET_HandleRecountTopLevel(UINT uNewCount) { DebugEntry(ASShare::HET_HandleRecountTopLevel); // // If we aren't sharing any apps (not sharing at all or sharing the // desktop), ignore this. // if ((m_pasLocal->hetCount == 0) || (m_pasLocal->hetCount == HET_DESKTOPSHARED)) { WARNING_OUT(("Ignoring new hosted notification; count is 0x%04x", m_pasLocal->hetCount)); DC_QUIT; } HETUpdateLocalCount(uNewCount); DC_EXIT_POINT: DebugExitVOID(ASShare::HET_HandleRecountTopLevel); } // // HETStartHosting() // // Called when we are about to begin sharing windows. fDesktop is TRUE if // we are sharing the entire desktop, FALSE if just individual windows. // BOOL ASShare::HETStartHosting(BOOL fDesktop) { BOOL rc = FALSE; DebugEntry(ASShare::HETStartHosting); // // Create the hosting object // ASSERT(!m_pHost); m_pHost = new ASHost; if (!m_pHost) { ERROR_OUT(("HETStartHosting: couldn't create m_pHost")); DC_QUIT; } ZeroMemory(m_pHost, sizeof(*(m_pHost))); SET_STAMP(m_pHost, HOST); // // Init hosting // if (!m_pHost->HET_HostStarting(this)) { ERROR_OUT(("Failed to init hosting for local person")); DC_QUIT; } // // Start tracking graphics/windows // if (fDesktop) { HET_SHARE_DESKTOP hdr; // // Shortcut directly to display driver. No need to track windows // since everything will be shared. // if (!OSI_FunctionRequest(HET_ESC_SHARE_DESKTOP, (LPOSI_ESCAPE_HEADER)&hdr, sizeof(hdr))) { ERROR_OUT(("HET_ESC_SHARE_DESKTOP failed")); DC_QUIT; } } else { // // Start tracking windows. // if (!OSI_StartWindowTracking()) { ERROR_OUT(( "Failed to install window tracking hooks")); DC_QUIT; } } if (m_scfViewSelf && !HET_ViewStarting(m_pasLocal)) { ERROR_OUT(("ViewSelf option is on, but can't create ASView data")); DC_QUIT; } HET_CalcViewers(NULL); rc = TRUE; DC_EXIT_POINT: // // Return to caller // DebugExitBOOL(ASShare::HETStartHosting, rc); return(rc); } // // // Name: HETStopHosting // // Description: Called when the last hosted window is unshared // ALWAYS CALL THIS AFTER the "hethostedTopLevel" count is 0. // // Params: none // // void ASShare::HETStopHosting(BOOL fDesktop) { DebugEntry(ASShare::HETStopHosting); m_hetViewers = FALSE; // // Stop tracking graphics/windows. This will stop viewing, then uninstall // hooks. // if (fDesktop) { HET_UNSHARE_DESKTOP hdr; // // There is no window tracking, just shortcut directly to the // display driver. // OSI_FunctionRequest(HET_ESC_UNSHARE_DESKTOP, (LPOSI_ESCAPE_HEADER)&hdr, sizeof(hdr)); } else { // // Unshare any remaining shared windows // HET_Clear(); OSI_StopWindowTracking(); } // // Tell areas we are finished hosting // if (m_pHost) { // // If we're viewing ourself, kill the view first // if (m_scfViewSelf) { HET_ViewEnded(m_pasLocal); } m_pHost->HET_HostEnded(); // // Delete host object // delete m_pHost; m_pHost = NULL; } // // Return to caller // DebugExitVOID(ASShare::HETStopHosting); } // // HETSendLocalCount() // This sends the hosting count to remotes. // * If zero, we are not sharing // * If one, we are sharing apps // * If 0xFFFF, we are sharing desktop // // Note that we used to send the real count of top level windows, so every // time a new window came or went, we would broadcast a packet. But // remotes only care when the value goes from zero to non-zero or back, // and when non-zero if it's the special desktop value or not. So don't // repeatedly broadcast values remotes don't care about! // void ASShare::HETSendLocalCount(void) { PHETPACKET pHETPacket; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(ASShare::HETSendLocalCount); // // Allocate a packet for the HET data. // pHETPacket = (PHETPACKET)SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, sizeof(HETPACKET)); if (!pHETPacket) { WARNING_OUT(("Failed to alloc HET host packet")); m_hetRetrySendState = TRUE; DC_QUIT; } // // Packet successfully allocated. Fill in the data and send it. // pHETPacket->header.data.dataType = DT_HET; pHETPacket->msg = HET_MSG_NUMHOSTED; switch (m_pasLocal->hetCount) { case 0: // Not hosting pHETPacket->hostState = HET_NOTHOSTING; break; case HET_DESKTOPSHARED: // Sharing desktop - 3.0 only pHETPacket->header.data.dataType = DT_HET30; pHETPacket->hostState = HET_DESKTOPSHARED; break; default: // Sharing apps pHETPacket->hostState = HET_APPSSHARED; break; } // // Compress and send the packet. // #ifdef _DEBUG sentSize = #endif // _DEBUG DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID, &(pHETPacket->header), sizeof(*pHETPacket)); TRACE_OUT(("HET packet size: %08d, sent %08d", sizeof(*pHETPacket), sentSize)); TRACE_OUT(("Sent new HET packet (%d)", m_pasLocal->hetCount)); m_hetRetrySendState = FALSE; // // Return to caller // DC_EXIT_POINT: DebugExitVOID(ASShare::HETSendLocalCount); } // // HETUpdateLocalCount() // void ASShare::HETUpdateLocalCount(UINT newCount) { UINT oldCount; DebugEntry(ASShare::HETUpdateLocalCount); oldCount = m_pasLocal->hetCount; m_pasLocal->hetCount = newCount; if ((oldCount == 0) && (newCount != 0)) { SendMessage(g_asSession.hwndHostUI, HOST_MSG_HOSTSTART, 0, 0); // // Don't bother sending net packets if nobody is viewing // if (m_hetViewers) { HETSendLocalCount(); } HETCheckSharing(TRUE); } else if ((oldCount != 0) && (newCount == 0)) { if (m_hetViewers) { // // Ending host, desktop or apps // HETSendLocalCount(); } // // The local guy is stopping sharing. // HETStopHosting(oldCount == HET_DESKTOPSHARED); ASSERT(IsWindow(g_asSession.hwndHostUI)); SendMessage(g_asSession.hwndHostUI, HOST_MSG_HOSTEND, 0, 0); HETCheckSharing(FALSE); } ASSERT(IsWindow(g_asSession.hwndHostUI)); SendMessage(g_asSession.hwndHostUI, HOST_MSG_UPDATELIST, 0, 0); DebugExitVOID(ASShare::HETUpdateLocalCount); } // // HETUpdateRemoteCount() // // Updates the count of shared top level windows from a remote, and notifies // the UI on transition from/to zero if a remote. If local, kills the share. // void ASShare::HETUpdateRemoteCount ( ASPerson * pasPerson, UINT newCount ) { UINT oldCount; DebugEntry(ASShare::HETUpdateRemoteCount); ValidatePerson(pasPerson); ASSERT(pasPerson != m_pasLocal); oldCount = pasPerson->hetCount; pasPerson->hetCount = newCount; TRACE_OUT(("HETUpdateRemoteCount: Person [%d] old %d, new %d", pasPerson->mcsID, oldCount, newCount)); // // We generate events for remote people if // * They were sharing but now they aren't // * There weren't sharing but now they are // if ((oldCount == 0) && (newCount != 0)) { // // The remote dude started to share // if (!HET_ViewStarting(pasPerson)) { ERROR_OUT(("HET_ViewStarting failed; pretending remote not sharing")); pasPerson->hetCount = 0; HET_ViewEnded(pasPerson); } else { HETCheckSharing(TRUE); } } else if ((oldCount != 0) && (newCount == 0)) { // // The remote dude stopped sharing. Notify the UI also. // HET_ViewEnded(pasPerson); HETCheckSharing(FALSE); } DebugExitVOID(ASShare::HETUpdateRemoteCount); } // // HETCheckSharing() // Called when any member of the conference (local or remote) transitions // to/from sharing. When the first person has shared something, we notify // the UI. When the last person has stopped sharing, we kill the share which // will notify the UI. // void ASShare::HETCheckSharing(BOOL fStarting) { DebugEntry(ASShare::HETCheckSharing); if (fStarting) { ++m_hetHostCount; if (m_hetHostCount == 1) { // First host started TRACE_OUT(("First person started hosting")); DCS_NotifyUI(SH_EVT_SHARING_STARTED, 0, 0); } } else { ASSERT(m_hetHostCount > 0); --m_hetHostCount; if (m_hetHostCount == 0) { // // Last host stopped sharing -- end share if we're not cleaning // up after the fact. But don't do it NOW, post a message. // We may have come in here because the share is ending already. // PostMessage(g_asMainWindow, DCS_KILLSHARE_MSG, 0, 0); } } DebugExitVOID(ASShare::HETCheckSharing); } // // HET_HostStarting() // // Called when we start to host applications. This creates our host data // then calls the component HostStarting() routines // BOOL ASHost::HET_HostStarting(ASShare * pShare) { BOOL rc = FALSE; DebugEntry(ASHost::HET_HostStarting); // Set back pointer to share m_pShare = pShare; // // Turn effects off // HET_SetGUIEffects(FALSE, &m_hetEffects); OSI_SetGUIEffects(FALSE); // // Now call HostStarting() routines // if (!USR_HostStarting()) { ERROR_OUT(("USR_HostStarting failed")); DC_QUIT; } if (!OE2_HostStarting()) { ERROR_OUT(("OE2_HostStarting failed")); DC_QUIT; } if (!SBC_HostStarting()) { ERROR_OUT(("SBC_HostStarting failed")); DC_QUIT; } if (!CM_HostStarting()) { ERROR_OUT(("CM_HostStarting failed")); DC_QUIT; } if (!SSI_HostStarting()) { ERROR_OUT(("SSI_HostStarting failed")); DC_QUIT; } if (!PM_HostStarting()) { ERROR_OUT(("PM_HostStarting failed")); DC_QUIT; } if (!SWL_HostStarting()) { ERROR_OUT(("SWL_HostStarting failed")); DC_QUIT; } if (!VIEW_HostStarting()) { ERROR_OUT(("VIEW_HostStarting failed")); DC_QUIT; } // // Now reset OUTGOING info. 2.x nodes do not; that's why we have to // hang on to RBC, OD2, CM, PM data for them. When 2.x compat is gone, // we can move ASPerson data in to ASView, which is only around while // the person is in fact hosting. // OA_LocalHostReset(); // // Reset OUTGOING data. // Note corresponding cleanup for 3.0 nodes // in CM, OD2, RBC, and PM. // Note that we don't need to reset SSI incoming goop, since we will // clear all pending orders and are invalidating everything shared // from scratch. There will be no reference to a previous savebits. // HET_SyncCommon(); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::HET_HostStarting, rc); return(rc); } // // HET_HostEnded() // // Called when we stop hosting applications. // void ASHost::HET_HostEnded(void) { DebugEntry(ASHost::HET_HostEnded); // // Call HostEnded() routines // CA_HostEnded(); SWL_HostEnded(); PM_HostEnded(); CM_HostEnded(); SBC_HostEnded(); OE2_HostEnded(); USR_HostEnded(); // // Restore windows animation. // HET_SetGUIEffects(TRUE, &m_hetEffects); OSI_SetGUIEffects(TRUE); DebugExitVOID(ASHost::HET_HostEnded); } // // HET_ViewStarting() // // Called to create the data needed to view somebody who is hosting. // BOOL ASShare::HET_ViewStarting(ASPerson * pasPerson) { BOOL rc = FALSE; DebugEntry(ASShare::HET_ViewStarting); ValidatePerson(pasPerson); // // Create ASView object // ASSERT(!pasPerson->m_pView); // Allocate VIEW structure pasPerson->m_pView = new ASView; if (!pasPerson->m_pView) { // Abject, total, failure. ERROR_OUT(("HET_ViewStarting: Couldn't allocate ASView for [%d]", pasPerson->mcsID)); DC_QUIT; } ZeroMemory(pasPerson->m_pView, sizeof(*(pasPerson->m_pView))); SET_STAMP(pasPerson->m_pView, VIEW); // // Now call ViewStarting routines // if (!USR_ViewStarting(pasPerson)) { ERROR_OUT(("USR_ViewStarting failed")); DC_QUIT; } if (!OD2_ViewStarting(pasPerson)) { ERROR_OUT(("OD2_ViewStarting failed")); DC_QUIT; } if (!OD_ViewStarting(pasPerson)) { ERROR_OUT(("OD_ViewStarting failed")); DC_QUIT; } if (!RBC_ViewStarting(pasPerson)) { ERROR_OUT(("RBC_ViewStarting failed")); DC_QUIT; } if (!CM_ViewStarting(pasPerson)) { ERROR_OUT(("CM_ViewStarting failed")); DC_QUIT; } if (!SSI_ViewStarting(pasPerson)) { ERROR_OUT(("SSI_ViewStarting failed")); DC_QUIT; } if (!PM_ViewStarting(pasPerson)) { ERROR_OUT(("PM_ViewStarting failed")); DC_QUIT; } if (!VIEW_ViewStarting(pasPerson)) { ERROR_OUT(("VIEW_ViewStarting failed")); DC_QUIT; } if (!CA_ViewStarting(pasPerson)) { ERROR_OUT(("CA_ViewStarting failed")); DC_QUIT; } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASShare::HET_ViewStarting, rc); return(rc); } // // HET_ViewEnded() // // Called when we stop viewing a host // void ASShare::HET_ViewEnded(ASPerson * pasPerson) { DebugEntry(ASShare::HET_ViewEnded); ValidatePerson(pasPerson); if (pasPerson->m_pView) { // // Call the component ViewEnded routines // CA_ViewEnded(pasPerson); VIEW_ViewEnded(pasPerson); PM_ViewEnded(pasPerson); SSI_ViewEnded(pasPerson); CM_ViewEnded(pasPerson); RBC_ViewEnded(pasPerson); OD_ViewEnded(pasPerson); OD2_ViewEnded(pasPerson); USR_ViewEnded(pasPerson); delete pasPerson->m_pView; pasPerson->m_pView = NULL; } DebugExitVOID(ASShare::HET_ViewEnded); } // // HETUnshareAllWindows() // EnumWindows() callback, to make sure when you exit a share on the local // machine, we aren't left with any properties on top level windows. // BOOL CALLBACK HETUnshareAllWindows(HWND hwnd, LPARAM lParam) { DebugEntry(HETUnshareAllWindows); HET_ClearHosting(hwnd); DebugExitVOID(HETUnshareAllWindows); return(TRUE); } // // HET_Clear() // void HET_Clear(void) { HET_UNSHARE_ALL req; DebugEntry(HET_Clear); ASSERT(g_asCanHost); // // Quick DD communication to wipe out the track list // OSI_FunctionRequest(HET_ESC_UNSHARE_ALL, (LPOSI_ESCAPE_HEADER)&req, sizeof(req)); // // Enum all top level windows, and wipe out the property. // if we can share. // EnumWindows(HETUnshareAllWindows, 0); DebugExitVOID(HET_Clear); } // // HETRepaintWindow() // EnumWindows() callback to repaint each window, happens when somebody // joins a share // BOOL CALLBACK HETRepaintWindow(HWND hwnd, LPARAM lParam) { ASShare * pShare = (ASShare *)lParam; ASSERT(!IsBadWritePtr(pShare, sizeof(*pShare))); if (pShare->HET_WindowIsHosted(hwnd)) { USR_RepaintWindow(hwnd); } return(TRUE); } // // HET_SetGUIEffects // // Turns various animations off/on when we start/stop hosting, to improve // performance. Currently, we mess with // * min animation // * all of the effects in SPI_SETUIEFFECTS (tooltip fade, menu animation, // etc.) // * cursor shadows // // We don't turn off smooth scroll or full drag. // void HET_SetGUIEffects ( BOOL fOn, GUIEFFECTS * pEffects ) { DebugEntry(HET_SetGUIEffects); ASSERT(!IsBadWritePtr(pEffects, sizeof(*pEffects))); // // NB. We deliberately do not track the state of animation whilst we // are sharing. A determined user could, using some other app (such as // the TweakUI control panel applet) reenable animation whilst in a // share. We will respect this. // // We only affect the current 'in memory' setting - we do not write our // temporary change to file. // if (fOn) { // // If it was on before, restore it. // if (pEffects->hetAnimation.iMinAnimate) { pEffects->hetAnimation.cbSize = sizeof(pEffects->hetAnimation); SystemParametersInfo(SPI_SETANIMATION, sizeof(pEffects->hetAnimation), &pEffects->hetAnimation, 0); } if (pEffects->hetAdvanced) { SystemParametersInfo(SPI_SETUIEFFECTS, 0, (LPVOID)pEffects->hetAdvanced, 0); } if (pEffects->hetCursorShadow) { SystemParametersInfo(SPI_SETCURSORSHADOW, 0, (LPVOID)pEffects->hetCursorShadow, 0); } } else { // // Find out what animations are on. // ZeroMemory(&pEffects->hetAnimation, sizeof(pEffects->hetAnimation)); pEffects->hetAnimation.cbSize = sizeof(pEffects->hetAnimation); SystemParametersInfo(SPI_GETANIMATION, sizeof(pEffects->hetAnimation), &pEffects->hetAnimation, 0); pEffects->hetAdvanced = FALSE; SystemParametersInfo(SPI_GETUIEFFECTS, 0, &pEffects->hetAdvanced, 0); pEffects->hetCursorShadow = FALSE; SystemParametersInfo(SPI_GETCURSORSHADOW, 0, &pEffects->hetCursorShadow, 0); // // Turn off the animations which are on. // if (pEffects->hetAnimation.iMinAnimate) { // // It's currently enabled, suppress it. // pEffects->hetAnimation.cbSize = sizeof(pEffects->hetAnimation); pEffects->hetAnimation.iMinAnimate = FALSE; SystemParametersInfo(SPI_SETANIMATION, sizeof(pEffects->hetAnimation), &pEffects->hetAnimation, 0); // SPI will wipe this out. Keep it set so we know to restore it. pEffects->hetAnimation.iMinAnimate = TRUE; } if (pEffects->hetAdvanced) { SystemParametersInfo(SPI_SETUIEFFECTS, 0, FALSE, 0); } if (pEffects->hetCursorShadow) { SystemParametersInfo(SPI_SETCURSORSHADOW, 0, FALSE, 0); } } DebugExitVOID(ASHost::HET_SetGUIEffects); } // // HET_GetAppsList() // Gets the list of shareable applications, the ones currently shared and // the ones available for sharing. // // This routine does NOT check if we're in a call. The interface from the // UI for the SDK does. This allows us to show the task list, disabled, // always in the share host UI. // BOOL HET_GetAppsList(IAS_HWND_ARRAY ** ppArray) { BOOL rc = FALSE; HOSTENUM hostEnum; DebugEntry(HET_GetAppsList); ASSERT(ppArray != NULL); *ppArray = NULL; // // Generate a list of shareable apps // This does NOT include the desktop. // ::COM_BasedListInit(&hostEnum.list); hostEnum.count = 0; hostEnum.countShared = 0; ::EnumWindows(HostEnumProc, (LPARAM)&hostEnum); // // If there's nothing left in the list, but we know something is // shared, it means there's a hidden/weird window the user can't // see. Fake a catchall entry. // if (hostEnum.countShared && !hostEnum.count) { ::COM_SimpleListAppend(&hostEnum.list, HWND_BROADCAST); hostEnum.count++; } *ppArray = (IAS_HWND_ARRAY *)new BYTE[sizeof(IAS_HWND_ARRAY) + (hostEnum.count * sizeof(IAS_HWND))]; if (*ppArray != NULL) { (*ppArray)->cEntries = hostEnum.count; (*ppArray)->cShared = hostEnum.countShared; IAS_HWND * pEntry; pEntry = (*ppArray)->aEntries; while (! ::COM_BasedListIsEmpty(&hostEnum.list)) { pEntry->hwnd = (HWND) ::COM_SimpleListRemoveHead(&hostEnum.list); pEntry->fShared = (pEntry->hwnd == HWND_BROADCAST) || (HET_IsWindowShared(pEntry->hwnd)); pEntry++; } rc = TRUE; } else { WARNING_OUT(("HET_GetAppsList: can't allocate app array")); } DebugExitBOOL(HET_GetAppsList, rc); return(rc); } // // HET_FreeAppsList() // void HET_FreeAppsList(IAS_HWND_ARRAY * pArray) { ASSERT(!IsBadWritePtr(pArray, sizeof(*pArray))); delete pArray; } // // HostEnumProc() // // EnumWindows callback. This makes the shared/shareable task list. // BOOL CALLBACK HostEnumProc(HWND hwnd, LPARAM lParam) { PHOSTENUM phostEnum = (PHOSTENUM)lParam; // // We are only interested in windows which: // - are shareable // - have no owner. This should remove all top level windows // except task windows // - are not the front end itself, which should not be shared // - are visible // - are not shadowed or already hosted // // We are also only interested in already hosted or shadowed apps, but // since only ASMaster knows our SHP_HANDLE, we let it test for that // afterwards, since then we can use SHP_GetWindowStatus(). // if (HET_IsWindowShared(hwnd)) { phostEnum->countShared++; } HWND hwndOwner = ::GetWindow(hwnd, GW_OWNER); // // Note that we also want to skip windows with no title. There's not // much point is showing in the Share menu since // nobody will have a clue what it is. // if ( HET_IsWindowShareable(hwnd) && ((NULL == hwndOwner) || !::IsWindowVisible(hwndOwner)) && ::IsWindowVisible(hwnd) && ::GetWindowTextLength(hwnd) ) { ::COM_SimpleListAppend((PBASEDLIST)(&((PHOSTENUM)phostEnum)->list), (void *) hwnd); phostEnum->count++; } // // Return TRUE for the enumeration to continue // return TRUE; } // // HET_IsWindowShared() // BOOL HET_IsWindowShared(HWND hwnd) { BOOL rc = FALSE; UT_Lock(UTLOCK_AS); if (g_asSession.pShare && g_asSession.pShare->m_pasLocal) { if (hwnd == GetDesktopWindow()) { rc = (g_asSession.pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED); } else if (hwnd == HWND_BROADCAST) { rc = (g_asSession.pShare->m_pasLocal->hetCount != 0); } else { rc = (HET_GetHosting(hwnd) != 0); } } UT_Unlock(UTLOCK_AS); return(rc); } // // HET_IsWindowShareable() // BOOL HET_IsWindowShareable(HWND hwnd) { BOOL rc = FALSE; UT_Lock(UTLOCK_AS); if (HET_IsWindowShared(hwnd)) { // It's shared -- so it must be shareable (or was at the time) rc = TRUE; DC_QUIT; } // // Now check the window against share restrictions // // if this is the desktop, check it if (hwnd == ::GetDesktopWindow()) { if (g_asPolicies & SHP_POLICY_NODESKTOPSHARE) { // // Policy prevents desktop sharing // DC_QUIT; } } else { DWORD idProcess; char szClass[HET_CLASS_NAME_SIZE]; if (GetWindowThreadProcessId(hwnd, &idProcess) && (idProcess == GetCurrentProcessId())) { // // We NEVER let you share windows in the caller's process // DC_QUIT; } if (HET_IsShellWindow(hwnd)) { // // We NEVER let you share the tray or the shell desktop // DC_QUIT; } if ((g_asPolicies & SHP_POLICY_SHAREMASK) && GetClassName(hwnd, szClass, sizeof(szClass))) { // // Check for CMD prompt // if (!lstrcmpi(szClass, HET_CMD95_CLASS) || !lstrcmpi(szClass, HET_CMDNT_CLASS)) { if (g_asPolicies & SHP_POLICY_NODOSBOXSHARE) { // // Policy prevents cmd prompt sharing // DC_QUIT; } } // // Check for SHELL // if (!lstrcmpi(szClass, HET_EXPLORER_CLASS) || !lstrcmpi(szClass, HET_CABINET_CLASS)) { if (g_asPolicies & SHP_POLICY_NOEXPLORERSHARE) { // // Policy prevents shell sharing // DC_QUIT; } } } } // // Finally! It's OK to share this. // rc = TRUE; DC_EXIT_POINT: UT_Unlock(UTLOCK_AS); return(rc); } // // HostDlgProc() // // Handles the hosting UI dialog. This may or may not be visible. It can // only actually share apps and change control state when in a call. But // users may keep it up as a mini-taskman thing, so we need to dyanmically // update its state. // INT_PTR CALLBACK HostDlgProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { BOOL rc = TRUE; DebugEntry(HostDlgProc); switch (uMsg) { case WM_INITDIALOG: { HOST_InitDialog(hwnd); rc = FALSE; break; } case WM_DESTROY: { // // Because NT4.x has bad WM_DELETEITEM bugs, we must clear out // the listbox now, to avoid leaking the memory for the // items. SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_RESETCONTENT, 0, 0); rc = FALSE; break; } case WM_INITMENU: { if (IsIconic(hwnd)) { EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_MINIMIZE, MF_BYCOMMAND | MF_GRAYED); } else { EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_RESTORE, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED); } break; } case WM_SYSCOMMAND: { switch (wParam) { case CMD_TOPMOST: { if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) { CheckMenuItem(GetSystemMenu(hwnd, FALSE), CMD_TOPMOST, MF_BYCOMMAND | MF_UNCHECKED); SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } else { CheckMenuItem(GetSystemMenu(hwnd, FALSE), CMD_TOPMOST, MF_BYCOMMAND | MF_CHECKED); SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } break; } default: { rc = FALSE; break; } } break; } case WM_COMMAND: { switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: if (::GetFocus() == GetDlgItem(hwnd, CTRL_PROGRAM_LIST)) { // Do same thing as double-click HOST_ChangeShareState(hwnd, CHANGE_TOGGLE); break; } // FALL THROUGH case IDCANCEL: SendMessage(hwnd, WM_CLOSE, 0, 0); break; case CTRL_PROGRAM_LIST: { // Double-click/Enter means to toggle sharing switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case LBN_SELCHANGE: { HOST_OnSelChange(hwnd); break; } case LBN_DBLCLK: { HOST_ChangeShareState(hwnd, CHANGE_TOGGLE); break; } } break; } case CTRL_SHARE_BTN: { HOST_ChangeShareState(hwnd, CHANGE_SHARED); break; } case CTRL_UNSHARE_BTN: { HOST_ChangeShareState(hwnd, CHANGE_UNSHARED); break; } case CTRL_UNSHAREALL_BTN: { HOST_ChangeShareState(hwnd, CHANGE_ALLUNSHARED); break; } case CTRL_ALLOWCONTROL_BTN: { // Turn on allow state. switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case BN_CLICKED: { // // CA_AllowControl() will send us a message back // and cause us to change the button. // SendMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, TRUE, 0); break; } } break; } case CTRL_PREVENTCONTROL_BTN: { // Turn off allow state. switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case BN_CLICKED: { // // CA_AllowControl() will send us a message back // and cause us to change the button. // SendMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0); break; } } break; } case CTRL_ENABLETRUECOLOR_CHECK: { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case BN_CLICKED: { // // This takes effect the next time something // changes--somebody joins, somebody leaves, // you stop/start hosting // if (IsDlgButtonChecked(hwnd, CTRL_ENABLETRUECOLOR_CHECK)) { g_asSettings |= SHP_SETTING_TRUECOLOR; } else { g_asSettings &= ~SHP_SETTING_TRUECOLOR; } break; } } break; } case CTRL_AUTOACCEPTCONTROL_CHECK: { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case BN_CLICKED: { // // This takes effect when the next control // request comes in. // if (g_asSession.pShare && g_asSession.pShare->m_pHost) { g_asSession.pShare->m_pHost->m_caAutoAcceptRequests = (IsDlgButtonChecked(hwnd, CTRL_AUTOACCEPTCONTROL_CHECK) != 0); } break; } } break; } case CTRL_TEMPREJECTCONTROL_CHECK: { switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case BN_CLICKED: { // // This takes effect when the next control // request comes in. // // NOTE THAT IT TAKES PRECEDENCE OVER AUTO-ACCEPT. // This allows you to keep auto-accept on, but then // temporarily do not disturb. // if (g_asSession.pShare && g_asSession.pShare->m_pHost) { g_asSession.pShare->m_pHost->m_caTempRejectRequests = (IsDlgButtonChecked(hwnd, CTRL_TEMPREJECTCONTROL_CHECK) != 0); } break; } } break; } } break; } case WM_MEASUREITEM: { rc = HOST_MeasureItem(hwnd, (LPMEASUREITEMSTRUCT)lParam); break; } case WM_DELETEITEM: { rc = HOST_DeleteItem(hwnd, (LPDELETEITEMSTRUCT)lParam); break; } case WM_DRAWITEM: { rc = HOST_DrawItem(hwnd, (LPDRAWITEMSTRUCT)lParam); break; } case WM_TIMER: { if (wParam != IDT_REFRESH) { rc = FALSE; } else { ASSERT(IsWindowVisible(hwnd)); HOST_FillList(hwnd); } break; } case WM_ACTIVATE: { // // When activating, kill timer. When deactivating, start // timer. The theory is, there's nothing else going on when we // are active, so why poll for updates? On sharing state // changes, we update the list anyway. // if (IsWindowVisible(hwnd)) { if (wParam) { KillTimer(hwnd, IDT_REFRESH); HOST_FillList(hwnd); } else { SetTimer(hwnd, IDT_REFRESH, PERIOD_REFRESH, 0); } } break; } // // Private communication messages // case HOST_MSG_CALL: { HOST_OnCall(hwnd, (wParam != FALSE)); break; } case HOST_MSG_OPEN: { // // If we are temporarily hidden, ignore all open requests. // if (!g_asSession.fHostUIFrozen) { if (!IsWindowVisible(hwnd)) { // // Note, we may end up updating the list twice, once here // and once under activation. HOST_FillList(hwnd); ShowWindow(hwnd, SW_SHOW); g_asSession.fHostUI = TRUE; } if (IsIconic(hwnd)) SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); SetForegroundWindow(hwnd); } break; } case WM_CLOSE: case HOST_MSG_CLOSE: { if (IsWindowVisible(hwnd)) { // // Hiding the window will deactivate it. Deactivating it // will kick off timer. So kill timer afterwards. // ShowWindow(hwnd, SW_HIDE); KillTimer(hwnd, IDT_REFRESH); g_asSession.fHostUI = FALSE; } break; } case HOST_MSG_UPDATELIST: { // // We only do list stuff when the UI is up. // if (IsWindowVisible(hwnd)) { HOST_FillList(hwnd); // // If timer is on, reset it. This is for case where you // are hosting but this UI window is up in the background. // There's no point in overlapping the updates. We want the // list to update every time there's a top level shared // window change OR PERIOD_REFRESH milliseconds have elapsed // without a change. // if (hwnd != GetActiveWindow()) { SetTimer(hwnd, IDT_REFRESH, PERIOD_REFRESH, 0); } } break; } case HOST_MSG_HOSTSTART: { HOST_OnSharing(hwnd, TRUE); break; } case HOST_MSG_HOSTEND: { HOST_OnSharing(hwnd, FALSE); break; } case HOST_MSG_ALLOWCONTROL: { HOST_OnControllable(hwnd, (wParam != 0)); break; } case HOST_MSG_CONTROLLED: { if (wParam) { // // Hide the window temporarily // ASSERT(!g_asSession.fHostUIFrozen); g_asSession.fHostUIFrozen = TRUE; if (IsWindowVisible(hwnd)) { ASSERT(g_asSession.fHostUI); SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW); } } else { // // Put the window back in the state it was // if (g_asSession.fHostUIFrozen) { g_asSession.fHostUIFrozen = FALSE; if (g_asSession.fHostUI) { SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); } } } break; } default: rc = FALSE; break; } DebugExitBOOL(HostDlgProc, rc); return(rc); } // // HOST_InitDialog() // // Initializes the host UI dialog // void HOST_InitDialog(HWND hwnd) { HMENU hMenu; char szText[128]; MENUITEMINFO mi; DebugEntry(HOST_InitDialog); // Set title text HOST_UpdateTitle(hwnd, IDS_NOTINCALL); // // Set window icon // SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)g_hetASIcon); // // Update system menu // hMenu = GetSystemMenu(hwnd, FALSE); EnableMenuItem(hMenu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); // Append separator, always on top to system menu ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIIM_TYPE; mi.fType = MFT_SEPARATOR; InsertMenuItem(hMenu, -1, TRUE, &mi); mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE; mi.fType = MFT_STRING; mi.fState = MFS_ENABLED; mi.wID = CMD_TOPMOST; LoadString(g_asInstance, IDS_TOPMOST, szText, sizeof(szText)); mi.dwTypeData = szText; mi.cch = lstrlen(szText); InsertMenuItem(hMenu, -1, TRUE, &mi); // // Enable/disable true color sharing control. If a policy prevents it // or our screen depth isn't capable, disable it. // HOST_EnableCtrl(hwnd, CTRL_ENABLETRUECOLOR_CHECK, ((g_usrScreenBPP >= 24) && !(g_asPolicies & SHP_POLICY_NOTRUECOLOR))); // // Get text, control buttons set. // HOST_OnControllable(hwnd, TRUE); HOST_OnControllable(hwnd, FALSE); DebugExitVOID(HOST_InitDialog); } // // HOST_UpdateTitle() // // Updates title bar of hosting UI // void HOST_UpdateTitle(HWND hwnd, UINT idState) { char szText[64]; char szFormat[128]; char szTitle[192]; DebugEntry(HOST_UpdateTitle); LoadString(g_asInstance, IDS_SHARING_FORMAT, szFormat, sizeof(szFormat)); LoadString(g_asInstance, idState, szText, sizeof(szText)); wsprintf(szTitle, szFormat, szText); SetWindowText(hwnd, szTitle); DebugExitVOID(HOST_UpdateTitle); } // // HOST_OnCall() // // Handles call start/stop // void HOST_OnCall(HWND hwnd, BOOL fCall) { DebugEntry(HOST_OnCall); // Update title bar HOST_UpdateTitle(hwnd, (fCall ? IDS_NOTHING : IDS_NOTINCALL)); HOST_EnableCtrl(hwnd, CTRL_PROGRAM_LIST, fCall); if (IsWindowVisible(hwnd)) { SendMessage(hwnd, HOST_MSG_UPDATELIST, 0, 0); } DebugExitVOID(HOST_OnCall); } // // HOST_OnSharing() // // Handles sharing start/stop // void HOST_OnSharing(HWND hwnd, BOOL fSharing) { DebugEntry(HOST_OnSharing); // Update title bar if (fSharing) { HOST_UpdateTitle(hwnd, (g_asSession.pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED) ? IDS_DESKTOP : IDS_PROGRAMS); } else { HOST_UpdateTitle(hwnd, IDS_NOTHING); } // // The ctrl button should always be Allow. When we stop hosting, we turn // off allowing control first. // if (!(g_asPolicies & SHP_POLICY_NOCONTROL)) { HOST_EnableCtrl(hwnd, CTRL_ALLOWCONTROL_BTN, fSharing); } HOST_EnableCtrl(hwnd, CTRL_UNSHAREALL_BTN, fSharing); if ((g_usrScreenBPP >= 24) && !(g_asPolicies & SHP_POLICY_NOTRUECOLOR)) { // // Only dynamically change this checkbox if true color is available. // HOST_EnableCtrl(hwnd, CTRL_ENABLETRUECOLOR_CHECK, !fSharing); } DebugExitVOID(HOST_OnSharing); } // // HOST_OnControllable() // // Updates the blurb, button text, and button ID when the controllable // state changes. // void HOST_OnControllable(HWND hwnd, BOOL fControllable) { HWND hwndBtn; TCHAR szText[256]; DebugEntry(HOST_OnControllable); // Control blurb LoadString(g_asInstance, (fControllable ? IDS_MSG_TOPREVENTCONTROL : IDS_MSG_TOALLOWCONTROL), szText, sizeof(szText)); SetDlgItemText(hwnd, CTRL_CONTROL_MSG, szText); // Control button if (fControllable) { hwndBtn = GetDlgItem(hwnd, CTRL_ALLOWCONTROL_BTN); ASSERT(hwndBtn); SetWindowLong(hwndBtn, GWL_ID, CTRL_PREVENTCONTROL_BTN); LoadString(g_asInstance, IDS_PREVENTCONTROL, szText, sizeof(szText)); } else { hwndBtn = GetDlgItem(hwnd, CTRL_PREVENTCONTROL_BTN); ASSERT(hwndBtn); SetWindowLong(hwndBtn, GWL_ID, CTRL_ALLOWCONTROL_BTN); LoadString(g_asInstance, IDS_ALLOWCONTROL, szText, sizeof(szText)); } SetWindowText(hwndBtn, szText); // Enable/disable the control checkboxes, make sure they start unchecked. HOST_EnableCtrl(hwnd, CTRL_TEMPREJECTCONTROL_CHECK, fControllable); CheckDlgButton(hwnd, CTRL_TEMPREJECTCONTROL_CHECK, FALSE); HOST_EnableCtrl(hwnd, CTRL_AUTOACCEPTCONTROL_CHECK, fControllable); CheckDlgButton(hwnd, CTRL_AUTOACCEPTCONTROL_CHECK, FALSE); DebugExitVOID(HOST_OnControllable); } // // HOST_FillList() // // Fills the contents of the shared/unshared applications list // void HOST_FillList(HWND hwnd) { IAS_HWND_ARRAY * pArray; int iItem; PHOSTITEM pItem; char szText[80]; UINT iWnd; HICON hIcon; BOOL fAppsAvailable; HWND hwndSelect; int iSelect; int iTop; int cxExtent; RECT rc; HFONT hfnT; HFONT hfnControl; HDC hdc; // // For the common case, remember what was selected and try to put that // back. // // Save current top index iTop = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETTOPINDEX, 0, 0); // Save currently selected item hwndSelect = HWND_BOTTOM; iSelect = -1; iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETCURSEL, 0, 0); if (iItem != -1) { pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETITEMDATA, iItem, 0); if (pItem) { hwndSelect = pItem->hwnd; } } // // Turn off redraw and clear the apps list. // SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, WM_SETREDRAW, FALSE, 0); SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_RESETCONTENT, 0, 0); // // We're going to calculate the horizontal extent since ownerdraw // lists can't do that. // hdc = GetDC(hwnd); hfnControl = (HFONT)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, WM_GETFONT, 0, 0); cxExtent = 0; // // HET_GetAppsList() will fail if there's not enough memory to allocate // the array. If we really can't allocate it, why add an item for the // desktop? // if (HET_GetAppsList(&pArray)) { ASSERT(pArray); fAppsAvailable = TRUE; // // If desktop sharing is permitted, add desktop item. // if (!(g_asPolicies & SHP_POLICY_NODESKTOPSHARE)) { pItem = new HOSTITEM; if (!pItem) { ERROR_OUT(("Unable to alloc HOSTITEM for listbox")); } else { pItem->hwnd = GetDesktopWindow(); pItem->hIcon = g_hetDeskIconSmall; LoadString(g_asInstance, IDS_DESKTOP, szText, sizeof(szText)); pItem->fShared = (HET_IsWindowShared(pItem->hwnd) != FALSE); if (pItem->fShared) { // // When everything (the desktop) is shared, sharing // individual apps makes no sense. We keep them in the // list but draw them unavailable, same as if the list // itself were completely disabled. // fAppsAvailable = FALSE; pItem->fAvailable = TRUE; } else if (!pArray->cShared && g_asSession.callID && (g_asSession.attendeePermissions & NM_PERMIT_SHARE)) { // // No apps are shared, the desktop item is available. // pItem->fAvailable = TRUE; } else { // // Apps are shared, sharing the entire desktop makes no // sense. // pItem->fAvailable = FALSE; } iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_ADDSTRING, 0, (LPARAM)szText); if (iItem == -1) { ERROR_OUT(("Couldn't append item to list")); delete pItem; } else { SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETITEMDATA, iItem, (LPARAM)pItem); // // Calculate width. // hfnT = SelectFont(hdc, (pItem->fShared ? g_hetSharedFont : hfnControl)); SetRectEmpty(&rc); DrawText(hdc, szText, lstrlen(szText), &rc, DT_LEFT | DT_VCENTER | DT_EXTERNALLEADING | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT); SelectFont(hdc, hfnT); rc.right -= rc.left; cxExtent = max(cxExtent, rc.right); // // If this desktop item were selected last time, // remember so we select it again after. // if (pItem->hwnd == hwndSelect) iSelect = iItem; } } } // // Add items for apps. // for (iWnd = 0; iWnd < pArray->cEntries; iWnd++) { hIcon = NULL; if (pArray->aEntries[iWnd].hwnd == HWND_BROADCAST) { LoadString(g_asInstance, IDS_HIDDEN_WINDOW, szText, sizeof(szText)); hIcon = g_hetASIconSmall; } else { GetWindowText(pArray->aEntries[iWnd].hwnd, szText, sizeof(szText)); if (!szText[0]) continue; // Try to get window small icon SendMessageTimeout(pArray->aEntries[iWnd].hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_NORMAL, 1000, (DWORD_PTR*)&hIcon); if (!hIcon) { hIcon = (HICON)GetClassLongPtr(pArray->aEntries[iWnd].hwnd, GCLP_HICON); } // // Make a copy of the small icon, we can't just hang on to // the application's, it could go away. // if (hIcon) { hIcon = (HICON)CopyImage(hIcon, IMAGE_ICON, 0, 0, 0); } if (!hIcon) { hIcon = g_hetASIconSmall; } } // // Add item to list // pItem = new HOSTITEM; if (!pItem) { ERROR_OUT(("Unable to alloc HOSTITEM for listbox")); } else { pItem->hwnd = pArray->aEntries[iWnd].hwnd; pItem->hIcon = hIcon; pItem->fShared = pArray->aEntries[iWnd].fShared; pItem->fAvailable = g_asSession.callID && (g_asSession.attendeePermissions & NM_PERMIT_SHARE) && (fAppsAvailable != FALSE); iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_ADDSTRING, 0, (LPARAM)szText); if (iItem == -1) { ERROR_OUT(("Couldn't append item to list")); delete pItem; } else { SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETITEMDATA, iItem, (LPARAM)pItem); // // Calculate width. // hfnT = SelectFont(hdc, (pItem->fShared ? g_hetSharedFont : hfnControl)); SetRectEmpty(&rc); DrawText(hdc, szText, lstrlen(szText), &rc, DT_LEFT | DT_VCENTER | DT_EXTERNALLEADING | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT); SelectFont(hdc, hfnT); rc.right -= rc.left; cxExtent = max(cxExtent, rc.right); } // // If this app item were selected before, remember so we // select it again when done. // if (pItem->hwnd == hwndSelect) iSelect = iItem; } } HET_FreeAppsList(pArray); } ReleaseDC(hwnd, hdc); // // Set cur sel, top index, update buttons // SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETTOPINDEX, iTop, 0); SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETCURSEL, iSelect, 0); HOST_OnSelChange(hwnd); // // Turn on redraw and repaint // SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, WM_SETREDRAW, TRUE, 0); // // Set horizontal extent // if (cxExtent) { // Add on space for checkmark, icons cxExtent += GetSystemMetrics(SM_CXMENUCHECK) + GetSystemMetrics(SM_CXSMICON) + 3*GetSystemMetrics(SM_CXEDGE); } SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_SETHORIZONTALEXTENT, cxExtent, 0); InvalidateRect(GetDlgItem(hwnd, CTRL_PROGRAM_LIST), NULL, TRUE); UpdateWindow(GetDlgItem(hwnd, CTRL_PROGRAM_LIST)); DebugExitVOID(HOST_FillList); } // // HOST_MeasureItem() // // Calculates height of ownerdraw items in host list // BOOL HOST_MeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmi) { BOOL rc = FALSE; UINT cy; TEXTMETRIC tm; HDC hdc; HFONT hfnT; DebugEntry(HOST_MeasureItem); if (lpmi->CtlID != CTRL_PROGRAM_LIST) { // Not for us DC_QUIT; } // Get height of bolded font hdc = GetDC(hwnd); hfnT = SelectFont(hdc, g_hetSharedFont); GetTextMetrics(hdc, &tm); SelectFont(hdc, hfnT); ReleaseDC(hwnd, hdc); // // Height is max of default height (height of char in font), // checkmark height, and small icon height, plus dotted rect. // cy = (UINT)tm.tmHeight; lpmi->itemHeight = max(lpmi->itemHeight, cy); cy = (UINT)GetSystemMetrics(SM_CYMENUCHECK); lpmi->itemHeight = max(lpmi->itemHeight, cy); cy = (UINT)GetSystemMetrics(SM_CYSMICON); lpmi->itemHeight = max(lpmi->itemHeight, cy); lpmi->itemHeight += GetSystemMetrics(SM_CYEDGE); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(HOST_MeasureItem, rc); return(rc); } // // HOST_DeleteItem() // // Cleans up after an item is deleted from the list. // BOOL HOST_DeleteItem(HWND hwnd, LPDELETEITEMSTRUCT lpdi) { PHOSTITEM pItem; BOOL rc = FALSE; DebugEntry(HOST_DeleteItem); if (lpdi->CtlID != CTRL_PROGRAM_LIST) { DC_QUIT; } pItem = (PHOSTITEM)lpdi->itemData; if (!pItem) { // // NT 4.x has a terrible bug where the item data is not passed // in the DELETEITEMSTRUCT always. So try to obtain it if not. // pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETITEMDATA, lpdi->itemID, 0); } if (pItem) { if ((pItem->hIcon != g_hetASIconSmall) && (pItem->hIcon != g_hetDeskIconSmall)) { DestroyIcon(pItem->hIcon); } delete pItem; } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(HOST_DeleteItem, rc); return(rc); } // // HOST_DrawItem() // // Draws list item // BOOL HOST_DrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdi) { COLORREF clrBk; COLORREF clrText; HBRUSH hbr; HFONT hfnT; RECT rcItem; char szText[80]; PHOSTITEM pItem; BOOL rc = FALSE; if (lpdi->CtlID != CTRL_PROGRAM_LIST) { DC_QUIT; } pItem = (PHOSTITEM)lpdi->itemData; if (!pItem) { // Empty item for focus rc = TRUE; DC_QUIT; } rcItem = lpdi->rcItem; // // Set up colors // if (!pItem->fAvailable) { // No selection color clrBk = GetSysColor(COLOR_WINDOW); hbr = GetSysColorBrush(COLOR_WINDOW); clrText = GetSysColor(COLOR_GRAYTEXT); } else if (lpdi->itemState & ODS_SELECTED) { clrBk = GetSysColor(COLOR_HIGHLIGHT); hbr = GetSysColorBrush(COLOR_HIGHLIGHT); clrText = GetSysColor(COLOR_HIGHLIGHTTEXT); } else { clrBk = GetSysColor(COLOR_WINDOW); hbr = GetSysColorBrush(COLOR_WINDOW); clrText = GetSysColor(COLOR_WINDOWTEXT); } SetBkColor(lpdi->hDC, clrBk); SetTextColor(lpdi->hDC, clrText); // Erase background FillRect(lpdi->hDC, &rcItem, hbr); // Focus rect if (lpdi->itemState & ODS_FOCUS) { DrawFocusRect(lpdi->hDC, &rcItem); } rcItem.left += GetSystemMetrics(SM_CXEDGE); InflateRect(&rcItem, 0, -GetSystemMetrics(SM_CYBORDER)); // // Draw checkmark and select bolded font // if (pItem->fShared) { HDC hdcT; HBITMAP hbmpOld; hdcT = CreateCompatibleDC(lpdi->hDC); hbmpOld = SelectBitmap(hdcT, g_hetCheckBitmap); SetTextColor(hdcT, clrText); SetBkColor(hdcT, clrBk); BitBlt(lpdi->hDC, rcItem.left, (rcItem.top + rcItem.bottom - GetSystemMetrics(SM_CYMENUCHECK)) / 2, GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), hdcT, 0, 0, SRCCOPY); SelectBitmap(hdcT, hbmpOld); DeleteDC(hdcT); hfnT = SelectFont(lpdi->hDC, g_hetSharedFont); } rcItem.left += GetSystemMetrics(SM_CXMENUCHECK) + GetSystemMetrics(SM_CXEDGE); // Draw icon, centered vertically DrawIconEx(lpdi->hDC, rcItem.left, (rcItem.top + rcItem.bottom - GetSystemMetrics(SM_CYSMICON)) /2, pItem->hIcon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0, NULL, DI_NORMAL); rcItem.left += GetSystemMetrics(SM_CXSMICON) + GetSystemMetrics(SM_CXEDGE); // // Draw the text // szText[0] = 0; SendMessage(lpdi->hwndItem, LB_GETTEXT, lpdi->itemID, (LPARAM)szText); DrawText(lpdi->hDC, szText, lstrlen(szText), &rcItem, DT_LEFT | DT_VCENTER | DT_EXTERNALLEADING | DT_NOPREFIX | DT_SINGLELINE); // // Deselect bolded shared font // if (pItem->fShared) { SelectFont(lpdi->hDC, hfnT); } rc = TRUE; DC_EXIT_POINT: return(rc); } // // HOST_ChangeShareState() // // Changes the sharing state of the currently selected item. // void HOST_ChangeShareState(HWND hwnd, UINT action) { int iItem; PHOSTITEM pItem; HWND hwndChange; HCURSOR hcurT; DebugEntry(HOST_ChangeShareState); if (action == CHANGE_ALLUNSHARED) { hwndChange = HWND_BROADCAST; action = CHANGE_UNSHARED; goto ChangeState; } iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETCURSEL, 0, 0); if (iItem != -1) { pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETITEMDATA, iItem, 0); if (pItem && pItem->fAvailable) { hwndChange = pItem->hwnd; if (action == CHANGE_TOGGLE) { if (HET_IsWindowShared(hwndChange)) { action = CHANGE_UNSHARED; } else { action = CHANGE_SHARED; } } ChangeState: ASSERT((action == CHANGE_SHARED) || (action == CHANGE_UNSHARED)); // // Set wait cursor // hcurT = SetCursor(LoadCursor(NULL, IDC_WAIT)); if (action == CHANGE_SHARED) { DCS_Share(hwndChange, IAS_SHARE_DEFAULT); } else { DCS_Unshare(hwndChange); } // // Set wait cursor // SetCursor(hcurT); } } DebugExitVOID(HOST_ChangeShareState); } // // HOST_OnSelChange() // // Handles a selection change in the task list. We enable/disable // buttons as appropriate, depending on whether item is available. // void HOST_OnSelChange(HWND hwnd) { int iItem; PHOSTITEM pItem; BOOL fShareBtn = FALSE; BOOL fUnshareBtn = FALSE; DebugEntry(HOST_OnSelChange); // // Get current selection, and decide what to do based off that. // iItem = (int)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETCURSEL, 0, 0); if (iItem != -1) { pItem = (PHOSTITEM)SendDlgItemMessage(hwnd, CTRL_PROGRAM_LIST, LB_GETITEMDATA, iItem, 0); if (pItem) { if (pItem->fShared) { fUnshareBtn = TRUE; } else if (pItem->fAvailable) { ASSERT(g_asSession.callID); fShareBtn = TRUE; } } } HOST_EnableCtrl(hwnd, CTRL_UNSHARE_BTN, fUnshareBtn); HOST_EnableCtrl(hwnd, CTRL_SHARE_BTN, fShareBtn); DebugExitVOID(HOST_OnSelChange); } // // HOST_EnableCtrl() // // This enables/disables the child control. If disabling, and this control // used to have the focus, we make sure the dialog resets the focus control // so the keyboard keeps working. We know that the Close button is always // available, so this won't die. // void HOST_EnableCtrl ( HWND hwnd, UINT ctrl, BOOL fEnable ) { HWND hwndCtrl; DebugEntry(HOST_EnableCtrl); hwndCtrl = GetDlgItem(hwnd, ctrl); ASSERT(hwndCtrl); if (fEnable) { EnableWindow(hwndCtrl, TRUE); } else { if (GetFocus() == hwndCtrl) { // Advance the focus SendMessage(hwnd, WM_NEXTDLGCTL, 0, 0); } EnableWindow(hwndCtrl, FALSE); } DebugExitVOID(HOST_EnableCtrl); }