2904 lines
92 KiB
C++
2904 lines
92 KiB
C++
#include "precomp.h"
|
||
|
||
|
||
//
|
||
// SWL.CPP
|
||
// Shared Window List
|
||
//
|
||
// Copyright(c) Microsoft 1997-
|
||
//
|
||
|
||
#define MLZ_FILE_ZONE ZONE_CORE
|
||
|
||
//
|
||
// SWL strategy when network packets are not available
|
||
//
|
||
// The SWL only sends one type of message - the window structure message.
|
||
// When no network packets are available the SWL will drop its current
|
||
// packet and remember that the window structure has changed since it was
|
||
// last able to send a packet. SWL_Periodic will also return FALSE when
|
||
// this happens so that the DCS will know not to send any updates if it
|
||
// failed to send a window structure.
|
||
//
|
||
// This pending of window structure messages is integrated with the
|
||
// ignore envelopes where the SWL wants to ignore changes caused by itself
|
||
// (or other components if they call the SWL_Begin/EndIgnoreWindowChanges
|
||
// functions).
|
||
//
|
||
|
||
//
|
||
// SWL strategy for backward compatibility.
|
||
//
|
||
// The differences between the R2.0 and 3.0 SWL protocol are:
|
||
// 1. Tokenless packets.
|
||
// 2. No shadows.
|
||
//
|
||
|
||
|
||
|
||
|
||
//
|
||
// SWL_PartyLeftShare()
|
||
//
|
||
void ASShare::SWL_PartyLeftShare(ASPerson * pasPerson)
|
||
{
|
||
DebugEntry(ASShare::SWL_PartyLeftShare);
|
||
|
||
ValidatePerson(pasPerson);
|
||
|
||
//
|
||
// 2.x nodes will fake up a packet for a remote leaving with an empty
|
||
// window list. That's how they'd nuke shadows for that person, if he
|
||
// had been hosting. In so doing, they'd use a new token. We need to
|
||
// bump our token value up also so that the next window list we send
|
||
// is not dropped.
|
||
//
|
||
m_swlLastTokenSeen = SWL_CalculateNextToken(m_swlLastTokenSeen);
|
||
TRACE_OUT(("SWL_PartyLeftShare: bumped up token to 0x%08x", m_swlLastTokenSeen));
|
||
|
||
DebugExitVOID(ASShare::SWL_PartyLeftShare);
|
||
}
|
||
|
||
|
||
//
|
||
// SWL_SyncOutgoing
|
||
//
|
||
void ASHost::SWL_SyncOutgoing(void)
|
||
{
|
||
DebugEntry(ASHost::SWL_SyncOutgoing);
|
||
|
||
//
|
||
// Ensure that we send an SWL packet next time we need.
|
||
//
|
||
m_swlfForceSend = TRUE;
|
||
m_swlfSyncing = TRUE;
|
||
|
||
DebugExitVOID(ASHost::SWL_SyncOutgoing);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// SWL_HostStarting()
|
||
//
|
||
BOOL ASHost::SWL_HostStarting(void)
|
||
{
|
||
BOOL rc = FALSE;
|
||
|
||
DebugEntry(ASHost::SWL_HostStarting);
|
||
|
||
//
|
||
// Get an atom to use in getting and setting window properties (which
|
||
// will give us SWL information about the window).
|
||
//
|
||
m_swlPropAtom = GlobalAddAtom(SWL_ATOM_NAME);
|
||
if (!m_swlPropAtom)
|
||
{
|
||
ERROR_OUT(( "GlobalAddAtom error %#x", GetLastError()));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// If this is NT, get the name of our startup desktop
|
||
//
|
||
if (!g_asWin95)
|
||
{
|
||
ASSERT(m_aswlOurDesktopName[0] == 0);
|
||
GetUserObjectInformation(GetThreadDesktop(g_asMainThreadId),
|
||
UOI_NAME, m_aswlOurDesktopName,
|
||
sizeof(m_aswlOurDesktopName), NULL);
|
||
|
||
TRACE_OUT(("Our desktop name is %s", m_aswlOurDesktopName));
|
||
}
|
||
|
||
if (!m_aswlOurDesktopName[0])
|
||
{
|
||
// Use default name
|
||
TRACE_OUT(("Couldn't get desktop name; using %s",
|
||
NAME_DESKTOP_DEFAULT));
|
||
lstrcpy(m_aswlOurDesktopName, NAME_DESKTOP_DEFAULT);
|
||
}
|
||
|
||
//
|
||
// Allocate memory for the window titles. We fix the maximum size of
|
||
// window title we will send - task list doesn't scroll horizontally so
|
||
// we truncate window titles at MAX_WINDOW_TITLE_SEND. However, we do
|
||
// not pad the titles so we try to send as little data as possible.
|
||
// Allocate all the segment but the rest of the code does not rely on
|
||
// this so we split them into more segments later if need be. The
|
||
// memory pointed to by winNames[0] etc looks like this:
|
||
//
|
||
// For each entry in the corresponding WinStruct which is a window from
|
||
// a shared task (and in the same order):
|
||
//
|
||
// either -
|
||
// (char)0xFF - not a `task window' - give it a NULL title
|
||
// or -
|
||
// a null terminated string up to MAX_WINDOW_TITLE_SEND characters
|
||
//
|
||
// Note that we don't need full and compact versions because only
|
||
// windows which will be in the compact WinStruct will have
|
||
// corresponding entries in this structure.
|
||
//
|
||
m_aswlWinNames[0] =
|
||
new char[2*SWL_MAX_WINDOWS*SWL_MAX_WINDOW_TITLE_SEND];
|
||
if (!m_aswlWinNames[0])
|
||
{
|
||
ERROR_OUT(( "failed to get memory for window title lists"));
|
||
DC_QUIT;
|
||
}
|
||
|
||
m_aswlWinNames[1] = m_aswlWinNames[0] +
|
||
SWL_MAX_WINDOWS*SWL_MAX_WINDOW_TITLE_SEND;
|
||
|
||
ASSERT(m_swlCurIndex == 0);
|
||
|
||
rc = TRUE;
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(ASHost::SWL_HostStarting, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// SWL_HostEnded()
|
||
//
|
||
void ASHost::SWL_HostEnded(void)
|
||
{
|
||
DebugEntry(ASHost::SWL_HostEnded);
|
||
|
||
//
|
||
// For 2.x nodes, we must send out one last packet so they kill
|
||
// their shadows.
|
||
//
|
||
|
||
//
|
||
// Remove the SWL properties for all existing windows.
|
||
//
|
||
EnumWindows(SWLDestroyWindowProperty, 0);
|
||
|
||
m_swlfSyncing = FALSE;
|
||
|
||
if (m_pShare->m_scShareVersion < CAPS_VERSION_30)
|
||
{
|
||
//
|
||
// SWL_Periodic() should NOT put properties on windows
|
||
// when we're not hosting anymore.
|
||
//
|
||
ASSERT(m_pShare->m_pasLocal->hetCount == 0);
|
||
TRACE_OUT(("SWL_HostEnded: Must send an empty window list for 2.x nodes"));
|
||
m_swlfForceSend = TRUE;
|
||
SWL_Periodic();
|
||
}
|
||
|
||
if (m_aswlNRInfo[0])
|
||
{
|
||
delete[] m_aswlNRInfo[0];
|
||
m_aswlNRInfo[0] = NULL;
|
||
}
|
||
|
||
if (m_aswlNRInfo[1])
|
||
{
|
||
delete[] m_aswlNRInfo[1];
|
||
m_aswlNRInfo[1] = NULL;
|
||
}
|
||
|
||
if (m_aswlWinNames[0])
|
||
{
|
||
delete[] m_aswlWinNames[0];
|
||
m_aswlWinNames[0] = NULL;
|
||
}
|
||
|
||
if (m_swlPropAtom)
|
||
{
|
||
GlobalDeleteAtom(m_swlPropAtom);
|
||
m_swlPropAtom = 0;
|
||
}
|
||
|
||
DebugExitVOID(ASHost::SWL_HostEnded);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: SWL_GetSharedIDFromLocalID
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Given a window ID from a shared application which is running locally
|
||
// this will return the top level parent. If this parent is invisible,
|
||
// we return NULL.
|
||
//
|
||
// the parent window nearest the desktop. If this parent window is
|
||
// invisible NULL is returned.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// window - the window in question
|
||
//
|
||
// RETURNS:
|
||
//
|
||
// a HWND or NULL if the window is not from a shared application
|
||
//
|
||
//
|
||
HWND ASHost::SWL_GetSharedIDFromLocalID(HWND window)
|
||
{
|
||
HWND hwnd;
|
||
HWND hwndParent;
|
||
HWND hwndDesktop;
|
||
|
||
DebugEntry(ASHost::SWL_GetSharedIDFromLocalID);
|
||
|
||
hwnd = window;
|
||
if (!hwnd)
|
||
{
|
||
DC_QUIT;
|
||
}
|
||
|
||
hwndDesktop = GetDesktopWindow();
|
||
|
||
//
|
||
// Get the real top level ancestor
|
||
//
|
||
while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
|
||
{
|
||
hwndParent = GetParent(hwnd);
|
||
if (hwndParent == hwndDesktop)
|
||
break;
|
||
|
||
hwnd = hwndParent;
|
||
}
|
||
|
||
//
|
||
// Is this a hosted guy?
|
||
//
|
||
if (m_pShare->HET_WindowIsHosted(hwnd))
|
||
{
|
||
if (!(GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE))
|
||
{
|
||
//
|
||
// This window does not have the visible style. But it may just
|
||
// be transiently invisible and SWL is still treating it as
|
||
// visible. RAID3074 requires that a window which is not yet
|
||
// believed to be invisible by SWL is treated as visible (since
|
||
// the remote has not been informed that it is invisible). We
|
||
// can determine whether SWL is traeting this window as visible
|
||
// by looking at the SWL window property. If no property exists
|
||
// then the window is new so the remote cannot know about it
|
||
// and we can assume it is indeed invisible.
|
||
//
|
||
if (! ((UINT_PTR)GetProp(hwnd, MAKEINTATOM(m_swlPropAtom)) & SWL_PROP_COUNTDOWN_MASK))
|
||
{
|
||
//
|
||
// SWL knows that the parent of a shared application is
|
||
// invisible so we just return NULL.
|
||
//
|
||
hwnd = NULL;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hwnd = NULL;
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitDWORD(ASHost::SWL_GetSharedIDFromLocalID, HandleToUlong(hwnd));
|
||
return(hwnd);
|
||
}
|
||
|
||
|
||
//
|
||
// SWL_UpdateCurrentDesktop()
|
||
//
|
||
// This checks what the current desktop is, and if it's changed, updates
|
||
// the NT input hooks for winlogon/screensaver for the service. But normal
|
||
// SWL and AWC also make use of this info.
|
||
//
|
||
void ASHost::SWL_UpdateCurrentDesktop(void)
|
||
{
|
||
HDESK hDeskCurrent = NULL;
|
||
UINT newCurrentDesktop;
|
||
char szName[SWL_DESKTOPNAME_MAX];
|
||
|
||
DebugEntry(ASHost::SWL_UpdateCurrentDesktop);
|
||
|
||
newCurrentDesktop = DESKTOP_OURS;
|
||
|
||
if (g_asWin95)
|
||
{
|
||
// Nothing to do
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Get the current desktop. If we can't even get it, assume it's the
|
||
// winlogon desktop.
|
||
//
|
||
hDeskCurrent = OpenInputDesktop(0, TRUE, DESKTOP_READOBJECTS);
|
||
if (!hDeskCurrent)
|
||
{
|
||
TRACE_OUT(("OpenInputDesktop failed; must be WINLOGON"));
|
||
newCurrentDesktop = DESKTOP_WINLOGON;
|
||
DC_QUIT;
|
||
}
|
||
|
||
// Get the name of the current desktop
|
||
szName[0] = 0;
|
||
GetUserObjectInformation(hDeskCurrent, UOI_NAME, szName,
|
||
sizeof(szName), NULL);
|
||
TRACE_OUT(("GetUserObjectInformation returned %s for name", szName));
|
||
|
||
if (!lstrcmpi(szName, m_aswlOurDesktopName))
|
||
{
|
||
newCurrentDesktop = DESKTOP_OURS;
|
||
}
|
||
else if (!lstrcmpi(szName, NAME_DESKTOP_SCREENSAVER))
|
||
{
|
||
newCurrentDesktop = DESKTOP_SCREENSAVER;
|
||
}
|
||
else if (!lstrcmpi(szName, NAME_DESKTOP_WINLOGON))
|
||
{
|
||
newCurrentDesktop = DESKTOP_WINLOGON;
|
||
}
|
||
else
|
||
{
|
||
newCurrentDesktop = DESKTOP_OTHER;
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
if (newCurrentDesktop != m_swlCurrentDesktop)
|
||
{
|
||
//
|
||
// If this is the service, adjust where we playback events
|
||
// and/or block local input.
|
||
//
|
||
OSI_DesktopSwitch(m_swlCurrentDesktop, newCurrentDesktop);
|
||
m_swlCurrentDesktop = newCurrentDesktop;
|
||
}
|
||
|
||
if (hDeskCurrent != NULL)
|
||
{
|
||
CloseDesktop(hDeskCurrent);
|
||
}
|
||
|
||
DebugExitVOID(ASHost::SWL_UpdateCurrentDesktop);
|
||
}
|
||
|
||
|
||
//
|
||
// SWL_IsOurDesktopActive()
|
||
//
|
||
BOOL ASHost::SWL_IsOurDesktopActive(void)
|
||
{
|
||
return(!g_asSharedMemory->fullScreen && (m_swlCurrentDesktop == DESKTOP_OURS));
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// FUNCTION: SWLInitHostFullWinListEntry
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Initializes a hosted window entry in the full window list.
|
||
//
|
||
// PARAMETERS: hwnd - Window ID of the hosted window
|
||
// windowProp - SWL window properties for hwnd
|
||
// ownerID - Window ID of hwnd's owner
|
||
// pFullWinEntry - pointer to the list entry to initialize
|
||
//
|
||
// RETURNS: Nothing
|
||
//
|
||
//
|
||
void ASHost::SWLInitHostFullWinListEntry
|
||
(
|
||
HWND hwnd,
|
||
UINT windowProp,
|
||
HWND hwndOwner,
|
||
PSWLWINATTRIBUTES pFullWinEntry
|
||
)
|
||
{
|
||
DebugEntry(ASHost::SWLInitHostFullWinListEntry);
|
||
|
||
//
|
||
// The window is a shared application hosted locally.
|
||
// These get the application id, the local window id and the owner
|
||
// window id.
|
||
//
|
||
// Note that the real owner of the window may be a child of a shared
|
||
// window, and therefore not known to the remote machine. We therefore
|
||
// pass the real owner to SWL_GetSharedIDFromLocalID() which will
|
||
// traverse up the owner's window tree until it finds a window that is
|
||
// shared and store the returned window handle in the window structure.
|
||
//
|
||
pFullWinEntry->flags = SWL_FLAG_WINDOW_HOSTED;
|
||
pFullWinEntry->winID = HandleToUlong(hwnd);
|
||
pFullWinEntry->extra = GetWindowThreadProcessId(hwnd, NULL);
|
||
|
||
// NOTE: ownerWinID is ignored by NM 3.0 and up.
|
||
pFullWinEntry->ownerWinID = HandleToUlong(SWL_GetSharedIDFromLocalID(hwndOwner));
|
||
|
||
//
|
||
// Check if the window is minimized.
|
||
//
|
||
if (IsIconic(hwnd))
|
||
{
|
||
pFullWinEntry->flags |= SWL_FLAG_WINDOW_MINIMIZED;
|
||
}
|
||
|
||
//
|
||
// TAGGABLE is for 2.x nodes only; 3.0 and up don't look at this.
|
||
//
|
||
if (windowProp & SWL_PROP_TAGGABLE)
|
||
{
|
||
pFullWinEntry->flags |= SWL_FLAG_WINDOW_TAGGABLE;
|
||
}
|
||
|
||
if (windowProp & SWL_PROP_TRANSPARENT)
|
||
{
|
||
//
|
||
// The window is transparent and (to have got this far) must be
|
||
// shared or the desktop is shared, ie we will be sending the
|
||
// window but need to fiddle the z-order. Flag the transparency so
|
||
// we can do the z-order later.
|
||
//
|
||
pFullWinEntry->flags |= SWL_FLAG_WINDOW_TRANSPARENT;
|
||
}
|
||
else if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
|
||
{
|
||
//
|
||
// The window is not transparent and is topmost, so set the topmost
|
||
// flag.
|
||
//
|
||
pFullWinEntry->flags |= SWL_FLAG_WINDOW_TOPMOST;
|
||
}
|
||
|
||
//
|
||
// If this window is on the task bar then pass this info on
|
||
//
|
||
if (windowProp & SWL_PROP_TASKBAR)
|
||
{
|
||
pFullWinEntry->flags |= SWL_FLAG_WINDOW_TASKBAR;
|
||
}
|
||
else
|
||
{
|
||
pFullWinEntry->flags |= SWL_FLAG_WINDOW_NOTASKBAR;
|
||
}
|
||
|
||
DebugExitVOID(ASHost::SWLInitHostFullWinListEntry);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// FUNCTION: SWLAddHostWindowTitle
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Adds a hosted window title (or blank entry) to our window titles list.
|
||
//
|
||
// PARAMETERS: winid - Window ID of the hosted window
|
||
// windowProp - SWL window properties for winid
|
||
// ownerID - Window ID of winid's owner
|
||
// ppWinNames - pointer to pointer to window names structure
|
||
//
|
||
// RETURNS: Nothing
|
||
//
|
||
//
|
||
void ASHost::SWLAddHostWindowTitle
|
||
(
|
||
HWND hwnd,
|
||
UINT windowProp,
|
||
HWND hwndOwner,
|
||
LPSTR *ppWinNames
|
||
)
|
||
{
|
||
int lenTitle;
|
||
|
||
DebugEntry(ASHost::SWLAddHostWindowTitle);
|
||
|
||
//
|
||
// This window gets an entry in our window titles data if it passes the
|
||
// following tests
|
||
//
|
||
// for Windows: it has no owner, or its owner is invisible
|
||
//
|
||
//
|
||
if ( (windowProp & SWL_PROP_TASKBAR) ||
|
||
hwndOwner == NULL ||
|
||
!IsWindowVisible(hwndOwner) )
|
||
{
|
||
//
|
||
// LAURABU 2.x COMPAT:
|
||
// 3.0 nodes only look at the text if TASKBAR is set. When 2.x
|
||
// compat is gone, don't send text in the other cases.
|
||
//
|
||
|
||
//
|
||
// Get the title - truncated and null terminated for us. First
|
||
// look for the desktop, which may have a special, configurable
|
||
// name.
|
||
//
|
||
lenTitle = GetWindowText(hwnd, *ppWinNames, SWL_MAX_WINDOW_TITLE_SEND);
|
||
|
||
//
|
||
// Check that the title has been null terminated.
|
||
//
|
||
(*ppWinNames)[lenTitle] = '\0';
|
||
*ppWinNames += lenTitle;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is not a task window - put a corresponding entry in the
|
||
// title info.
|
||
//
|
||
**ppWinNames = '\xff';
|
||
}
|
||
|
||
*ppWinNames += 1;
|
||
|
||
DebugExitVOID(ASHost::SWLAddHostWindowTitle);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: SWL_InitFullWindowListEntry
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Initialises an entry in the full window list.
|
||
//
|
||
// PARAMETERS: hwnd - Window ID of the window for which an entry is
|
||
// initialized
|
||
// windowProp - SWL window properties for hwnd
|
||
// ownerID - Window ID of hwnd's owner
|
||
// pFullWinEntry - pointer to the list entry to initialize
|
||
//
|
||
// RETURNS: Nothing
|
||
//
|
||
//
|
||
void ASHost::SWL_InitFullWindowListEntry
|
||
(
|
||
HWND hwnd,
|
||
UINT windowProp,
|
||
LPSTR * ppWinNames,
|
||
PSWLWINATTRIBUTES pFullWinEntry
|
||
)
|
||
{
|
||
HWND hwndOwner;
|
||
RECT rect;
|
||
|
||
DebugEntry(ASHost::SWL_InitFullWindowListEntry);
|
||
|
||
if (windowProp & SWL_PROP_HOSTED)
|
||
{
|
||
//
|
||
// The window is a shared application hosted locally.
|
||
// Set up an entry in our full window structure.
|
||
//
|
||
hwndOwner = GetWindow(hwnd, GW_OWNER);
|
||
SWLInitHostFullWinListEntry(hwnd,
|
||
windowProp,
|
||
hwndOwner,
|
||
pFullWinEntry);
|
||
|
||
SWLAddHostWindowTitle(hwnd, windowProp, hwndOwner, ppWinNames);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// The window is a local (non-shared) application
|
||
//
|
||
pFullWinEntry->flags = SWL_FLAG_WINDOW_LOCAL;
|
||
|
||
//
|
||
// We set the winID here because we may need this info
|
||
// again later, but we will NULL it out before we send the
|
||
// protocol packet out because it is not info that the
|
||
// remote needs
|
||
//
|
||
pFullWinEntry->winID = HandleToUlong(hwnd);
|
||
pFullWinEntry->extra = MCSID_NULL;
|
||
pFullWinEntry->ownerWinID = 0;
|
||
}
|
||
|
||
//
|
||
// Get the position and size of the window, in inclusive
|
||
// Virtual Desktop coordinates.
|
||
//
|
||
GetWindowRect(hwnd, &rect);
|
||
|
||
//
|
||
// TAGGABLE is for 2.x nodes only
|
||
//
|
||
if (IsRectEmpty(&rect))
|
||
{
|
||
pFullWinEntry->flags &= ~SWL_FLAG_WINDOW_TAGGABLE;
|
||
}
|
||
else
|
||
{
|
||
if (windowProp & SWL_PROP_TAGGABLE)
|
||
{
|
||
if (!SWLWindowIsTaggable(hwnd))
|
||
pFullWinEntry->flags &= ~SWL_FLAG_WINDOW_TAGGABLE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make the rectangle inclusive.
|
||
//
|
||
rect.right -= 1;
|
||
rect.bottom -= 1;
|
||
TSHR_RECT16_FROM_RECT(&(pFullWinEntry->position), rect);
|
||
|
||
DebugExitVOID(ASHost::SWL_InitFullWindowListEntry);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: SWLCompactWindowList
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Compacts the full window list into one containng only those windows SWL
|
||
// needs to send (hosts and any locals overlapping hosts)
|
||
//
|
||
// PARAMETERS: numFullListEntries - number of entries in the full window
|
||
// list.
|
||
// pFullWinList - pointer to the full window list
|
||
// pCompactWinList - pointer to the compact window list
|
||
//
|
||
// RETURNS: Number of entries copied to the compact window list
|
||
//
|
||
//
|
||
UINT ASHost::SWLCompactWindowList
|
||
(
|
||
UINT numFullListEntries,
|
||
PSWLWINATTRIBUTES pFullWinList,
|
||
PSWLWINATTRIBUTES pCompactWinList
|
||
)
|
||
{
|
||
UINT fullIndex;
|
||
UINT compactIndex = 0;
|
||
UINT i;
|
||
|
||
DebugEntry(ASHost::SWLCompactWindowList);
|
||
|
||
//
|
||
// For each window in the full list...
|
||
//
|
||
for ( fullIndex = 0; fullIndex < numFullListEntries; fullIndex++ )
|
||
{
|
||
if (pFullWinList[fullIndex].flags & SWL_FLAG_WINDOW_LOCAL)
|
||
{
|
||
//
|
||
// This is a local window so we need to track it only if it
|
||
// overlaps a hosted window. Run through the remaining windows
|
||
// until we either find an overlapped hosted window (meaning we
|
||
// must track this local window) or reach the end of the list
|
||
// (meaning we don't need to track this local window).
|
||
//
|
||
for ( i = fullIndex + 1; i < numFullListEntries; i++ )
|
||
{
|
||
//
|
||
// If this window is hosted and intersects the local
|
||
// window then we need to track the local window.
|
||
//
|
||
if ( (pFullWinList[i].flags & SWL_FLAG_WINDOW_HOSTED) &&
|
||
(COM_Rect16sIntersect(&pFullWinList[fullIndex].position,
|
||
&pFullWinList[i].position)))
|
||
{
|
||
//
|
||
// Copy the local window to the compact array and
|
||
// break out the inner loop.
|
||
//
|
||
TRACE_OUT(("Add local hwnd 0x%08x to list at %u",
|
||
pFullWinList[fullIndex].winID, compactIndex));
|
||
pCompactWinList[compactIndex++] =
|
||
pFullWinList[fullIndex];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a shadow or hosted window so we must track it.
|
||
//
|
||
TRACE_OUT(("Add shared hwnd 0x%08x to list at %u",
|
||
pFullWinList[fullIndex].winID, compactIndex));
|
||
pCompactWinList[compactIndex++] = pFullWinList[fullIndex];
|
||
}
|
||
}
|
||
|
||
DebugExitDWORD(ASHost::SWLCompactWindowList, compactIndex);
|
||
return(compactIndex);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// FUNCTION: SWLAdjustZOrderForTransparency
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Rearranges the window structure z-order to take account of a transparent
|
||
// window (winID). Must not be called if the transparent entry is the last
|
||
// in the compact list.
|
||
//
|
||
// PARAMETERS: pTransparentListEntry - pointer to the transparent entry
|
||
// pLastListEntry - pointer to the last compact window list
|
||
// entry
|
||
// winPosition - position of window in names array
|
||
// pWinNames - hosted window names
|
||
// sizeWinNames - number of bytes in winNames
|
||
//
|
||
// RETURNS: Nothing.
|
||
//
|
||
//
|
||
void ASHost::SWLAdjustZOrderForTransparency
|
||
(
|
||
PSWLWINATTRIBUTES pTransparentListEntry,
|
||
PSWLWINATTRIBUTES pLastListEntry,
|
||
UINT winPosition,
|
||
LPSTR pWinNames,
|
||
UINT sizeWinNames
|
||
)
|
||
{
|
||
SWLWINATTRIBUTES winCopyBuffer;
|
||
LPSTR pEndNames = &pWinNames[sizeWinNames - 1];
|
||
UINT nameLen;
|
||
char windowText[TSHR_MAX_PERSON_NAME_LEN + SWL_MAX_WINDOW_TITLE_SEND];
|
||
|
||
DebugEntry(ASHost::SWLAdjustZOrderForTransparency);
|
||
|
||
//
|
||
// - turn off the transparent flag (it's not part of the protocol)
|
||
// - move the window to the end of the structure, ie bottom of the
|
||
// z-order (unless the desktop is at the bottom, in which case
|
||
// the window becomes next to bottom).
|
||
//
|
||
TRACE_OUT(("Adjust z-order for transparent hwnd 0x%08x position %u",
|
||
pTransparentListEntry->winID,
|
||
winPosition));
|
||
pTransparentListEntry->flags &= ~SWL_FLAG_WINDOW_TRANSPARENT;
|
||
winCopyBuffer = *pTransparentListEntry;
|
||
|
||
//
|
||
// Shuffle the windows after the transparent entry one place toward the
|
||
// start of the list.
|
||
//
|
||
UT_MoveMemory(pTransparentListEntry,
|
||
&pTransparentListEntry[1],
|
||
(LPBYTE)pLastListEntry - (LPBYTE)pTransparentListEntry);
|
||
|
||
*pLastListEntry = winCopyBuffer;
|
||
|
||
//
|
||
// Now rearrange the window names in the same way. First, find the name
|
||
// for this window.
|
||
//
|
||
ASSERT((sizeWinNames != 0));
|
||
for ( ;winPosition != 0; winPosition-- )
|
||
{
|
||
if ( *pWinNames == '\xff' )
|
||
{
|
||
//
|
||
// No name exists for this window, so just advance past the
|
||
// 0xff placeholder.
|
||
//
|
||
TRACE_OUT(("No name for %u", winPosition-1));
|
||
pWinNames++;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// A name exists for this window, so skip past all the
|
||
// characters, including the NULL terminator.
|
||
//
|
||
TRACE_OUT(( "Ignore %s", pWinNames));
|
||
while ( *pWinNames != '\0' )
|
||
{
|
||
pWinNames++;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// winNames now points to the start of the name for the window being
|
||
// reordered.
|
||
//
|
||
if ( *pWinNames == '\xff' )
|
||
{
|
||
//
|
||
// This window has no name and simply has an 0xff placeholder in
|
||
// the name list. Move all the remaining names down by one and add
|
||
// the 0xff at the end.
|
||
//
|
||
TRACE_OUT(("Reorder nameless window"));
|
||
UT_MoveMemory(pWinNames, pWinNames + 1, pEndNames - pWinNames);
|
||
*pEndNames = (char)'\xff';
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Move as many bytes as there are characters in the window name
|
||
// then tack the name on the end.
|
||
//
|
||
TRACE_OUT(("Reorder %s", pWinNames));
|
||
lstrcpy(windowText, pWinNames);
|
||
nameLen = lstrlen(pWinNames);
|
||
UT_MoveMemory(pWinNames, pWinNames + nameLen + 1, pEndNames - pWinNames -
|
||
nameLen);
|
||
lstrcpy(pEndNames - nameLen, windowText);
|
||
}
|
||
|
||
DebugExitVOID(ASHost::SWLAdjustZOrderForTransparency);
|
||
}
|
||
|
||
//
|
||
// SWL_Periodic()
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Called periodically. If the window structure has changed (such that it
|
||
// impacts remote systems) then send a new one if we can.
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// fSend - TRUE if the caller really wants us to try to send the new
|
||
// structure.
|
||
//
|
||
// RETURNS: SWL_RC_ERROR : An error occurred
|
||
// SWL_RC_SENT : Window structure sent successfully
|
||
// SWL_RC_NOT_SENT : No need to send window structure
|
||
//
|
||
UINT ASHost::SWL_Periodic(void)
|
||
{
|
||
UINT fRC = SWL_RC_NOT_SENT;
|
||
UINT newIndex;
|
||
PSWLWINATTRIBUTES newFullWinStruct;
|
||
PSWLWINATTRIBUTES curFullWinStruct;
|
||
PSWLWINATTRIBUTES newCompactWinStruct;
|
||
PSWLWINATTRIBUTES curCompactWinStruct;
|
||
UINT i;
|
||
UINT k;
|
||
BOOL fNoTitlesChanged;
|
||
HWND hwnd;
|
||
SWLENUMSTRUCT swlEnumStruct;
|
||
int complexity;
|
||
UINT cNonRectData;
|
||
UINT size;
|
||
UINT ourSize;
|
||
HRGN hrgnNR;
|
||
HRGN hrgnRect;
|
||
LPRGNDATA pRgnData = NULL;
|
||
LPTSHR_INT16 pOurRgnData = NULL;
|
||
LPTSHR_INT16 pEndRgnData;
|
||
LPTSHR_INT16 pAllocRgnData = NULL;
|
||
BOOL fNonRectangularInfoChanged;
|
||
BOOL rgnOK;
|
||
RECT rectBound;
|
||
int left;
|
||
int top;
|
||
int right;
|
||
int bottom;
|
||
int lastleft;
|
||
int lasttop;
|
||
int lastright;
|
||
int lastbottom;
|
||
int deltaleft;
|
||
int deltatop;
|
||
int deltaright;
|
||
int deltabottom;
|
||
int lastdeltaleft;
|
||
int lastdeltatop;
|
||
int lastdeltaright;
|
||
int lastdeltabottom;
|
||
UINT numCompactWins;
|
||
UINT lastTransparency;
|
||
UINT winFlags;
|
||
UINT iHosted;
|
||
|
||
DebugEntry(ASSHost::SWL_Periodic);
|
||
|
||
SWL_UpdateCurrentDesktop();
|
||
|
||
//
|
||
// If this party isn't hosting apps (and isn't faking up an empty
|
||
// packet for 2.x nodes), there's nothing to do.
|
||
//
|
||
if (m_pShare->m_pasLocal->hetCount == HET_DESKTOPSHARED)
|
||
{
|
||
m_swlfForceSend = FALSE;
|
||
fRC = SWL_RC_NOT_SENT;
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Get the window structure into the "new" array.
|
||
//
|
||
newIndex = (m_swlCurIndex+1)%2;
|
||
curFullWinStruct = &(m_aswlFullWinStructs[m_swlCurIndex * SWL_MAX_WINDOWS]);
|
||
newFullWinStruct = &(m_aswlFullWinStructs[newIndex * SWL_MAX_WINDOWS]);
|
||
|
||
//
|
||
// Free any previously allocated data.
|
||
//
|
||
if (m_aswlNRInfo[newIndex])
|
||
{
|
||
delete[] m_aswlNRInfo[newIndex];
|
||
m_aswlNRInfo[newIndex] = NULL;
|
||
}
|
||
m_aswlNRSize[newIndex] = 0;
|
||
|
||
//
|
||
// Start from the first child of the desktop - should be the top
|
||
// top-level window
|
||
//
|
||
ZeroMemory(&swlEnumStruct, sizeof(swlEnumStruct));
|
||
swlEnumStruct.pHost = this;
|
||
swlEnumStruct.newWinNames = m_aswlWinNames[newIndex];
|
||
swlEnumStruct.newFullWinStruct = newFullWinStruct;
|
||
|
||
//
|
||
// Before we consider the windows on the windows desktop we check for
|
||
// an active full-screen session. If there is one then we insert a
|
||
// local window the size of the physical screen first so that all
|
||
// applications which are hosted on this system will become obscured
|
||
// on the remote system.
|
||
//
|
||
ASSERT(swlEnumStruct.count == 0);
|
||
|
||
if (!SWL_IsOurDesktopActive())
|
||
{
|
||
newFullWinStruct[0].flags = SWL_FLAG_WINDOW_LOCAL;
|
||
newFullWinStruct[0].winID = 0;
|
||
newFullWinStruct[0].extra = MCSID_NULL;
|
||
newFullWinStruct[0].ownerWinID = 0;
|
||
newFullWinStruct[0].position.left = 0;
|
||
newFullWinStruct[0].position.top = 0;
|
||
newFullWinStruct[0].position.right = (TSHR_UINT16)(m_pShare->m_pasLocal->cpcCaps.screen.capsScreenWidth-1);
|
||
newFullWinStruct[0].position.bottom = (TSHR_UINT16)(m_pShare->m_pasLocal->cpcCaps.screen.capsScreenHeight-1);
|
||
|
||
swlEnumStruct.count++;
|
||
}
|
||
|
||
EnumWindows(SWLEnumProc, (LPARAM)&swlEnumStruct);
|
||
|
||
//
|
||
// Check if we should bail out because of visibility detection
|
||
//
|
||
if (swlEnumStruct.fBailOut)
|
||
{
|
||
TRACE_OUT(("SWL_MaybeSendWindowList: bailing out due to visibility detection"));
|
||
fRC = SWL_RC_ERROR;
|
||
DC_QUIT;
|
||
}
|
||
|
||
m_aswlWinNamesSize[newIndex] = (UINT)(swlEnumStruct.newWinNames - m_aswlWinNames[newIndex]);
|
||
m_aswlNumFullWins[newIndex] = swlEnumStruct.count;
|
||
|
||
//
|
||
// Check whether we found a transparent window.
|
||
//
|
||
lastTransparency = swlEnumStruct.count - 1;
|
||
k = 0;
|
||
iHosted = 0;
|
||
while ( (swlEnumStruct.transparentCount > 0) && (k < lastTransparency) )
|
||
{
|
||
//
|
||
// If the transparent flag is set then rearrange the z-order,
|
||
// providing the transparent window is not already at the
|
||
// bottom of the z-order.
|
||
//
|
||
if (newFullWinStruct[k].flags & SWL_FLAG_WINDOW_TRANSPARENT)
|
||
{
|
||
//
|
||
// Now continue with the non-rectangular check - but this will
|
||
// be on the window "shunted down" from what was the next
|
||
// position in newCompactWinStruct, ie same value of i. We will
|
||
// see the moved (transparent) window when we reach it
|
||
// again at the end of this for-loop (when it will have the
|
||
// transparent flag off, so we don't redo this bit).
|
||
//
|
||
SWLAdjustZOrderForTransparency(
|
||
&newFullWinStruct[k],
|
||
&newFullWinStruct[lastTransparency],
|
||
iHosted,
|
||
m_aswlWinNames[newIndex],
|
||
m_aswlWinNamesSize[newIndex]);
|
||
|
||
swlEnumStruct.transparentCount--;
|
||
}
|
||
else
|
||
{
|
||
if (newFullWinStruct[k].flags & SWL_FLAG_WINDOW_HOSTED)
|
||
{
|
||
iHosted++;
|
||
}
|
||
k++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Compare the current and new information - if they are identical then
|
||
// we can quit now.
|
||
//
|
||
fNoTitlesChanged = ((m_aswlWinNamesSize[0] == m_aswlWinNamesSize[1]) &&
|
||
(memcmp(m_aswlWinNames[0],
|
||
m_aswlWinNames[1],
|
||
m_aswlWinNamesSize[0]) == 0));
|
||
|
||
if ( fNoTitlesChanged &&
|
||
!m_swlfRegionalChanges &&
|
||
(m_aswlNumFullWins[0] == m_aswlNumFullWins[1]) &&
|
||
(memcmp(newFullWinStruct,
|
||
curFullWinStruct,
|
||
(m_aswlNumFullWins[0] * sizeof(SWLWINATTRIBUTES))) == 0) )
|
||
{
|
||
//
|
||
// We don't need to send a window structure if nothing has changed
|
||
// unless there has been a send override.
|
||
//
|
||
if (m_swlfForceSend)
|
||
{
|
||
//
|
||
// This is a normal call AND there are pending changes.
|
||
//
|
||
TRACE_OUT(( "NORMAL, pending changes - send"));
|
||
if (SWLSendPacket(&(m_aswlCompactWinStructs[m_swlCurIndex * SWL_MAX_WINDOWS]),
|
||
m_aswlNumCompactWins[m_swlCurIndex],
|
||
m_aswlWinNames[m_swlCurIndex],
|
||
m_aswlWinNamesSize[m_swlCurIndex],
|
||
m_aswlNRSize[m_swlCurIndex],
|
||
m_aswlNRInfo[m_swlCurIndex]) )
|
||
{
|
||
//
|
||
// Successfully sent this so reset the m_swlfForceSend
|
||
// flag.
|
||
//
|
||
m_swlfForceSend = FALSE;
|
||
fRC = SWL_RC_SENT;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Failed to send this packet so don't reset
|
||
// m_swlfForceSend so that we retry next time and return
|
||
// an error.
|
||
//
|
||
fRC = SWL_RC_ERROR;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a normal call and we don't have any changes pending
|
||
// so don't send anything.
|
||
//
|
||
TRACE_OUT(( "No changes - SWL not sent"));
|
||
}
|
||
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// We can reset the flag that alerted us to potential regional window
|
||
// changes now that we have gone and actually checked all the windows.
|
||
//
|
||
m_swlfRegionalChanges = FALSE;
|
||
|
||
//
|
||
// Something in the window structure has changed. Determine which
|
||
// windows in the full list are unnecessary (local ones not overlapping
|
||
// any hosted ones) and create a compact array of windows we really
|
||
// need.
|
||
//
|
||
curCompactWinStruct = &(m_aswlCompactWinStructs[m_swlCurIndex * SWL_MAX_WINDOWS]);
|
||
newCompactWinStruct = &(m_aswlCompactWinStructs[newIndex * SWL_MAX_WINDOWS]);
|
||
|
||
numCompactWins = SWLCompactWindowList(m_aswlNumFullWins[newIndex],
|
||
newFullWinStruct,
|
||
newCompactWinStruct);
|
||
|
||
m_aswlNumCompactWins[newIndex] = numCompactWins;
|
||
|
||
//
|
||
// Run through the compact window list to check for regional windows
|
||
//
|
||
cNonRectData = 0;
|
||
|
||
hrgnNR = CreateRectRgn(0, 0, 0, 0);
|
||
|
||
for (i = 0; i < numCompactWins; i++)
|
||
{
|
||
winFlags = newCompactWinStruct[i].flags;
|
||
hwnd = (HWND)newCompactWinStruct[i].winID;
|
||
|
||
//
|
||
// There are some "fake" windows for which we do not provide a
|
||
// winID - these will never be non-rectangular anyway.
|
||
//
|
||
if ( (hwnd != NULL) &&
|
||
(winFlags & (SWL_FLAG_WINDOW_LOCAL | SWL_FLAG_WINDOW_HOSTED)) )
|
||
{
|
||
//
|
||
// If any of the remote systems care, see if this window has a
|
||
// non rectangular region selected into it.
|
||
//
|
||
if (GetWindowRgn(hwnd, hrgnNR) != ERROR)
|
||
{
|
||
TRACE_OUT(("Regional window 0x%08x", hwnd));
|
||
|
||
//
|
||
// There is a region selected in.
|
||
//
|
||
// This region is exactly as the application passed it to
|
||
// Windows, and has not yet been clipped to the window
|
||
// rectangle itself.
|
||
// THE COORDS ARE INCLUSIVE, SO WE ADD ONE to BOTTOM-RIGHT
|
||
//
|
||
hrgnRect = CreateRectRgn(0, 0,
|
||
newCompactWinStruct[i].position.right -
|
||
newCompactWinStruct[i].position.left + 1,
|
||
newCompactWinStruct[i].position.bottom -
|
||
newCompactWinStruct[i].position.top + 1);
|
||
|
||
complexity = IntersectRgn(hrgnNR, hrgnNR, hrgnRect);
|
||
|
||
DeleteRgn(hrgnRect);
|
||
|
||
if (complexity == COMPLEXREGION)
|
||
{
|
||
//
|
||
// The intersection is still a non-rectangular region.
|
||
//
|
||
// See how big a buffer we need to get the data for
|
||
// this region.
|
||
//
|
||
size = GetRegionData(hrgnNR,
|
||
0,
|
||
NULL);
|
||
|
||
//
|
||
// The size we are returned is the size of a full
|
||
// RGNDATAHEADER plus the rectangles stored in DWORDS.
|
||
// We can get away with just a WORD as the count of the
|
||
// rectangles, plus using WORDs for each of the
|
||
// coordinates.
|
||
//
|
||
size = (size - sizeof(RGNDATAHEADER)) / 2 + 2;
|
||
|
||
// Max UINT16 check
|
||
if ((size <= SWL_MAX_NONRECT_SIZE) &&
|
||
(size + cNonRectData < 65535))
|
||
{
|
||
//
|
||
// We will be able to query this data later, so
|
||
// we can flag this as a non-rectangular window.
|
||
//
|
||
newCompactWinStruct[i].flags
|
||
|= SWL_FLAG_WINDOW_NONRECTANGLE;
|
||
|
||
cNonRectData += size;
|
||
|
||
TRACE_OUT(("Regional window region is %d bytes", size));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This region is far too complex for us, so we
|
||
// pretend it is simple so we just consider its
|
||
// bounding box.
|
||
//
|
||
TRACE_OUT(("Region too big %d - use bounds", size));
|
||
complexity = SIMPLEREGION;
|
||
}
|
||
}
|
||
|
||
if (complexity == SIMPLEREGION)
|
||
{
|
||
//
|
||
// The resultant intersection region happens to be a
|
||
// rectangle so we can send this via the standard
|
||
// structure.
|
||
//
|
||
// Apply the virtual desktop adjustment, make it
|
||
// inclusive, and remember we were passed back window
|
||
// relative coords for the region.
|
||
//
|
||
TRACE_OUT(( "rectangular clipped regional window"));
|
||
|
||
// Since we are modifying the compact window struct here
|
||
// we need to call this so we don't falsely assume that
|
||
// there are no changes in the window struct based on
|
||
// comparisons of the old and new full window structs
|
||
m_swlfRegionalChanges = TRUE;
|
||
|
||
GetRgnBox(hrgnNR, &rectBound);
|
||
|
||
newCompactWinStruct[i].position.left = (TSHR_INT16)
|
||
(newCompactWinStruct[i].position.left +
|
||
rectBound.left);
|
||
newCompactWinStruct[i].position.top = (TSHR_INT16)
|
||
(newCompactWinStruct[i].position.top +
|
||
rectBound.top);
|
||
|
||
newCompactWinStruct[i].position.right = (TSHR_INT16)
|
||
(newCompactWinStruct[i].position.left +
|
||
rectBound.right - rectBound.left - 1);
|
||
newCompactWinStruct[i].position.bottom = (TSHR_INT16)
|
||
(newCompactWinStruct[i].position.top +
|
||
rectBound.bottom - rectBound.top - 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get any non-rectangular areas we need.
|
||
//
|
||
if (cNonRectData)
|
||
{
|
||
//
|
||
// There was some data needed - allocate some memory for it.
|
||
//
|
||
rgnOK = FALSE;
|
||
pAllocRgnData = (LPTSHR_INT16) new BYTE[cNonRectData];
|
||
if (pAllocRgnData)
|
||
{
|
||
pOurRgnData = pAllocRgnData;
|
||
pEndRgnData = (LPTSHR_INT16)((LPBYTE)pAllocRgnData + cNonRectData);
|
||
rgnOK = TRUE;
|
||
|
||
//
|
||
// Loop through the windows again, getting the data this time.
|
||
//
|
||
for ( i = 0; i < numCompactWins; i++ )
|
||
{
|
||
if (newCompactWinStruct[i].flags &
|
||
SWL_FLAG_WINDOW_NONRECTANGLE)
|
||
{
|
||
GetWindowRgn((HWND)newCompactWinStruct[i].winID, hrgnNR);
|
||
|
||
//
|
||
// Clip the region to the window once again.
|
||
// THE COORDS ARE INCLUSIVE, SO ADD ONE TO BOTTOM-RIGHT
|
||
//
|
||
hrgnRect = CreateRectRgn(0, 0,
|
||
newCompactWinStruct[i].position.right -
|
||
newCompactWinStruct[i].position.left + 1,
|
||
newCompactWinStruct[i].position.bottom -
|
||
newCompactWinStruct[i].position.top + 1);
|
||
|
||
IntersectRgn(hrgnNR, hrgnNR, hrgnRect);
|
||
|
||
DeleteRgn(hrgnRect);
|
||
|
||
//
|
||
// Get the clipped region data.
|
||
//
|
||
// We have already excluded windows above that will
|
||
// return too large a size here, so we know we are only
|
||
// working with reasonable sizes now.
|
||
//
|
||
size = GetRegionData(hrgnNR, 0, NULL);
|
||
|
||
//
|
||
// For the moment we allocate memory each time for the
|
||
// region. Perhaps a better idea would be to save the
|
||
// max size from when we previously queried the region
|
||
// sizes, and allocate just that size one outside the
|
||
// loop.
|
||
//
|
||
pRgnData = (LPRGNDATA) new BYTE[size];
|
||
|
||
if (pRgnData)
|
||
{
|
||
GetRegionData(hrgnNR, size, pRgnData);
|
||
|
||
//
|
||
// There is a possibility that regions will have
|
||
// changed since we calculated the amount of data
|
||
// required. Before updating our structure with
|
||
// this window's region, check
|
||
// - the window hasn't become normal (ie 0 rects)
|
||
// - there is still enough space for the rects.
|
||
//
|
||
//
|
||
// Make sure this window still has regions
|
||
//
|
||
if (pRgnData->rdh.nCount == 0)
|
||
{
|
||
WARNING_OUT(( "No rects for window %#x",
|
||
newCompactWinStruct[i].winID));
|
||
newCompactWinStruct[i].flags &=
|
||
~SWL_FLAG_WINDOW_NONRECTANGLE;
|
||
|
||
delete[] pRgnData;
|
||
|
||
//
|
||
// Move on to next window.
|
||
//
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Check we have enough space for the rects:
|
||
// - ourSize is the number of int16s required.
|
||
// - GetRegionData returns the number of
|
||
// rectangles.
|
||
//
|
||
// We need one extra int16 to contain the count of
|
||
// rectangles.
|
||
//
|
||
ourSize = (pRgnData->rdh.nCount * 4) + 1;
|
||
if ((pOurRgnData + ourSize) > pEndRgnData)
|
||
{
|
||
WARNING_OUT(( "Can't fit %d int16s of region data",
|
||
ourSize));
|
||
rgnOK = FALSE;
|
||
delete[] pRgnData;
|
||
|
||
//
|
||
// Give up processing regional windows.
|
||
//
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Copy the data across to our SWL area in a more
|
||
// compact form.
|
||
//
|
||
// We take care to produce a compressible form
|
||
// because the raw data is essentially
|
||
// uncompressible via sliding window techniques.
|
||
// (Basically boils down to trying hard to make
|
||
// most values 0, or else of small magnitude).
|
||
//
|
||
//
|
||
// First we write the count of the number of
|
||
// rectangles.
|
||
//
|
||
*pOurRgnData++ = LOWORD(pRgnData->rdh.nCount);
|
||
|
||
//
|
||
// Now store the encoded rectangles.
|
||
//
|
||
lastleft = 0;
|
||
lasttop = 0;
|
||
lastright = 0;
|
||
lastbottom = 0;
|
||
|
||
lastdeltaleft = 0;
|
||
lastdeltatop = 0;
|
||
lastdeltaright = 0;
|
||
lastdeltabottom = 0;
|
||
|
||
for ( k = 0; k < (UINT)pRgnData->rdh.nCount; k++ )
|
||
{
|
||
//
|
||
// Extract 16bit quantities from the data we
|
||
// were returned.
|
||
//
|
||
// We also use inclusive coords whereas Windows
|
||
// gives us exclusive coords.
|
||
//
|
||
left = LOWORD(((LPRECT)(pRgnData->
|
||
Buffer))[k].left);
|
||
top = LOWORD(((LPRECT)(pRgnData->
|
||
Buffer))[k].top);
|
||
right = LOWORD(((LPRECT)(pRgnData->
|
||
Buffer))[k].right) - 1;
|
||
bottom = LOWORD(((LPRECT)(pRgnData->
|
||
Buffer))[k].bottom) - 1;
|
||
|
||
//
|
||
// The rectangles are ordered top to bottom,
|
||
// left to right, so the deltas are of smaller
|
||
// magnitude than the values themselves.
|
||
//
|
||
deltaleft = left - lastleft;
|
||
deltatop = top - lasttop;
|
||
deltaright = right - lastright;
|
||
deltabottom = bottom - lastbottom;
|
||
|
||
//
|
||
// In general, the left and right edges are
|
||
// connected lines, and the rectangles are of
|
||
// equal height so top/bottom are regular.
|
||
//
|
||
// Thus the values form a series which we can
|
||
// exploit to give a more compressible form.
|
||
//
|
||
// We already have the delta in each component,
|
||
// and these values themselves also form a
|
||
// series. For a straight line series all the
|
||
// deltas will be the same, so the "delta in
|
||
// the delta" will be zero. For a curve,
|
||
// although not all the deltas are the same,
|
||
// the "delta in the delta" is probably very
|
||
// small.
|
||
//
|
||
// A set of lots of zeros and small magnitude
|
||
// numbers is very compressible.
|
||
//
|
||
// Thus we store the "delta in the delta" for
|
||
// all components, rather than the values
|
||
// themselves. The receiver can undo all the
|
||
// deltaing to arive back at the original
|
||
// values.
|
||
//
|
||
*pOurRgnData++ =
|
||
(TSHR_UINT16)(deltaleft - lastdeltaleft);
|
||
*pOurRgnData++ =
|
||
(TSHR_UINT16)(deltatop - lastdeltatop);
|
||
*pOurRgnData++ =
|
||
(TSHR_UINT16)(deltaright - lastdeltaright);
|
||
*pOurRgnData++ =
|
||
(TSHR_UINT16)(deltabottom - lastdeltabottom);
|
||
|
||
//
|
||
// Update our last values.
|
||
//
|
||
lastleft = left;
|
||
lasttop = top;
|
||
lastright = right;
|
||
lastbottom = bottom;
|
||
lastdeltaleft = deltaleft;
|
||
lastdeltatop = deltatop;
|
||
lastdeltaright = deltaright;
|
||
lastdeltabottom = deltabottom;
|
||
}
|
||
|
||
//
|
||
// Free the data now we are finished with it.
|
||
//
|
||
delete[] pRgnData;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Failed to get memory for the rectangles, so the
|
||
// best we can do is use the bounding rect
|
||
//
|
||
// Clear the nonrect flag.
|
||
//
|
||
TRACE_OUT(("Failed alloc %d - use bounds", i));
|
||
|
||
newCompactWinStruct[i].flags &=
|
||
~SWL_FLAG_WINDOW_NONRECTANGLE;
|
||
}
|
||
|
||
if (newCompactWinStruct[i].flags & SWL_FLAG_WINDOW_LOCAL)
|
||
{
|
||
//
|
||
// The protocol defines that we will send a NULL
|
||
// winID for local windows, so NULL it out, now
|
||
// that we have finished with it.
|
||
//
|
||
newCompactWinStruct[i].winID = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!rgnOK)
|
||
{
|
||
//
|
||
// Something went wrong, one of:
|
||
// - we failed to allocate the memory we need to store the
|
||
// non-rectangular data
|
||
// - we allocated the memory but it turned out not to be large
|
||
// enough.
|
||
//
|
||
// Either way, best to act as if there is no such data for us.
|
||
//
|
||
if (pAllocRgnData == NULL)
|
||
{
|
||
WARNING_OUT(( "Failed to alloc %d for NRInfo", cNonRectData));
|
||
}
|
||
else
|
||
{
|
||
delete[] pAllocRgnData;
|
||
pAllocRgnData = NULL;
|
||
}
|
||
cNonRectData = 0;
|
||
|
||
//
|
||
// Clear all the nonrect flags since we will not be sending any
|
||
// data.
|
||
//
|
||
for ( i = 0; i < numCompactWins; i++)
|
||
{
|
||
newCompactWinStruct[i].flags &= ~SWL_FLAG_WINDOW_NONRECTANGLE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Store the NR information
|
||
//
|
||
m_aswlNRSize[newIndex] = cNonRectData;
|
||
m_aswlNRInfo[newIndex] = (LPTSHR_UINT16)pAllocRgnData;
|
||
|
||
//
|
||
// We have finished with the region now.
|
||
//
|
||
DeleteRgn(hrgnNR);
|
||
|
||
//
|
||
// Did the data we stored change from the last time?
|
||
//
|
||
fNonRectangularInfoChanged = ((m_aswlNRSize[0] != m_aswlNRSize[1]) ||
|
||
(memcmp(m_aswlNRInfo[0], m_aswlNRInfo[1],
|
||
m_aswlNRSize[0])));
|
||
|
||
TRACE_OUT(("Non-rectinfo changed %d", fNonRectangularInfoChanged));
|
||
|
||
//
|
||
// Check again for no changes - quit if we can.
|
||
//
|
||
if (fNoTitlesChanged &&
|
||
!fNonRectangularInfoChanged &&
|
||
(m_aswlNumCompactWins[0] == m_aswlNumCompactWins[1]) &&
|
||
(!memcmp(newCompactWinStruct,
|
||
curCompactWinStruct,
|
||
(numCompactWins*sizeof(SWLWINATTRIBUTES)))))
|
||
{
|
||
if (!m_swlfForceSend)
|
||
{
|
||
//
|
||
// This is a normal call and we don't have any changes pending
|
||
// so don't send anything.
|
||
//
|
||
TRACE_OUT(("NORMAL no changes, not sent"));
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is a normal call AND there are pending changes.
|
||
//
|
||
TRACE_OUT(( "NORMAL pending changes, send"));
|
||
if (SWLSendPacket(&(m_aswlCompactWinStructs[m_swlCurIndex * SWL_MAX_WINDOWS]),
|
||
m_aswlNumCompactWins[m_swlCurIndex],
|
||
m_aswlWinNames[m_swlCurIndex],
|
||
m_aswlWinNamesSize[m_swlCurIndex],
|
||
m_aswlNRSize[m_swlCurIndex],
|
||
m_aswlNRInfo[m_swlCurIndex]) )
|
||
{
|
||
//
|
||
// Succesfully sent this so reset the m_swlfForceSend
|
||
// flag.
|
||
//
|
||
m_swlfForceSend = FALSE;
|
||
fRC = SWL_RC_SENT;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Failed to send this packet so don't reset
|
||
// m_swlfForceSend so that we retry next time and return
|
||
// an error.
|
||
//
|
||
fRC = SWL_RC_ERROR;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We can exit here with a changed full window structure but an
|
||
// unchanged compact window structure. By updating the current
|
||
// index we avoid having to compact the window structure next time
|
||
// if the full list doesn't change, ie we will exit on the full
|
||
// list comparison. If the compact structure subsequently changes
|
||
// then the full structure must also change, so we will detect this
|
||
// change.
|
||
//
|
||
m_swlCurIndex = newIndex;
|
||
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Now the window structure has changed so decide what to do.
|
||
//
|
||
m_swlCurIndex = newIndex;
|
||
|
||
//
|
||
// The window structure has changed so try to send it.
|
||
//
|
||
if (SWLSendPacket(&(m_aswlCompactWinStructs[m_swlCurIndex * SWL_MAX_WINDOWS]),
|
||
m_aswlNumCompactWins[m_swlCurIndex],
|
||
m_aswlWinNames[m_swlCurIndex],
|
||
m_aswlWinNamesSize[m_swlCurIndex],
|
||
m_aswlNRSize[m_swlCurIndex],
|
||
m_aswlNRInfo[m_swlCurIndex]) )
|
||
|
||
{
|
||
//
|
||
// We have succesfully sent changes so reset the m_swlfForceSend
|
||
// flag.
|
||
//
|
||
m_swlfForceSend = FALSE;
|
||
fRC = SWL_RC_SENT;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// There were changes but we have failed to send them - set the
|
||
// m_swlfForceSend flag and return error.
|
||
// We must tell DCS scheduling that we need a callback BEFORE any
|
||
// more changes are sent out.
|
||
//
|
||
m_swlfForceSend = TRUE;
|
||
fRC = SWL_RC_ERROR;
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
|
||
DebugExitDWORD(ASHost::SWL_Periodic, fRC);
|
||
return(fRC);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// SWLEnumProc()
|
||
// Callback for top level window enumeration
|
||
//
|
||
BOOL CALLBACK SWLEnumProc(HWND hwnd, LPARAM lParam)
|
||
{
|
||
PSWLENUMSTRUCT pswlEnum = (PSWLENUMSTRUCT)lParam;
|
||
UINT_PTR property;
|
||
UINT windowProp;
|
||
UINT storedWindowProp;
|
||
UINT visibleCount;
|
||
BOOL fVisible;
|
||
BOOL rc = TRUE;
|
||
|
||
DebugEntry(SWLEnumProc);
|
||
|
||
//
|
||
// FIRST, WE DETERMINE THE PROPERTIES FOR THE WINDOW.
|
||
// Get the SWL properties for this window.
|
||
//
|
||
windowProp = (UINT)pswlEnum->pHost->SWL_GetWindowProperty(hwnd);
|
||
|
||
//
|
||
// We'll modify windowProp as we go, so keep a copy of the original
|
||
// value as stored in the window as we may need it later.
|
||
//
|
||
storedWindowProp = windowProp;
|
||
|
||
//
|
||
// HET tracks whether a window is hosted. Find out now and add this
|
||
// info to our window properties for convenience.
|
||
//
|
||
if (pswlEnum->pHost->m_pShare->HET_WindowIsHosted(hwnd))
|
||
{
|
||
windowProp |= SWL_PROP_HOSTED;
|
||
}
|
||
|
||
//
|
||
// Find out whether this window is transparent.
|
||
// A transparent window overpaints the desktop only, ie it is
|
||
// overpainted by all other windows. In other words, we can
|
||
// forget about it (treat it as invisible) unless a toolbar itself
|
||
// is shared. The MSOffice95
|
||
// hidden toolbar is a topmost transparent window (SFR1083).
|
||
// Add a property flag if transparent.
|
||
//
|
||
if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TRANSPARENT)
|
||
{
|
||
windowProp |= SWL_PROP_TRANSPARENT;
|
||
}
|
||
|
||
//
|
||
// If this window is one that we have identified as generating no
|
||
// remote shadows, then treat it as being invisible.
|
||
//
|
||
fVisible = FALSE;
|
||
if (IsWindowVisible(hwnd) &&
|
||
!(windowProp & SWL_PROP_IGNORE) &&
|
||
(!(windowProp & SWL_PROP_TRANSPARENT) || (windowProp & SWL_PROP_HOSTED)))
|
||
{
|
||
//
|
||
// SFR1083: if the window is transparent but it is hosted,
|
||
// we need to send it. In such a case we drop into here to do
|
||
// the normal visibility processing and will handle
|
||
// z-order issues later.
|
||
//
|
||
// We have been informed that a top level window is visible.
|
||
// Make sure its visible countdown value is reset.
|
||
//
|
||
if ((pswlEnum->pHost->m_pShare->m_pasLocal->hetCount != 0) &&
|
||
((windowProp & SWL_PROP_COUNTDOWN_MASK) != SWL_BELIEVE_INVISIBLE_COUNT))
|
||
{
|
||
//
|
||
// We were doing an invisibility countdown for this window
|
||
// but it has re-visibilized, so reset the counter.
|
||
//
|
||
TRACE_OUT(( "Reset visible countdown on hwnd 0x%08x", hwnd));
|
||
property = storedWindowProp;
|
||
property &= ~SWL_PROP_COUNTDOWN_MASK;
|
||
property |= SWL_BELIEVE_INVISIBLE_COUNT;
|
||
|
||
SetProp(hwnd, SWL_ATOM_NAME, (HANDLE)property);
|
||
}
|
||
|
||
//
|
||
// This window is visible
|
||
//
|
||
fVisible = TRUE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// LAURABU BOGUS!
|
||
// With NM 3.0, who cares? It's only 2.x systems that will kill
|
||
// then recreate the shadow, causing flicker.
|
||
//
|
||
|
||
//
|
||
// We are told that this top level window is invisible.
|
||
// Check whether we're going to believe it.
|
||
// Some applications (ie WordPerfect, Freelance Graphics)
|
||
// upset AS-Shares window structure handling by doing something
|
||
// like this:
|
||
//
|
||
// Make a window invisible
|
||
// Do some processing which would not normally yield
|
||
// Make the window visible again
|
||
//
|
||
// There is a chance that DC-Share will get scheduled whilst
|
||
// the window is invisible (because of our cunning scheduling)
|
||
// and we will think the window is invisible when it is not.
|
||
//
|
||
// Also, 32bit tasks that use similar methods (Eg Word95,
|
||
// Freelance graphics and WM_SETREDRAW messages) may be
|
||
// interrupted while the window is (temporarily) marked as
|
||
// invisible. When the CORE is scheduled we may, again, think
|
||
// that the window is invisible when it is not.
|
||
//
|
||
// To overcome this the SWL window property contains a
|
||
// visibility count, initially set to
|
||
// SWL_BELIEVE_INVISIBLE_COUNT. Following a visible to
|
||
// invisible switch, the counter is decremented and only when
|
||
// it reaches zero does SWL believe that the window is
|
||
// invisible. The counter is reset when a window is detected as
|
||
// visible and the counter is not SWL_BELIEVE_INVISIBLE_COUNT.
|
||
//
|
||
// This would be fine but there are windows when we mistakenly
|
||
// pretend that a window which really has become invisible
|
||
// (rather than one which is transitionally invisible) is
|
||
// visible. This is exposed by menus and dialog boxes. To
|
||
// reduce this problem we will never pretend a window is
|
||
// visible if its class has a CS_SAVEBITS style which should
|
||
// be the case for windows which are transitionally
|
||
// visible like menus and dialog boxes.
|
||
//
|
||
// SFR1083: always treat a transparent window as invisible
|
||
//
|
||
if ( !(windowProp & SWL_PROP_TRANSPARENT) &&
|
||
!(windowProp & SWL_PROP_SAVEBITS) )
|
||
{
|
||
visibleCount = windowProp & SWL_PROP_COUNTDOWN_MASK;
|
||
if ((visibleCount != 0) && (pswlEnum->pHost->m_pShare->m_pasLocal->hetCount > 0))
|
||
{
|
||
//
|
||
// We are still treating this window as visible, ie we
|
||
// are doing a visibilty countdown. Update the count in
|
||
// the window property.
|
||
//
|
||
visibleCount--;
|
||
property = ~SWL_PROP_COUNTDOWN_MASK & storedWindowProp;
|
||
property |= visibleCount;
|
||
|
||
TRACE_OUT(( "Decrement visible countdown on window 0x%08x to %d",
|
||
hwnd, visibleCount));
|
||
|
||
SetProp(hwnd, SWL_ATOM_NAME, MAKEINTATOM(property));
|
||
|
||
//
|
||
// Delay sending of updates since the remote still
|
||
// has a window structure which includes this window
|
||
// but it is not on the local screen (so any updates
|
||
// sent may be for the area where this window was and
|
||
// the remote will not show them).
|
||
//
|
||
pswlEnum->fBailOut = TRUE;
|
||
rc = FALSE;
|
||
DC_QUIT;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Only concerned about visible windows.
|
||
//
|
||
if (fVisible)
|
||
{
|
||
pswlEnum->pHost->SWL_InitFullWindowListEntry(hwnd, windowProp,
|
||
&(pswlEnum->newWinNames),
|
||
&(pswlEnum->newFullWinStruct[pswlEnum->count]));
|
||
|
||
//
|
||
// If we've added a transparent window then remember this.
|
||
//
|
||
if (windowProp & SWL_PROP_TRANSPARENT)
|
||
{
|
||
pswlEnum->transparentCount++;
|
||
}
|
||
|
||
//
|
||
// Update index
|
||
//
|
||
pswlEnum->count++;
|
||
if (pswlEnum->count == SWL_MAX_WINDOWS)
|
||
{
|
||
//
|
||
// We've reached our limit on # of top level windows, so bail
|
||
// out.
|
||
//
|
||
WARNING_OUT(("SWL_MAX_WINDOWS exceeded"));
|
||
rc = FALSE;
|
||
}
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitBOOL(SWLEnumProc, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
//
|
||
// SWLSendPacket()
|
||
//
|
||
// Called when the shared apps of this node have changed shape/text/position/
|
||
// zorder or there have been new windows created/old shared windows destroyed.
|
||
// We must send these updates out to the remote systems.
|
||
//
|
||
// RETURNS: TRUE or FALSE - success of failure.
|
||
//
|
||
//
|
||
BOOL ASHost::SWLSendPacket
|
||
(
|
||
PSWLWINATTRIBUTES pWindows,
|
||
UINT numWindows,
|
||
LPSTR pTitles,
|
||
UINT lenTitles,
|
||
UINT NRInfoSize,
|
||
LPTSHR_UINT16 pNRInfo
|
||
)
|
||
{
|
||
PSWLPACKET pSWLPacket;
|
||
UINT sizeWindowPkt;
|
||
UINT i;
|
||
LPSTR pString;
|
||
LPBYTE pCopyLocation;
|
||
UINT cCopySize;
|
||
SWLPACKETCHUNK chunk;
|
||
#ifdef _DEBUG
|
||
UINT sentSize;
|
||
#endif // _DEBUG
|
||
|
||
DebugEntry(ASHost::SWLSendPacket);
|
||
|
||
if (m_pShare->m_pasLocal->hetCount != 0)
|
||
{
|
||
//
|
||
// This is a real packet, not an empty one
|
||
//
|
||
if (!UP_MaybeSendSyncToken())
|
||
{
|
||
//
|
||
// We needed to send a sync token and couldn't so just return
|
||
// failure immediately.
|
||
//
|
||
TRACE_OUT(( "couldn't send sync token"));
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// How big a packet do we need?
|
||
//
|
||
sizeWindowPkt = sizeof(SWLPACKET) + (numWindows - 1) * sizeof(SWLWINATTRIBUTES)
|
||
+ lenTitles;
|
||
|
||
//
|
||
// Add in the size of the regional window information, plus the
|
||
// size we need for the chunk header.
|
||
//
|
||
if (NRInfoSize)
|
||
{
|
||
if (lenTitles & 1)
|
||
{
|
||
//
|
||
// We need an extra byte for correct alignment
|
||
//
|
||
sizeWindowPkt++;
|
||
}
|
||
|
||
sizeWindowPkt += NRInfoSize + sizeof(SWLPACKETCHUNK);
|
||
}
|
||
|
||
//
|
||
// Allocate a packet for the windows data.
|
||
//
|
||
pSWLPacket = (PSWLPACKET)m_pShare->SC_AllocPkt(PROT_STR_UPDATES, g_s20BroadcastID,
|
||
sizeWindowPkt);
|
||
if (!pSWLPacket)
|
||
{
|
||
WARNING_OUT(("Failed to alloc SWL packet, size %u", sizeWindowPkt));
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Packet successfully allocated. Fill in the data and send it.
|
||
//
|
||
pSWLPacket->header.data.dataType = DT_SWL;
|
||
|
||
pSWLPacket->msg = SWL_MSG_WINSTRUCT;
|
||
pSWLPacket->flags = 0;
|
||
if (m_swlfSyncing)
|
||
{
|
||
pSWLPacket->flags |= SWL_FLAG_STATE_SYNCING;
|
||
m_swlfSyncing = FALSE;
|
||
}
|
||
|
||
pSWLPacket->numWindows = (TSHR_UINT16)numWindows;
|
||
|
||
pCopyLocation = (LPBYTE)pSWLPacket->aWindows;
|
||
cCopySize = numWindows*sizeof(SWLWINATTRIBUTES);
|
||
memcpy(pCopyLocation, pWindows, cCopySize);
|
||
|
||
//
|
||
// Copy the title information
|
||
//
|
||
pCopyLocation += cCopySize;
|
||
cCopySize = lenTitles;
|
||
memcpy(pCopyLocation, pTitles, cCopySize);
|
||
|
||
//
|
||
// Copy any non-rectangular window information.
|
||
//
|
||
if (NRInfoSize)
|
||
{
|
||
pCopyLocation += cCopySize;
|
||
|
||
//
|
||
// The chunk must be word aligned in the packet
|
||
//
|
||
if (lenTitles & 1)
|
||
{
|
||
//
|
||
// An odd number of bytes of window titles has misaligned us,
|
||
// so write a 0 (compresses best!) to realign the pointer.
|
||
//
|
||
*pCopyLocation++ = 0;
|
||
}
|
||
|
||
//
|
||
// Write the chunk header
|
||
//
|
||
chunk.size = (TSHR_INT16)(NRInfoSize + sizeof(chunk));
|
||
chunk.idChunk = SWL_PACKET_ID_NONRECT;
|
||
cCopySize = sizeof(chunk);
|
||
memcpy(pCopyLocation, &chunk, cCopySize);
|
||
|
||
//
|
||
// Now write the variable info itself
|
||
//
|
||
pCopyLocation += cCopySize;
|
||
cCopySize = NRInfoSize;
|
||
memcpy(pCopyLocation, pNRInfo, cCopySize);
|
||
|
||
TRACE_OUT(("Non rect data length %d",NRInfoSize));
|
||
}
|
||
|
||
//
|
||
// Backwards compatibility.
|
||
//
|
||
pSWLPacket->tick = (TSHR_UINT16)GetTickCount();
|
||
pSWLPacket->token = m_pShare->SWL_CalculateNextToken(m_pShare->m_swlLastTokenSeen);
|
||
|
||
TRACE_OUT(("Updating m_swlLastTokenSeen to 0x%08x for sent packet",
|
||
pSWLPacket->token));
|
||
m_pShare->m_swlLastTokenSeen = pSWLPacket->token;
|
||
|
||
pSWLPacket->reserved = 0;
|
||
|
||
#ifdef _DEBUG
|
||
{
|
||
int i;
|
||
int cWins;
|
||
PSWLWINATTRIBUTES pSwl;
|
||
|
||
// Trace out the entries
|
||
pSwl = pSWLPacket->aWindows;
|
||
cWins = pSWLPacket->numWindows;
|
||
|
||
TRACE_OUT(("SWLSendPacket: Sending packet with %d windows", cWins));
|
||
for (i = 0; i < cWins; i++, pSwl++)
|
||
{
|
||
TRACE_OUT(("SWLSendPacket: Entry %d", i));
|
||
TRACE_OUT(("SWLSendPacket: Flags %08x", pSwl->flags));
|
||
TRACE_OUT(("SWLSendPacket: Window %08x", pSwl->winID));
|
||
TRACE_OUT(("SWLSendPacket: Position {%04d, %04d, %04d, %04d}",
|
||
pSwl->position.left, pSwl->position.top,
|
||
pSwl->position.right, pSwl->position.bottom));
|
||
}
|
||
}
|
||
#endif // _DEBUG
|
||
|
||
//
|
||
// Send the windows packet on the UPDATE stream.
|
||
//
|
||
if (m_pShare->m_scfViewSelf)
|
||
m_pShare->SWL_ReceivedPacket(m_pShare->m_pasLocal, &pSWLPacket->header);
|
||
|
||
#ifdef _DEBUG
|
||
sentSize =
|
||
#endif // _DEBUG
|
||
m_pShare->DCS_CompressAndSendPacket(PROT_STR_UPDATES, g_s20BroadcastID,
|
||
&(pSWLPacket->header), sizeWindowPkt);
|
||
|
||
TRACE_OUT(("SWL packet size: %08d, sent %08d", sizeWindowPkt, sentSize));
|
||
|
||
DebugExitBOOL(ASHost::SWLSendPacket, TRUE);
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// SWL_CalculateNextToken()
|
||
//
|
||
// This calculates the next token to put in an outgoing SWL packet. This is
|
||
// only looked at by backlevel systems (<= NM 2.1) who treat all incoming
|
||
// SWL streams in one big messy global fashion. So we need to put something
|
||
// there, something that won't scare them but ensure that our
|
||
// packets aren't ignored if at all possible.
|
||
//
|
||
TSHR_UINT16 ASShare::SWL_CalculateNextToken(TSHR_UINT16 currentToken)
|
||
{
|
||
UINT increment;
|
||
TSHR_UINT16 newToken;
|
||
|
||
DebugEntry(ASShare::SWL_CalculateNextToken);
|
||
|
||
//
|
||
// We use the highest priority increment to make sure our packets get
|
||
// through. But will this cause collisions with other 3.0 sharers?
|
||
// Try lowest priority if necessary.
|
||
//
|
||
increment = SWL_NEW_ZORDER_FAKE_WINDOW_INC;
|
||
|
||
//
|
||
// Return the new token
|
||
//
|
||
newToken = SWL_MAKE_TOKEN(
|
||
SWL_GET_INDEX(currentToken) + SWL_GET_INCREMENT(currentToken), increment);
|
||
|
||
DebugExitDWORD(ASShare::SWL_CalculateNextToken, newToken);
|
||
return(newToken);
|
||
}
|
||
|
||
|
||
//
|
||
// SWL_ReceivedPacket()
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Processes a windows structure packet which has been received from the
|
||
// PR. This defines the position of the shared windows hosted on the
|
||
// remote system, any obscured regions, and the Z-order relative to the
|
||
// shared windows hosted locally.
|
||
//
|
||
// NOTE: We don't do any token stuff for _incoming_ packets; we never
|
||
// want to drop them since we aren't zordering anything locally. We are
|
||
// simply applying the zorder/region/position info to the client area
|
||
// drawing.
|
||
//
|
||
void ASShare::SWL_ReceivedPacket
|
||
(
|
||
ASPerson * pasFrom,
|
||
PS20DATAPACKET pPacket
|
||
)
|
||
{
|
||
PSWLPACKET pSWLPacket;
|
||
UINT i;
|
||
UINT j;
|
||
PSWLWINATTRIBUTES wins;
|
||
UINT numWins;
|
||
HRGN hrgnShared;
|
||
HRGN hrgnObscured;
|
||
HRGN hrgnThisWindow;
|
||
HRGN hrgnRect;
|
||
LPTSHR_INT16 pOurRgnData;
|
||
LPSTR pOurRgnChunk;
|
||
UINT cNonRectWindows;
|
||
BOOL viewAnyChanges;
|
||
|
||
DebugEntry(ASShare::SWL_ReceivedPacket);
|
||
|
||
ValidatePerson(pasFrom);
|
||
|
||
pSWLPacket = (PSWLPACKET)pPacket;
|
||
switch (pSWLPacket->msg)
|
||
{
|
||
//
|
||
// This is the only packet we currently recognize.
|
||
//
|
||
case SWL_MSG_WINSTRUCT:
|
||
break;
|
||
|
||
default:
|
||
WARNING_OUT(("Unknown SWL packet msg %d from [%d]",
|
||
pSWLPacket->msg, pasFrom->mcsID));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Update the last token we've seen, if it's greater than the last
|
||
// one we know about. Unlike 2.x, we don't drop this packet if it isn't.
|
||
//
|
||
if (pSWLPacket->token > m_swlLastTokenSeen)
|
||
{
|
||
TRACE_OUT(("Updating m_swlLastTokenSeen to 0x%08x, received packet from person [%d]",
|
||
pSWLPacket->token, pasFrom->mcsID));
|
||
m_swlLastTokenSeen = pSWLPacket->token;
|
||
}
|
||
else if (pasFrom->cpcCaps.general.version < CAPS_VERSION_30)
|
||
{
|
||
WARNING_OUT(("Received SWL packet from [%d] with stale token 0x%08x",
|
||
pasFrom->mcsID, pSWLPacket->token));
|
||
}
|
||
|
||
//
|
||
// Return immediately and ignore this baby if we aren't sharing. Back
|
||
// level systems may send us a SYNC packet with no windows before we've
|
||
// shared, and may send us one final SWL packet after we're done
|
||
// sharing.
|
||
//
|
||
if (!pasFrom->m_pView)
|
||
{
|
||
WARNING_OUT(("SWL_ReceivedPacket: Ignoring SWL packet from person [%d] not hosting",
|
||
pasFrom->mcsID));
|
||
DC_QUIT;
|
||
}
|
||
|
||
//
|
||
// Set up local variables to access the data in the packet
|
||
//
|
||
wins = pSWLPacket->aWindows;
|
||
numWins = pSWLPacket->numWindows;
|
||
pOurRgnChunk = (LPSTR)wins + numWins*sizeof(SWLWINATTRIBUTES);
|
||
|
||
TRACE_OUT(("SWL_ReceivedPacket: Received packet with %d windows from [%d]",
|
||
numWins, pasFrom->mcsID));
|
||
|
||
//
|
||
// We can't handle more than SWL_MAX_WINDOWS in the packet
|
||
// BOGUS:
|
||
// LauraBu -- We should negotiate this (make it a cap) on how many
|
||
// windows we can handle receiving. Then we have an easy path to
|
||
// increase this number.
|
||
//
|
||
if (numWins > SWL_MAX_WINDOWS)
|
||
{
|
||
ERROR_OUT(("SWL_ReceivedPacket: too many windows (%04d) in packet from [%08d]",
|
||
numWins, pasFrom->mcsID));
|
||
DC_QUIT;
|
||
}
|
||
|
||
cNonRectWindows = 0;
|
||
|
||
//
|
||
// The first pass over the arriving packet is to count the amount of
|
||
// region data and to update the window tray.
|
||
//
|
||
viewAnyChanges = FALSE;
|
||
|
||
//
|
||
// This part we process front to back, since that's the order of the
|
||
// strings and we use them for putting entries on the traybar.
|
||
//
|
||
for (i = 0; i < numWins; i++)
|
||
{
|
||
// Mask out bogus old bits that aren't OK to process
|
||
wins[i].flags &= SWL_FLAGS_VALIDPACKET;
|
||
|
||
TRACE_OUT(("SWL_ReceivedPacket: Entry %d", i));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Flags %08x", wins[i].flags));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Window %08x", wins[i].winID));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Position {%04d, %04d, %04d, %04d}",
|
||
wins[i].position.left, wins[i].position.top,
|
||
wins[i].position.right, wins[i].position.bottom));
|
||
|
||
//
|
||
// NOTE:
|
||
// 2.x nodes may send us a packet with an entry for a shadow.
|
||
// Go look up the REAL shadow rect from its host.
|
||
//
|
||
// And fix up the SWL packet then.
|
||
//
|
||
if (wins[i].flags & SWL_FLAG_WINDOW_SHADOW)
|
||
{
|
||
ASPerson * pasRealHost;
|
||
|
||
TRACE_OUT(("SWLReceivedPacket: Entry is 2.x SHADOW for host [%d]",
|
||
wins[i].extra));
|
||
|
||
// This must be a back level dude, giving us an empty rect.
|
||
ASSERT(wins[i].position.left == 0);
|
||
ASSERT(wins[i].position.top == 0);
|
||
ASSERT(wins[i].position.right == 0);
|
||
ASSERT(wins[i].position.bottom == 0);
|
||
|
||
// Find the real host of this window
|
||
SC_ValidateNetID(wins[i].extra, &pasRealHost);
|
||
if (pasRealHost != NULL)
|
||
{
|
||
int cSwl = 0;
|
||
PSWLWINATTRIBUTES pSwl = NULL;
|
||
|
||
// Try to find this window's entry
|
||
|
||
if (pasRealHost == m_pasLocal)
|
||
{
|
||
//
|
||
// This was shared by US. We can just use the scratch
|
||
// arrays we already have. m_swlCurIndex has the last
|
||
// one we sent out to everybody in the share, so the
|
||
// info it has is most likely reflected on that 2x
|
||
// remote.
|
||
//
|
||
if (m_pHost != NULL)
|
||
{
|
||
cSwl = m_pHost->m_aswlNumCompactWins[m_pHost->m_swlCurIndex];
|
||
pSwl = &(m_pHost->m_aswlCompactWinStructs[m_pHost->m_swlCurIndex * SWL_MAX_WINDOWS]);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This was shared by somebody else, not us and not
|
||
// the person who sent this SWL packet. So go use the
|
||
// last SWL info we received from them.
|
||
//
|
||
if (pasRealHost->m_pView)
|
||
{
|
||
cSwl = pasRealHost->m_pView->m_swlCount;
|
||
pSwl = pasRealHost->m_pView->m_aswlLast;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Loop through the window list for the real host to
|
||
// find the entry--we'll use the last REAL rect we got
|
||
// for this window.
|
||
//
|
||
while (cSwl > 0)
|
||
{
|
||
if (wins[i].winID == pSwl->winID)
|
||
{
|
||
// Copy the _real_ position into the packet.
|
||
TRACE_OUT(("SWLReceivedPacket: Using real rect {%04d, %04d, %04d, %04d}",
|
||
pSwl->position.left, pSwl->position.top,
|
||
pSwl->position.right, pSwl->position.bottom));
|
||
|
||
wins[i].position = pSwl->position;
|
||
break;
|
||
}
|
||
|
||
cSwl--;
|
||
pSwl++;
|
||
}
|
||
|
||
if (cSwl == 0)
|
||
{
|
||
ERROR_OUT(("SWLReceivedPacket: Couldn't find real window %08x from host [%d]",
|
||
wins[i].winID, wins[i].extra));
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// 2.x nodes send us VD coords, not screen coords. But that's what
|
||
// we display for them, so that's what we save away. Note that this
|
||
// works even in the 2.x shadow case above. Hosted and shadowed
|
||
// windows both get moved in a desktop scroll, so they stay in the
|
||
// same place in the virtual desktop, meaning that the coords sent
|
||
// from the host stay the same even if the windows move, meaning that
|
||
// we can use the coords of the real host to get the real shadow
|
||
// rect.
|
||
//
|
||
|
||
if (wins[i].flags & SWL_FLAG_WINDOW_HOSTED)
|
||
{
|
||
TRACE_OUT(("SWL_ReceivedPacket: Hosted Window 0x%08x", wins[i].winID));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Text %s", ((*pOurRgnChunk == '\xff') ? "" : pOurRgnChunk)));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Flags %08x", wins[i].flags));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Owner %08x", wins[i].ownerWinID));
|
||
TRACE_OUT(("SWL_ReceivedPacket: Position {%04d, %04d, %04d, %04d}",
|
||
wins[i].position.left, wins[i].position.top,
|
||
wins[i].position.right, wins[i].position.bottom));
|
||
|
||
//
|
||
// We are stepping through the titles (which get sent from
|
||
// downlevel systems) which do not contain an
|
||
// explicit length) so that we can get to the data that follows
|
||
//
|
||
if (*pOurRgnChunk == '\xff')
|
||
{
|
||
//
|
||
// This is the title for a non-task window - there is just
|
||
// a single byte to ignore
|
||
//
|
||
pOurRgnChunk++;
|
||
}
|
||
else
|
||
{
|
||
|
||
//
|
||
// This is the title for a task window - there is a NULL
|
||
// terminated string to ignore.
|
||
//
|
||
if (wins[i].flags & SWL_FLAG_WINDOW_TASKBAR)
|
||
{
|
||
if (VIEW_WindowBarUpdateItem(pasFrom, &wins[i], pOurRgnChunk))
|
||
{
|
||
viewAnyChanges = TRUE;
|
||
}
|
||
}
|
||
pOurRgnChunk += lstrlen(pOurRgnChunk)+1;
|
||
}
|
||
}
|
||
|
||
if (wins[i].flags & SWL_FLAG_WINDOW_NONRECTANGLE)
|
||
{
|
||
//
|
||
// We need to know how many windows have non rectangular data
|
||
// provided.
|
||
//
|
||
cNonRectWindows++;
|
||
}
|
||
}
|
||
|
||
if (cNonRectWindows)
|
||
{
|
||
TRACE_OUT(( "%d non-rect windows", cNonRectWindows));
|
||
|
||
//
|
||
// The window title data is variable length bytes, so may end with
|
||
// incorrect alignment. Any data which follows (currently only
|
||
// non-rectangular windows data) is word aligned.
|
||
//
|
||
// So check if offset from beginning of data is not aligned. Note
|
||
// that the packet may start on an ODD boundary because we get
|
||
// a pointer to the data directly and don't allocate a copy.
|
||
//
|
||
if ((LOWORD(pSWLPacket) & 1) != (LOWORD(pOurRgnChunk) & 1))
|
||
{
|
||
TRACE_OUT(("SWL_ReceivedPacket: Aligning region data"));
|
||
pOurRgnChunk++;
|
||
}
|
||
|
||
//
|
||
// Loop through the tagged chunks that follow until we find the
|
||
// one we want.
|
||
//
|
||
while (((PSWLPACKETCHUNK)pOurRgnChunk)->idChunk != SWL_PACKET_ID_NONRECT)
|
||
{
|
||
ERROR_OUT(("SWL_ReceivedPacket: unknown chunk 0x%04x",
|
||
((PSWLPACKETCHUNK)pOurRgnChunk)->idChunk));
|
||
|
||
pOurRgnChunk += ((PSWLPACKETCHUNK)pOurRgnChunk)->size;
|
||
}
|
||
|
||
TRACE_OUT(("Total non rect data 0x%04x", ((PSWLPACKETCHUNK)pOurRgnChunk)->size));
|
||
}
|
||
|
||
//
|
||
// Now scan the wins array backwards (ie furthest away to closest
|
||
// window) to calculate the unshared region (obscured or nothing there).
|
||
// and the shared region.
|
||
//
|
||
hrgnShared = CreateRectRgn(0, 0, 0, 0);
|
||
hrgnObscured = CreateRectRgn(0, 0, 0, 0);
|
||
|
||
//
|
||
// Create a region we can make use of in the next bit of processing.
|
||
//
|
||
hrgnRect = CreateRectRgn(0, 0, 0, 0);
|
||
hrgnThisWindow = CreateRectRgn(0, 0, 0, 0);
|
||
|
||
//
|
||
// While we are building the shared/obscured regions, also fill in
|
||
// the host list. Note that this may contain references to local
|
||
// windows also if they obscure shared ones. Since we don't reference
|
||
// the list very often, it's easier to just copy the same stuff.
|
||
//
|
||
|
||
i = numWins;
|
||
while (i != 0)
|
||
{
|
||
i--;
|
||
|
||
//
|
||
// Consider whether this is a non rectangular window
|
||
//
|
||
if (wins[i].flags & SWL_FLAG_WINDOW_NONRECTANGLE)
|
||
{
|
||
UINT numRects;
|
||
UINT cStepOver;
|
||
int top;
|
||
int left;
|
||
int right;
|
||
int bottom;
|
||
int lasttop;
|
||
int lastleft;
|
||
int lastright;
|
||
int lastbottom;
|
||
int deltaleft;
|
||
int deltatop;
|
||
int deltaright;
|
||
int deltabottom;
|
||
int lastdeltaleft;
|
||
int lastdeltatop;
|
||
int lastdeltaright;
|
||
int lastdeltabottom;
|
||
|
||
//
|
||
// A non-rectangular region. We go ahead and create the region
|
||
// from the rectangles that describe it.
|
||
//
|
||
pOurRgnData = (LPTSHR_INT16)(pOurRgnChunk + sizeof(SWLPACKETCHUNK));
|
||
|
||
//
|
||
// We need to step through the non-rectangular data because we
|
||
// are processing windows in reverse z-order.
|
||
//
|
||
cStepOver = --cNonRectWindows;
|
||
while (cStepOver--)
|
||
{
|
||
//
|
||
// The next word in the chain contains the number of
|
||
// rectangles, so we multiply by 4 to get the number of
|
||
// words to advance.
|
||
//
|
||
pOurRgnData += *pOurRgnData++ * 4;
|
||
}
|
||
|
||
//
|
||
// Find the number of rectangles.
|
||
//
|
||
numRects = *pOurRgnData++;
|
||
|
||
//
|
||
// The encoding is based on a series of deltas, based on some
|
||
// initial assumptions
|
||
//
|
||
lastleft = 0;
|
||
lasttop = 0;
|
||
lastright = 0;
|
||
lastbottom = 0;
|
||
|
||
lastdeltaleft = 0;
|
||
lastdeltatop = 0;
|
||
lastdeltaright = 0;
|
||
lastdeltabottom = 0;
|
||
|
||
//
|
||
// Create the region from the first rectangle.
|
||
//
|
||
deltaleft = lastdeltaleft + *pOurRgnData++;
|
||
deltatop = lastdeltatop + *pOurRgnData++;
|
||
deltaright = lastdeltaright + *pOurRgnData++;
|
||
deltabottom = lastdeltabottom + *pOurRgnData++;
|
||
|
||
left = lastleft + deltaleft;
|
||
top = lasttop + deltatop;
|
||
right = lastright + deltaright;
|
||
bottom = lastbottom + deltabottom;
|
||
|
||
// THESE COORDS ARE INCLUSIVE, SO ADD ONE
|
||
SetRectRgn(hrgnThisWindow, left, top, right+1, bottom+1);
|
||
|
||
while (--numRects > 0)
|
||
{
|
||
|
||
//
|
||
// Move to the next rectangle.
|
||
//
|
||
lastleft = left;
|
||
lasttop = top;
|
||
lastright = right;
|
||
lastbottom = bottom;
|
||
lastdeltaleft = deltaleft;
|
||
lastdeltatop = deltatop;
|
||
lastdeltaright = deltaright;
|
||
lastdeltabottom = deltabottom;
|
||
|
||
deltaleft = lastdeltaleft + *pOurRgnData++;
|
||
deltatop = lastdeltatop + *pOurRgnData++;
|
||
deltaright = lastdeltaright + *pOurRgnData++;
|
||
deltabottom = lastdeltabottom + *pOurRgnData++;
|
||
|
||
left = lastleft + deltaleft;
|
||
top = lasttop + deltatop;
|
||
right = lastright + deltaright;
|
||
bottom = lastbottom + deltabottom;
|
||
|
||
//
|
||
// Get the current rectangle into a region.
|
||
// THESE COORDS ARE INCLUSIVE SO ADD ONE TO BOTTOM-RIGHT
|
||
//
|
||
SetRectRgn(hrgnRect, left, top, right+1, bottom+1);
|
||
|
||
//
|
||
// Add this region to the combined region.
|
||
//
|
||
UnionRgn(hrgnThisWindow, hrgnRect, hrgnThisWindow);
|
||
}
|
||
|
||
//
|
||
// Switch from window coords to desktop coords.
|
||
//
|
||
OffsetRgn(hrgnThisWindow,
|
||
wins[i].position.left,
|
||
wins[i].position.top);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This window region is simply a rectangle.
|
||
|
||
SetRectRgn(hrgnThisWindow,
|
||
wins[i].position.left,
|
||
wins[i].position.top,
|
||
wins[i].position.right+1,
|
||
wins[i].position.bottom+1);
|
||
}
|
||
|
||
//
|
||
// Update the obscured region. As we are working from the back to
|
||
// the front of the Z-order we can simply add all local window
|
||
// entries in the incoming structure and subtract all hosted
|
||
// windows to arrive at the right answer.
|
||
//
|
||
if (wins[i].flags & SWL_FLAG_WINDOW_HOSTED)
|
||
{
|
||
//
|
||
// This is a hosted window, sitting above the previous ones.
|
||
// Add it to the shared region.
|
||
// Remove it from the obscured region.
|
||
//
|
||
UnionRgn(hrgnShared, hrgnShared, hrgnThisWindow);
|
||
SubtractRgn(hrgnObscured, hrgnObscured, hrgnThisWindow);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Local windows
|
||
//
|
||
TRACE_OUT(( "Adding window %d (%d,%d):(%d,%d) to obscured rgn",
|
||
i,
|
||
wins[i].position.left,
|
||
wins[i].position.top,
|
||
wins[i].position.right,
|
||
wins[i].position.bottom ));
|
||
|
||
//
|
||
// This is a local window, sitting above the previous ones.
|
||
// We only care about what part of it intersects the current
|
||
// shared area of the windows behind it. If it doesn't
|
||
// intersect the shared area at all, it will add no new
|
||
// obscured bits.
|
||
//
|
||
// So figure out what part of the current shared area is now
|
||
// obscured. Add that piece to the obscured region, and
|
||
// subtract it from the shared region.
|
||
//
|
||
IntersectRgn(hrgnThisWindow, hrgnShared, hrgnThisWindow);
|
||
UnionRgn(hrgnObscured, hrgnObscured, hrgnThisWindow);
|
||
SubtractRgn(hrgnShared, hrgnShared, hrgnThisWindow);
|
||
}
|
||
}
|
||
|
||
//
|
||
// We can destroy the regions we created way back when.
|
||
//
|
||
DeleteRgn(hrgnRect);
|
||
DeleteRgn(hrgnThisWindow);
|
||
|
||
//
|
||
// Save the new host regions.
|
||
//
|
||
// Pass the newly calculated regions to the Shadow Window Presenter.
|
||
// The view code will take care of repainting the invalid parts. And
|
||
// will delete what was passed in if not kept.
|
||
//
|
||
VIEW_SetHostRegions(pasFrom, hrgnShared, hrgnObscured);
|
||
|
||
//
|
||
// Save the new window list as the current one.
|
||
//
|
||
pasFrom->m_pView->m_swlCount = numWins;
|
||
memcpy(pasFrom->m_pView->m_aswlLast, wins, numWins * sizeof(SWLWINATTRIBUTES));
|
||
|
||
//
|
||
// Finish updating the window list. This will repaint the tray bar. We
|
||
// do this now instead of earlier so that the visual changes and
|
||
// window bar changes appear together.
|
||
//
|
||
VIEW_WindowBarEndUpdateItems(pasFrom, viewAnyChanges);
|
||
|
||
if ((pSWLPacket->flags & SWL_FLAG_STATE_SYNCING) &&
|
||
(m_scShareVersion < CAPS_VERSION_30))
|
||
{
|
||
//
|
||
// With 2.x nodes in the picture, we need to do the old 2.x ping-
|
||
// pongy nonsense. We must force a packet if we're hosting when
|
||
// we receive a SYNC packet.
|
||
//
|
||
if (m_pHost)
|
||
{
|
||
m_pHost->m_swlfForceSend = TRUE;
|
||
}
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitVOID(ASShare::SWL_ReceivedPacket);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Name: SWLWindowIsTaggable
|
||
//
|
||
// Purpose: Determine if a window would be taggable when hosted
|
||
//
|
||
// Returns: TRUE if the window would be taggable
|
||
// If the window is WS_EX_APPWINDOW or has a caption, it's tagged
|
||
//
|
||
// Params: winid - ID of window
|
||
//
|
||
//
|
||
BOOL ASHost::SWLWindowIsTaggable(HWND hwnd)
|
||
{
|
||
BOOL rc;
|
||
|
||
DebugEntry(ASHost::SWLWindowIsTaggable);
|
||
|
||
if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_APPWINDOW)
|
||
rc = TRUE;
|
||
else if ((GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION)
|
||
rc = TRUE;
|
||
else
|
||
rc = FALSE;
|
||
|
||
DebugExitBOOL(ASHost::SWLWindowIsTaggable, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
//
|
||
// FUNCTION: SWLWindowIsOnTaskBar
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Determines whether the given window is represented on the task bar
|
||
//
|
||
// PARAMETERS:
|
||
//
|
||
// hwnd - window to be queried
|
||
//
|
||
// RETURNS:
|
||
//
|
||
// TRUE - Window is represented on the task bar
|
||
//
|
||
// FALSE - Window is not represented on the task bar
|
||
//
|
||
//
|
||
BOOL ASHost::SWLWindowIsOnTaskBar(HWND hwnd)
|
||
{
|
||
BOOL rc = FALSE;
|
||
HWND owner;
|
||
RECT rect;
|
||
|
||
DebugEntry(ASHost::SWLWindowIsOnTaskBar);
|
||
|
||
//
|
||
// Our best understanding as to whether a window is on the task bar is
|
||
// the following:
|
||
//
|
||
// - it is a top level window (has no owner)
|
||
// AND - it does not have the WS_EX_TOOLWINDOW style
|
||
//
|
||
// Oprah1655: Visual Basic apps consist of a visible zero sized window
|
||
// with no owner and a window owned by the zero sized window. We do
|
||
// not want the zero sized window to be on the task bar, we do want the
|
||
// other window to be on the task bar.
|
||
//
|
||
//
|
||
owner = GetWindow(hwnd, GW_OWNER);
|
||
|
||
if (owner == NULL)
|
||
{
|
||
if (!(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW))
|
||
{
|
||
GetWindowRect(hwnd, &rect);
|
||
|
||
if ((rect.left < rect.right) &&
|
||
(rect.top < rect.bottom))
|
||
{
|
||
TRACE_OUT(("window 0x%08x allowed on task bar", hwnd));
|
||
rc = TRUE;
|
||
}
|
||
else
|
||
{
|
||
TRACE_OUT(( "window 0x%08x zero sized", hwnd));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Is the owner window a top-level window of zero size?
|
||
//
|
||
if (GetWindow(owner, GW_OWNER) == NULL)
|
||
{
|
||
GetWindowRect(owner, &rect);
|
||
|
||
if (IsRectEmpty(&rect))
|
||
{
|
||
TRACE_OUT(("HWND 0x%08x has zero sized top-level owner",
|
||
hwnd));
|
||
rc = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
DebugExitDWORD(ASHost::SWLWindowIsOnTaskBar, rc);
|
||
return(rc);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// SWL_GetWindowProperty()
|
||
//
|
||
UINT_PTR ASHost::SWL_GetWindowProperty(HWND hwnd)
|
||
{
|
||
UINT_PTR properties;
|
||
char className[HET_CLASS_NAME_SIZE];
|
||
|
||
DebugEntry(ASHost::SWL_GetWindowProperty);
|
||
|
||
properties = (UINT_PTR)GetProp(hwnd, MAKEINTATOM(m_swlPropAtom));
|
||
if (properties != SWL_PROP_INVALID)
|
||
DC_QUIT;
|
||
|
||
//
|
||
// No property for this window - it must be new, so create its
|
||
// initial property state.
|
||
//
|
||
|
||
//
|
||
// Assign an initial value to the property, so we never set a property
|
||
// of zero (which we reserve to indicate invalid).
|
||
//
|
||
properties = SWL_PROP_INITIAL;
|
||
|
||
//
|
||
// TAGGABLE IS FOR < 3.0 nodes only.
|
||
//
|
||
if (SWLWindowIsTaggable(hwnd))
|
||
{
|
||
properties |= SWL_PROP_TAGGABLE;
|
||
}
|
||
|
||
//
|
||
// Get all the SWL info which is stored as a window property.
|
||
//
|
||
if (SWLWindowIsOnTaskBar(hwnd))
|
||
{
|
||
//
|
||
// This class of window gets tagged.
|
||
//
|
||
properties |= SWL_PROP_TASKBAR;
|
||
}
|
||
|
||
//
|
||
// Find out if the window class has the CS_SAVEBITS style.
|
||
//
|
||
if (GetClassLong(hwnd, GCL_STYLE) & CS_SAVEBITS)
|
||
{
|
||
//
|
||
// This window's class has the CS_SAVEBITS style.
|
||
//
|
||
properties |= SWL_PROP_SAVEBITS;
|
||
}
|
||
|
||
//
|
||
// Set the visibility count. This is 0 if the window is currently
|
||
// invisible, SWL_BELIEVE_INVISIBLE_COUNT if visible.
|
||
//
|
||
if (IsWindowVisible(hwnd))
|
||
{
|
||
properties |= SWL_BELIEVE_INVISIBLE_COUNT;
|
||
}
|
||
|
||
//
|
||
// Set the window property, which we will retrieve when SWL determines
|
||
// whether it needs to resend the window structure.
|
||
//
|
||
if (m_pShare->m_pasLocal->hetCount > 0)
|
||
{
|
||
SetProp(hwnd, SWL_ATOM_NAME, (HANDLE)properties);
|
||
}
|
||
|
||
DC_EXIT_POINT:
|
||
DebugExitDWORD(ASHost::SWL_GetWindowProperty, properties);
|
||
return(properties);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// FUNCTION: SWLDestroyWindowProperty
|
||
//
|
||
// DESCRIPTION:
|
||
//
|
||
// Destroys the window property for the supplied window.
|
||
//
|
||
// PARMETERS: winID - the window ID of the window for which the property is
|
||
// destroyed.
|
||
//
|
||
// RETURNS: Zero
|
||
//
|
||
//
|
||
BOOL CALLBACK SWLDestroyWindowProperty(HWND hwnd, LPARAM lParam)
|
||
{
|
||
//
|
||
// NOTE LAURABU:
|
||
// We set the property using a string, which bumps up the ref count,
|
||
// to work around a Win95 bug. We therefore want to remove it using a
|
||
// string, which bumps down the ref count. Otherwise we will quickly
|
||
// get a ref count overflow.
|
||
//
|
||
RemoveProp(hwnd, SWL_ATOM_NAME);
|
||
return(TRUE);
|
||
}
|
||
|