You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2904 lines
95 KiB
2904 lines
95 KiB
#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 iWin;
|
|
int cWins;
|
|
PSWLWINATTRIBUTES pSwl;
|
|
|
|
// Trace out the entries
|
|
pSwl = pSWLPacket->aWindows;
|
|
cWins = pSWLPacket->numWindows;
|
|
|
|
TRACE_OUT(("SWLSendPacket: Sending packet with %d windows", cWins));
|
|
for (iWin = 0; iWin < cWins; iWin++, pSwl++)
|
|
{
|
|
TRACE_OUT(("SWLSendPacket: Entry %d", iWin));
|
|
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);
|
|
}
|
|
|