windows-nt/Source/XPSP1/NT/termsrv/remdsk/rds/as/dd/het.c
2020-09-26 16:20:57 +08:00

888 lines
23 KiB
C

#include "precomp.h"
//
// HET.C
// Hosted Entity Tracker, NT Display Driver version
//
// Copyright(c)Microsoft 1997-
//
#include <limits.h>
//
// HET_DDTerm()
//
void HET_DDTerm(void)
{
LPHET_WINDOW_MEMORY pMem;
DebugEntry(HET_DDTerm);
//
// Clean up any window/graphics tracking stuff
//
g_hetDDDesktopIsShared = FALSE;
HETDDViewing(NULL, FALSE);
HETDDUnshareAll();
//
// Loop through the memory list blocks, freeing each. Then clear
// the Window and Free lists.
//
while (pMem = COM_BasedListFirst(&g_hetMemoryList, FIELD_OFFSET(HET_WINDOW_MEMORY, chain)))
{
TRACE_OUT(("HET_DDTerm: Freeing memory block %lx", pMem));
COM_BasedListRemove(&(pMem->chain));
EngFreeMem(pMem);
}
//
// Clear the window linked lists since they contain elements in
// the now free memory block.
//
COM_BasedListInit(&g_hetFreeWndList);
COM_BasedListInit(&g_hetWindowList);
DebugExitVOID(HET_DDTerm);
}
//
// HET_DDProcessRequest - see host.h
//
ULONG HET_DDProcessRequest(SURFOBJ *pso,
UINT cjIn,
void * pvIn,
UINT cjOut,
void * pvOut)
{
ULONG rc = TRUE;
LPOSI_ESCAPE_HEADER pHeader;
DebugEntry(HET_DDProcessRequest);
pHeader = pvIn;
TRACE_OUT(( "Request %#x", pHeader->escapeFn));
switch (pHeader->escapeFn)
{
case HET_ESC_SHARE_WINDOW:
{
if ((cjIn != sizeof(HET_SHARE_WINDOW)) ||
(cjOut != sizeof(HET_SHARE_WINDOW)))
{
ERROR_OUT(("HET_DDProcessRequest: Invalid sizes %d, %d for HET_ESC_SHARE_WINDOW",
cjIn, cjOut));
rc = FALSE;
DC_QUIT;
}
((LPHET_SHARE_WINDOW)pvOut)->result =
HETDDShareWindow(pso, (LPHET_SHARE_WINDOW)pvIn);
}
break;
case HET_ESC_UNSHARE_WINDOW:
{
if ((cjIn != sizeof(HET_UNSHARE_WINDOW)) ||
(cjOut != sizeof(HET_UNSHARE_WINDOW)))
{
ERROR_OUT(("HET_DDProcessRequest: Invalid sizes %d, %d for HET_ESC_UNSHARE_WINDOW",
cjIn, cjOut));
rc = FALSE;
DC_QUIT;
}
HETDDUnshareWindow((LPHET_UNSHARE_WINDOW)pvIn);
}
break;
case HET_ESC_UNSHARE_ALL:
{
if ((cjIn != sizeof(HET_UNSHARE_ALL)) ||
(cjOut != sizeof(HET_UNSHARE_ALL)))
{
ERROR_OUT(("HET_DDProcessRequest: Invalid sizes %d, %d for HET_ESC_UNSHARE_ALL",
cjIn, cjOut));
rc = FALSE;
DC_QUIT;
}
HETDDUnshareAll();
}
break;
case HET_ESC_SHARE_DESKTOP:
{
if ((cjIn != sizeof(HET_SHARE_DESKTOP)) ||
(cjOut != sizeof(HET_SHARE_DESKTOP)))
{
ERROR_OUT(("HET_DDProcessRequest: Invalid sizes %d, %d for HET_ESC_SHARE_DESKTOP",
cjIn, cjOut));
rc = FALSE;
DC_QUIT;
}
g_hetDDDesktopIsShared = TRUE;
}
break;
case HET_ESC_UNSHARE_DESKTOP:
{
if ((cjIn != sizeof(HET_UNSHARE_DESKTOP)) ||
(cjOut != sizeof(HET_UNSHARE_DESKTOP)))
{
ERROR_OUT(("HET_DDProcessRequest: Invalid sizes %d, %d for HET_ESC_UNSHARE_DESKTOP",
cjIn, cjOut));
rc = FALSE;
DC_QUIT;
}
g_hetDDDesktopIsShared = FALSE;
HETDDViewing(NULL, FALSE);
}
break;
case HET_ESC_VIEWER:
{
//
// We may turn OFF viewing but keep stuff shared and the windows
// tracked -- hosting a meeting and sharing something, for
// example.
//
if ((cjIn != sizeof(HET_VIEWER)) ||
(cjOut != sizeof(HET_VIEWER)))
{
ERROR_OUT(("HET_DDProcessRequest: Invalid sizes %d, %d for HET_ESC_VIEWER",
cjIn, cjOut));
rc = FALSE;
DC_QUIT;
}
HETDDViewing(pso, (((LPHET_VIEWER)pvIn)->viewersPresent != 0));
break;
}
default:
{
ERROR_OUT(( "Unknown request type %#x", pHeader->escapeFn));
rc = FALSE;
}
break;
}
DC_EXIT_POINT:
DebugExitDWORD(HET_DDProcessRequest, rc);
return(rc);
}
//
// HET_DDOutputIsHosted - see host.h
//
BOOL HET_DDOutputIsHosted(POINT pt)
{
BOOL rc = FALSE;
UINT j;
LPHET_WINDOW_STRUCT pWnd;
DebugEntry(HET_DDOutputIsHosted);
//
// Now check to see if the desktop is shared - if it is then simply
// return TRUE.
//
if (g_hetDDDesktopIsShared)
{
rc = TRUE;
DC_QUIT;
}
//
// Search through the window list
//
pWnd = COM_BasedListFirst(&g_hetWindowList, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
while (pWnd != NULL)
{
//
// Search each enumerated rectangle
//
TRACE_OUT(( "Window %#x has %u rectangle(s)",
pWnd, pWnd->rects.c));
for (j = 0; j < pWnd->rects.c; j++)
{
//
// See whether the point passed in is within this rectangle.
// Note that at this point we are dealing with exclusive
// co-ordinates.
//
if ((pt.x >= pWnd->rects.arcl[j].left) &&
(pt.x < pWnd->rects.arcl[j].right) &&
(pt.y >= pWnd->rects.arcl[j].top) &&
(pt.y < pWnd->rects.arcl[j].bottom))
{
TRACE_OUT((
"Pt {%d, %d}, in win %#x rect %u {%ld, %ld, %ld, %ld}",
pt.x, pt.y, pWnd->hwnd, j,
pWnd->rects.arcl[j].left, pWnd->rects.arcl[j].right,
pWnd->rects.arcl[j].top, pWnd->rects.arcl[j].bottom ));
//
// Found it! Re-order the list, most recently used first
//
COM_BasedListRemove(&(pWnd->chain));
COM_BasedListInsertAfter(&g_hetWindowList, &(pWnd->chain));
//
// Stop looking
//
rc = TRUE;
DC_QUIT;
}
TRACE_OUT(( "Pt not in win %#x rect %u {%ld, %ld, %ld, %ld}",
pWnd->hwnd, j,
pWnd->rects.arcl[j].left, pWnd->rects.arcl[j].right,
pWnd->rects.arcl[j].top, pWnd->rects.arcl[j].bottom ));
} // for all rectangles
//
// Move on to next window
//
pWnd = COM_BasedListNext(&g_hetWindowList, pWnd, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
}
DC_EXIT_POINT:
DebugExitBOOL(HET_DDOutputIsHosted, rc);
return(rc);
}
//
// HET_DDOutputRectIsHosted - see host.h
//
BOOL HET_DDOutputRectIsHosted(LPRECT pRect)
{
BOOL rc = FALSE;
UINT j;
LPHET_WINDOW_STRUCT pWnd;
RECT rectIntersect;
DebugEntry(HET_DDOutputRectIsHosted);
//
// Now check to see if the desktop is shared - if it is then simply
// return TRUE.
//
if (g_hetDDDesktopIsShared)
{
rc = TRUE;
DC_QUIT;
}
//
// Search through the window list
//
pWnd = COM_BasedListFirst(&g_hetWindowList, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
while (pWnd != NULL)
{
//
// Search each enumerated rectangle
//
TRACE_OUT(( "Window %#x has %u rectangle(s)",
pWnd, pWnd->rects.c));
for (j = 0; j < pWnd->rects.c; j++)
{
//
// See whether the rect passed in intersects this rectangle.
// Note that at this point we are dealing with exclusive
// co-ordinates.
//
rectIntersect.left = max( pRect->left,
pWnd->rects.arcl[j].left );
rectIntersect.top = max( pRect->top,
pWnd->rects.arcl[j].top );
rectIntersect.right = min( pRect->right,
pWnd->rects.arcl[j].right );
rectIntersect.bottom = min( pRect->bottom,
pWnd->rects.arcl[j].bottom );
//
// If the intersection rectangle is well-ordered and non-NULL
// then we have an intersection.
//
// The rects that we are dealing with are exclusive.
//
if ((rectIntersect.left < rectIntersect.right) &&
(rectIntersect.top < rectIntersect.bottom))
{
TRACE_OUT((
"Rect {%d, %d, %d, %d} intersects win %#x rect %u {%ld, %ld, %ld, %ld}",
pRect->left, pRect->top, pRect->right, pRect->bottom,
pWnd, j,
pWnd->rects.arcl[j].left, pWnd->rects.arcl[j].right,
pWnd->rects.arcl[j].top, pWnd->rects.arcl[j].bottom ));
//
// Found it! Re-order the list, most recently used first
//
COM_BasedListRemove(&(pWnd->chain));
COM_BasedListInsertAfter(&g_hetWindowList, &(pWnd->chain));
//
// Stop looking
//
rc = TRUE;
DC_QUIT;
}
TRACE_OUT(( "Rect not in win %#x rect %u {%ld, %ld, %ld, %ld}",
pWnd, j,
pWnd->rects.arcl[j].left, pWnd->rects.arcl[j].right,
pWnd->rects.arcl[j].top, pWnd->rects.arcl[j].bottom ));
} // for all rectangles
//
// Move on to next window
//
pWnd = COM_BasedListNext(&g_hetWindowList, pWnd, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
}
DC_EXIT_POINT:
DebugExitBOOL(HET_DDOutputRectIsHosted, rc);
return(rc);
}
//
//
// Name: HETDDVisRgnCallback
//
// Description: WNDOBJ Callback
//
// Params: pWo - pointer to the WNDOBJ which has changed
// fl - flags (se NT DDK documentation)
//
// Returns: none
//
// Operation:
//
//
VOID CALLBACK HETDDVisRgnCallback(PWNDOBJ pWo, FLONG fl)
{
ULONG count;
int size;
LPHET_WINDOW_STRUCT pWnd;
RECTL rectl;
UINT i;
DebugEntry(HETDDVisRgnCallback);
//
// Some calls pass a NULL pWo - exit now in this case
//
if (pWo == NULL)
{
DC_QUIT;
}
//
// Find the window structure for this window
//
pWnd = pWo->pvConsumer;
if (pWnd == NULL)
{
ERROR_OUT(( "Wndobj %x (fl %x) has no window structure", pWo, fl));
DC_QUIT;
}
//
// Check for window deletion
//
if (fl & WOC_DELETE)
{
TRACE_OUT(( "Wndobj %x (structure %x) deleted", pWo, pWo->pvConsumer));
// ASSERT the window is valid
ASSERT(pWnd->hwnd != NULL);
//
// Move the window from the active to the free list
//
COM_BasedListRemove(&(pWnd->chain));
COM_BasedListInsertAfter(&g_hetFreeWndList, &(pWnd->chain));
#ifdef DEBUG
// Check if this has reentrancy problems
pWnd->hwnd = NULL;
#endif
//
// Do any processing if this is the last window to be unshared.
//
// If we are not keeping track of any windows, the first pointer in
// the list will point to itself, ie list head->next == 0
//
if (g_hetWindowList.next == 0)
{
HETDDViewing(NULL, FALSE);
}
//
// Exit now
//
DC_QUIT;
}
//
// If we get here, this callback must be for a new visible region on a
// tracked window.
//
//
// Start the enumeration. This function is supposed to count the
// rectangles, but it always returns 0.
//
WNDOBJ_cEnumStart(pWo, CT_RECTANGLES, CD_ANY, 200);
//
// BOGUS BUGBUG LAURABU (perf opt for NT):
//
// NT will enum up to HET_WINDOW_RECTS at a time. Note that the enum
// function returns FALSE if, after obtaining the current batch, none
// are left to grab the next time.
//
// If the visrgn is composed of more than that, we will wipe out the
// previous set of rects, then ensure that the bounding box of the
// preceding rects is the last rect in the list.
//
// This pulls in several cases. For example if there are n visrgn piece
// rects, and n == c*HET_WINDOW_RECTS + 1, we will end up with 2 entries:
// * The last piece rect
// * The bounding box of the previous n-1 piece rects
// A lot of output may be accumulated in deadspace as a result.
//
// A better algorithm may be to fill the first HET_WINDOW_RECTS-1 slots,
// then union the rest into the last rectangle. That way we make use of
// all the slots. But this could be awkward, since we need a scratch
// ENUM_RECT struct rather than using the HET_WINDOW_STRUCT directly.
//
//
// First time through, enumerate HET_WINDOW_RECTS rectangles.
// Subsequent times, enumerate HET_WINDOW_RECTS-1 (see bottom of loop).
// This guarantees that there will be room to store a combined
// rectangle when we finally finish enumerating them.
//
pWnd->rects.c = HET_WINDOW_RECTS;
rectl.left = LONG_MAX;
rectl.top = LONG_MAX;
rectl.right = 0;
rectl.bottom = 0;
//
// Enumerate the rectangles
// NOTE that WNDOBJ_bEnum returns FALSE when there is nothing left
// to enumerate AFTER grabbing this set.
//
while (WNDOBJ_bEnum(pWo, sizeof(pWnd->rects), (ULONG *)&pWnd->rects))
{
#ifdef _DEBUG
{
char trcStr[200];
UINT j;
sprintf(trcStr, "WNDOBJ %p %d: ", pWo, pWnd->rects.c);
for (j = 0; j < pWnd->rects.c; j++)
{
sprintf(trcStr, "%s {%ld, %ld, %ld, %ld} ", trcStr,
pWnd->rects.arcl[j].left, pWnd->rects.arcl[j].top,
pWnd->rects.arcl[j].right, pWnd->rects.arcl[j].bottom);
if ((j & 3) == 3) // output every 4th rect
{
TRACE_OUT(( "%s", trcStr));
strcpy(trcStr, " ");
}
}
if ((j & 3) != 0) // if any rects left
{
TRACE_OUT(( "%s", trcStr));
}
}
#endif
//
// Combine the preceding rectangles into one bounding rectangle
//
for (i = 0; i < pWnd->rects.c; i++)
{
if (pWnd->rects.arcl[i].left < rectl.left)
{
rectl.left = pWnd->rects.arcl[i].left;
}
if (pWnd->rects.arcl[i].top < rectl.top)
{
rectl.top = pWnd->rects.arcl[i].top;
}
if (pWnd->rects.arcl[i].right > rectl.right)
{
rectl.right = pWnd->rects.arcl[i].right;
}
if (pWnd->rects.arcl[i].bottom > rectl.bottom)
{
rectl.bottom = pWnd->rects.arcl[i].bottom;
}
}
TRACE_OUT(( "Combined into {%ld, %ld, %ld, %ld}",
rectl.left, rectl.top, rectl.right, rectl.bottom));
//
// Second & subsequent times, enumerate HET_WINDOW_RECTS-1
//
pWnd->rects.c = HET_WINDOW_RECTS - 1;
}
//
// If any combining was done, save the combined rectangle now.
//
if (rectl.right != 0)
{
pWnd->rects.arcl[pWnd->rects.c] = rectl;
pWnd->rects.c++;
TRACE_OUT(( "Add combined rectangle to list"));
}
//
// On the assumption that this WNDOBJ is the most likely to be the
// target of the next output command, move it to the top of the list.
//
COM_BasedListRemove(&(pWnd->chain));
COM_BasedListInsertAfter(&g_hetWindowList, &(pWnd->chain));
//
// Return to caller
//
DC_EXIT_POINT:
DebugExitVOID(HETDDVisRgnCallback);
}
//
//
// Name: HETDDShareWindow
//
// Description: Share a window (DD processing)
//
// Params: pso - SURFOBJ
// pReq - request received from DrvEscape
//
//
BOOL HETDDShareWindow(SURFOBJ *pso, LPHET_SHARE_WINDOW pReq)
{
PWNDOBJ pWo;
FLONG fl = WO_RGN_CLIENT | WO_RGN_UPDATE_ALL | WO_RGN_WINDOW;
LPHET_WINDOW_STRUCT pWnd;
BOOL rc = FALSE;
DebugEntry(HETDDShareWindow);
ASSERT(!g_hetDDDesktopIsShared);
//
// Try to track the window
//
pWo = EngCreateWnd(pso, (HWND)pReq->winID, HETDDVisRgnCallback, fl, 0);
//
// Failed to track window - exit now
//
if (pWo == 0)
{
ERROR_OUT(( "Failed to track window %#x", pReq->winID));
DC_QUIT;
}
//
// Window is already tracked. This happens when an invisible window is
// shown in a process the USER shared, and we caught its create.
//
if (pWo == (PWNDOBJ)-1)
{
//
// No more to do here
//
TRACE_OUT(( "Window %#x already tracked", pReq->winID));
rc = TRUE;
DC_QUIT;
}
//
// Add window into our list.
//
//
// Find free window structure
//
pWnd = COM_BasedListFirst(&g_hetFreeWndList, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
//
// If no free structures, grow the list
//
if (pWnd == NULL)
{
if (!HETDDAllocWndMem())
{
ERROR_OUT(( "Unable to allocate new window structures"));
DC_QUIT;
}
pWnd = COM_BasedListFirst(&g_hetFreeWndList, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
}
//
// Fill in the structure
//
TRACE_OUT(( "Fill in details for new window"));
pWnd->hwnd = (HWND)pReq->winID;
pWnd->wndobj = pWo;
//
// Set this to zero. There's a brief period between the time we put
// this in our tracked list and the time we get called back to recalc
// the visrgn (because the ring 3 code invalidates the window completely).
// We might get graphical output and we don't want to parse garbage
// from this window's record.
//
pWnd->rects.c = 0;
//
// Move the window structure from free to active list
//
COM_BasedListRemove(&(pWnd->chain));
COM_BasedListInsertAfter(&g_hetWindowList, &(pWnd->chain));
//
// Save backwards pointer in the WNDOBJ
// THIS MUST BE LAST since our callback can happen anytime afterwards.
//
// NOTE that the window's visrgn rects get into our list because the
// ring3 code completely invalidates the window, causing the callback
// to get called.
//
TRACE_OUT(( "Save pointer %#lx in Wndobj %#x", pWnd, pWo));
WNDOBJ_vSetConsumer(pWo, pWnd);
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(HETDDShareWindow, rc);
return(rc);
}
//
//
// Name: HETDDUnshareWindow
//
// Description: Unshare a window (DD processing)
//
//
//
void HETDDUnshareWindow(LPHET_UNSHARE_WINDOW pReq)
{
LPHET_WINDOW_STRUCT pWnd, pNextWnd;
DebugEntry(HETDDUnshareWindow);
TRACE_OUT(( "Unshare %x", pReq->winID));
//
// Scan window list for this window and its descendants
//
pWnd = COM_BasedListFirst(&g_hetWindowList, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
while (pWnd != NULL)
{
//
// If this window is being unshared, free it
//
pNextWnd = COM_BasedListNext(&g_hetWindowList, pWnd, FIELD_OFFSET(HET_WINDOW_STRUCT, chain));
if (pWnd->hwnd == (HWND)pReq->winID)
{
TRACE_OUT(( "Unsharing %x", pReq->winID));
//
// Stop tracking the window
//
HETDDDeleteAndFreeWnd(pWnd);
}
//
// Go on to (previously saved) next window
//
pWnd = pNextWnd;
}
//
// Return to caller
//
DebugExitVOID(HETDDUnshareWindow);
}
//
//
// Name: HETDDUnshareAll
//
// Description: Unshare all windows (DD processing) (what did you expect)
//
//
void HETDDUnshareAll(void)
{
LPHET_WINDOW_STRUCT pWnd;
DebugEntry(HETDDUnshareAll);
//
// Clear all window structures
//
while (pWnd = COM_BasedListFirst(&g_hetWindowList, FIELD_OFFSET(HET_WINDOW_STRUCT, chain)))
{
TRACE_OUT(( "Unshare Window structure %x", pWnd));
//
// Stop tracking the window
//
HETDDDeleteAndFreeWnd(pWnd);
}
//
// Return to caller
//
DebugExitVOID(HETDDUnshareAll);
}
//
//
// Name: HETDDAllocWndMem
//
// Description: Allocate memory for a (new) window list
//
// Parameters: None
//
//
BOOL HETDDAllocWndMem(void)
{
BOOL rc = FALSE;
int i;
LPHET_WINDOW_MEMORY pNew;
DebugEntry(HETDDAllocWndMem);
//
// Allocate a new strucure
//
pNew = EngAllocMem(FL_ZERO_MEMORY, sizeof(HET_WINDOW_MEMORY), OSI_ALLOC_TAG);
if (pNew == NULL)
{
ERROR_OUT(("HETDDAllocWndMem: unable to allocate memory"));
DC_QUIT;
}
//
// Add this memory block to the list of memory blocks
//
COM_BasedListInsertAfter(&g_hetMemoryList, &(pNew->chain));
//
// Add all new entries to free list
//
TRACE_OUT(("HETDDAllocWndMem: adding new entries to free list"));
for (i = 0; i < HET_WINDOW_COUNT; i++)
{
COM_BasedListInsertAfter(&g_hetFreeWndList, &(pNew->wnd[i].chain));
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(HETDDAllocWndMem, rc);
return(rc);
}
//
//
// Name: HETDDDeleteAndFreeWnd
//
// Description: Delete and window and free its window structure
//
// Parameters: pWnd - pointer to window structure to delete & free
//
// Returns: none
//
// Operation: Ths function stops tracking a window and frees its memory
//
//
void HETDDDeleteAndFreeWnd(LPHET_WINDOW_STRUCT pWnd)
{
DebugEntry(HETDDDeleteAndFreeWnd);
//
// Stop tracking the window
//
EngDeleteWnd(pWnd->wndobj);
//
// NOTE LAURABU! EngDeleteWnd() will call the VisRgnCallback with
// WO_DELETE, which will cause us to exectute a duplicate of exactly
// the code below. So why do it twice (which is scary anyway), especially
// the stop hosting code?
//
ASSERT(pWnd->hwnd == NULL);
//
// Return to caller
//
DebugExitVOID(HETDDDeleteAndFreeWnd);
}
//
// HETDDViewers()
//
// Called when viewing of our shared apps starts/stops. Naturally, no longer
// sharing anything stops viewing also.
//
void HETDDViewing
(
SURFOBJ * pso,
BOOL fViewers
)
{
DebugEntry(HETDDViewers);
if (g_oeViewers != fViewers)
{
g_oeViewers = fViewers;
CM_DDViewing(pso, fViewers);
if (g_oeViewers)
{
//
// Force palette grab.
//
g_asSharedMemory->pmPaletteChanged = TRUE;
}
}
DebugExitVOID(HETDDViewing);
}