#include "precomp.h" // // AWC.CPP // Active Window Coordinator // // Copyright(c) Microsoft 1997- // #define MLZ_FILE_ZONE ZONE_CORE // // The AWC code does three things: // * Notifies everybody in the share what the current active window // is when sharing (either a shared window or something else) // * When in control, requests to restore/activate a remote's shared // window // * When being controlled, handles request to restore/activate // a local hosted window // // // For the purposes of this strategy the AWC packets can be split into two // categories. // // 1. Immediate - these are the packets which are generated when a shadow // window is controlled by some means other than direct keyboard or mouse // input to the shadow window (which is all sent to the host system and // handled there). Examples include the Task List, window switching // (Alt-TAB, Alt-Esc etc), minimising or closing another app which may pass // activation on to a shadow window etc. The packets in this category are: // // AWC_MSG_ACTIVATE_WINDOW // AWC_MSG_RESTORE_WINDOW // // These packets can be (and are) sent immediately that the event happens. // This is because they always refer to real windows on the host system. // // 2. Periodic - these are the packets sent when the AWC detects that the // active window has changed locally and it should inform the remote AWC. // This packet is sent when AWC_Periodic is called. The packets in this // category are: // // AWC_MSG_ACTIVE_CHANGE_SHARED // AWC_MSG_ACTIVE_CHANGE_LOCAL // AWC_MSG_ACTIVE_CHANGE_INVISIBLE // // These are only sent when AWC_Periodic is called because they may refer // to shadow windows and therefore we avoid sending it until we know that // the SWL has succesfully sent a window structure which includes the // window referenced in the message. // // For packets in the first category we will queue up to two packets and at // the point where we have three packets we cannot send we will discard // packets from the front of the queue so that a users most recent actions // have priority over any previous actions. We will try to send all queued // packets whenever a new category 1 packet is generated and on the // AWC_Periodic call. // // For packets in the second category we will drop the packets if we cannot // send them but remember that we have failed to send a packet and retry on // the next call to AWC_Periodic. This is not the same as queueing as the // active window may change between us dropping a packet and being able to // send the next packet from AWC_Periodic. Queuing the dropped packet // would have been pointless as it would now be out of date. // // All AWC packets go on the same stream (the updates stream) so that they // are guaranteed to arrive in the same order as they are generated to // prevent a AWC_MSG_ACTIVE_CHANGE_XXX being overtaken by an // AWC_MSG_ACTIVATE_WINDOW and therefore the effect of // AWC_MSG_ACTIVATE_WINDOW being overridden by the // AWC_MSG_ACTIVE_CHANGE_XXX. // // // AWC_ReceivedPacket() // void ASShare::AWC_ReceivedPacket ( ASPerson * pasPerson, PS20DATAPACKET pPacket ) { PAWCPACKET pAWCPacket; UINT activateWhat; HWND hwnd; DebugEntry(ASShare::AWC_ReceivedPacket); ValidatePerson(pasPerson); pAWCPacket = (PAWCPACKET)pPacket; // // We trace the person ID out here so we don't bother to do it // elsewhere in this function on TRACE lines. // TRACE_OUT(("AWC_ReceivedPacket from [%d] - msg %x token %u data 0x%08x", pasPerson->mcsID, pAWCPacket->msg, pAWCPacket->token, pAWCPacket->data1)); if (AWC_IS_INDICATION(pAWCPacket->msg)) { // // We should simply change the view of the remote. // if (pasPerson->awcActiveWinID != pAWCPacket->data1) { pasPerson->awcActiveWinID = pAWCPacket->data1; if (pasPerson->m_pView) { // Update the pressed item on the window bar. VIEW_WindowBarChangedActiveWindow(pasPerson); } } } else if (AWC_MSG_SAS == pAWCPacket->msg) { // // Cause Ctrl+Alt+Del to be injected if we're in a service app, // we're hosting, and we're controlled by the sender. // if ((g_asOptions & AS_SERVICE) && (pasPerson->m_caInControlOf == m_pasLocal)) { ASSERT(m_pHost); OSI_InjectCtrlAltDel(); } } else { hwnd = (HWND)pAWCPacket->data1; // // Only accept requests if we're being controlled currently by // this person. We might get renegade packets from remotes that // don't yet they aren't in control, or from back-level systems. // if (pasPerson->m_caInControlOf != m_pasLocal) { // We're not controlled by this person DC_QUIT; } ASSERT(m_pHost); if ((pAWCPacket->msg == AWC_MSG_ACTIVATE_WINDOW) && IsWindow(hwnd) && IsWindowEnabled(hwnd)) { // Ony get owned window if enabled and we're activating. hwnd = GetLastActivePopup(hwnd); } if (IsWindow(hwnd) && HET_WindowIsHosted(hwnd) && IsWindowEnabled(hwnd)) { switch (pAWCPacket->msg) { case AWC_MSG_ACTIVATE_WINDOW: // // Activate the window. // TRACE_OUT(("Received AWC_MSG_ACTIVATE_WINDOW for hwnd 0x%08x from [%d]", hwnd, pasPerson->mcsID)); m_pHost->AWC_ActivateWindow(hwnd); break; case AWC_MSG_RESTORE_WINDOW: // // Restore the window // TRACE_OUT(("Received AWC_MSG_RESTORE_WINDOW for hwnd 0x%08x from [%d]", hwnd, pasPerson->mcsID)); if (IsIconic(hwnd)) { PostMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); } break; default: WARNING_OUT(("Received invalid msg %d from [%d]", pAWCPacket->msg, pasPerson->mcsID)); break; } } } DC_EXIT_POINT: DebugExitVOID(ASShare::AWC_ReceivedPacket); } // // AWC_Periodic() // void ASHost::AWC_Periodic(void) { HWND currentActiveWindow; HWND sendActiveWindow; TSHR_UINT16 sendMsg; DebugEntry(ASHost::AWC_Periodic); // // If we are hosting the desktop, skip this. // if (m_pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED) { // Skip. DC_QUIT; } // // Find the current active window. // if (SWL_IsOurDesktopActive()) { currentActiveWindow = GetForegroundWindow(); } else { // Another desktop is up. currentActiveWindow = NULL; } if (m_pShare->HET_WindowIsHosted(currentActiveWindow)) { // // A window which belongs to shared application is active - // find out if it is visible. // if (IsWindowVisible(currentActiveWindow)) { // // The active window is also visible - this means the // remote system will know about it as it will have // been sent in a preceding SWL message. // sendMsg = AWC_MSG_ACTIVE_CHANGE_SHARED; sendActiveWindow = SWL_GetSharedIDFromLocalID(currentActiveWindow); } else { // // The active window is invisible - this means that // although it is shared the remote system will not // know about it. Send a message to inform the remote // system about this. // sendMsg = AWC_MSG_ACTIVE_CHANGE_INVISIBLE; sendActiveWindow = 0; } } else { // // A local application has been activated send // AWC_ACTIVE_WINDOW_LOCAL. // sendMsg = AWC_MSG_ACTIVE_CHANGE_LOCAL; sendActiveWindow = 0; } // // Now send the packet if it's not the same as the last packet we // sent. NOTE that for local unshared windows, we don't care if // we've deactivated one and activated another, they are generic. So // we send a message if we // * change activation from a shared window // * change activation to a shared window // if ((sendActiveWindow != m_awcLastActiveWindow) || (sendMsg != m_awcLastActiveMsg)) { // // Note that this packet is sent on the updates stream so that it // cannot overtake a SWL packet containing the newly active window. // TRACE_OUT(("Broadcasting AWC change msg %x, hwnd 0x%08x", sendMsg, sendActiveWindow)); if (m_pShare->AWC_SendMsg(g_s20BroadcastID, sendMsg, HandleToUlong(sendActiveWindow), 0)) { // // The packet was sent succesfully - remember which window we // sent. // m_awcLastActiveWindow = sendActiveWindow; m_awcLastActiveMsg = sendMsg; } else { // // The packet could not be sent for some reason - set // m_awcLastActiveWindow to invalid so that we will try again // on the next call to AWC_Periodic. // m_awcLastActiveWindow = AWC_INVALID_HWND; m_awcLastActiveMsg = AWC_MSG_INVALID; } } DC_EXIT_POINT: DebugExitVOID(ASHost::AWC_Periodic); } // // AWC_SyncOutgoing() // void ASHost::AWC_SyncOutgoing(void) { DebugEntry(ASHost::AWC_SyncOutgoing); // // Ensure that we resend an indication message as soon as possible // m_awcLastActiveWindow = AWC_INVALID_HWND; m_awcLastActiveMsg = AWC_MSG_INVALID; DebugExitVOID(ASHost::AWC_SyncOutgoing); } // // FUNCTION: AWC_SendMsg // // DESCRIPTION: // // Sends a AWC message to remote system // * Requests to activate are just to one host // * Notifications of activation are to everyone // // RETURNS: TRUE or FALSE - success or failure // // BOOL ASShare::AWC_SendMsg ( UINT_PTR nodeID, UINT msg, UINT_PTR data1, UINT_PTR data2 ) { PAWCPACKET pAWCPacket; BOOL rc = FALSE; #ifdef _DEBUG UINT sentSize; #endif DebugEntry(ASShare::AWC_SendMsg); // // Allocate correct sized packet. // pAWCPacket = (PAWCPACKET)SC_AllocPkt(PROT_STR_UPDATES, nodeID, sizeof(AWCPACKET)); if (!pAWCPacket) { WARNING_OUT(("Failed to alloc AWC packet")); DC_QUIT; } // // Set up the data header for an AWC message. // pAWCPacket->header.data.dataType = DT_AWC; // // Now set up the AWC fields. By passing AWC_SYNC_MSG_TOKEN in the // token field, we ensure that back-level remotes will never drop our // packets. // pAWCPacket->msg = (TSHR_UINT16)msg; pAWCPacket->data1 = data1; pAWCPacket->data2 = data2; pAWCPacket->token = AWC_SYNC_MSG_TOKEN; // // Send the packet. // if (m_scfViewSelf) AWC_ReceivedPacket(m_pasLocal, &(pAWCPacket->header)); #ifdef _DEBUG sentSize = #endif // _DEBUG DCS_CompressAndSendPacket(PROT_STR_UPDATES, nodeID, &(pAWCPacket->header), sizeof(*pAWCPacket)); TRACE_OUT(("AWC packet size: %08d, sent: %08d", sizeof(*pAWCPacket), sentSize)); rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASShare::AWC_SendMsg, rc); return(rc); } // // AWC_ActivateWindow() // // Activates a shared window via a remote controller's request. // void ASHost::AWC_ActivateWindow(HWND window) { BOOL rcSFW; HWND hwndForeground; DebugEntry(ASHost::AWC_ActivateWindow); if (!IsWindow(window)) { WARNING_OUT(( "Trying to activate invalid window %08x", window)); DC_QUIT; } // // SetForegroundWindow appears to be asynchronous. That is, it may // return successfully here but immediately querying the active // window does not always reveal the newly set foreground window to // be active. // rcSFW = SetForegroundWindow(window); hwndForeground = GetForegroundWindow(); if (hwndForeground != window) { // // If a Screen Saver is active then it always refuses to let us // activate another window. Trace an alert if the call to // SetForegroundWindow, above, failed. // if (OSI_IsWindowScreenSaver(hwndForeground) || (m_swlCurrentDesktop != DESKTOP_OURS)) { WARNING_OUT(("Screen Saver or other desktop is up, failed to activate window 0x%08x", window)); } else if ( !rcSFW ) { // // The active window is not the one we set because // SetForegroundWindow failed. // WARNING_OUT(("Failed to activate window 0x%08x", window)); } // // We have apparently failed to set the active window, but // SetForegroundWindow succeeded. This is probably just a lag in // Windows getting up to date, so continue as if all is normal. // } // // Whether we succeeded or failed, make sure we broadcast the current // active window. // m_awcLastActiveWindow = AWC_INVALID_HWND; m_awcLastActiveMsg = AWC_MSG_INVALID; DC_EXIT_POINT: DebugExitVOID(ASHost::AWC_ActivateWindow); }