479 lines
14 KiB
C++
479 lines
14 KiB
C++
|
#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);
|
||
|
}
|
||
|
|
||
|
|