Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1514 lines
47 KiB

/******************************Module*Header*******************************\
* Module Name: wndobj.cxx
*
* WNDOBJ support routines.
*
* Created: 22-Sep-1993 17:42:20
* Author: Wendy Wu [wendywu]
* Hock San Lee [hockl]
*
* Copyright (c) 1993-1999 Microsoft Corporation
\**************************************************************************/
#include "precomp.hxx"
#if DBG
long glDebugLevel = 0;
#define DBGINFO(str) if (glDebugLevel >= 2) DbgPrint("GLSRVL: " str)
#define DBGENTRY(str) if (glDebugLevel >= 8) DbgPrint("GLSRVL: " str)
#else
#define DBGINFO(str)
#define DBGENTRY(str)
#endif
// Global tracking object (TRACKOBJ) pointer.
// If this is non-null, we are tracking some WNDOBJs in the system.
PTRACKOBJ gpto = (PTRACKOBJ)NULL;
// Global that indicates whether to notify driver with the new WNDOBJ
// states following a WNDOBJ creation. User has not changed the window
// states but this is required to initialize the driver. The update is
// done in the parent gdi functions (e.g. SetPixelFormat) that allow
// the DDI to create a WNDOBJ.
BOOL gbWndobjUpdate;
// The following is a global uniqueness that gets bumped up anytime USER
// changes anyone's VisRgn:
ULONG giVisRgnUniqueness = 0;
// Maximum region rectangle
RECTL grclMax = {
MIN_REGION_COORD,
MIN_REGION_COORD,
MAX_REGION_COORD,
MAX_REGION_COORD
};
// Here is a brief description of the semaphore usage.
//
// There are 3 semaphores that this module uses/references.
//
// 1. User critical section.
// The user critical section ensures that no window moves when a new
// update occurs. It is only relevent to the display DCs to ensure a
// consistent window client regions state. For example, GreSetClientRgn
// assumes that no window can move until GreClientRgnUpdated is called.
//
// 2. Display devlock and DC/surface locks.
// The display devlock must be entered when a WNDOBJ is being used or
// updated. This prevents a WNDOBJ from being modified when it is
// being used in the DDI. The display devlock applies to the display
// DCs only. For memory and printer DCs, the surface is locked when
// a WNDOBJ is used. This is the current GDI design to prevent a
// different thread from deleting a surface while it is being used.
// Note that this precludes any multi-thread access to the printer or
// memory DCs.
//
// 3. Window object semaphore.
// The window object semaphore is used to protect access to the window
// object data structures. Note that a semaphore is used instead of
// a mutex here to allow for process cleanup of the semaphore. The
// process cleanup is done in the user process cleanup code.
//
// The above 3 semaphores must be entered in the given order. Otherwise,
// a deadlock may occur.
/******************************Member*Function*****************************\
* VOID TRACKOBJ::vUpdateDrvDelta
*
* Update driver function for delta regions. fl must have
* WOC_RGN_CLIENT_DELTA or WOC_RGN_SURFACE_DELTA set.
* Note that if the delta is empty, we do not need to call the driver.
* The compiler does not allow this function to be inline because or forward
* reference.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Wrote it.
\**************************************************************************/
VOID TRACKOBJ::vUpdateDrvDelta(EWNDOBJ *pwo, FLONG fl)
{
ASSERTGDI(fl & (WOC_RGN_CLIENT_DELTA|WOC_RGN_SURFACE_DELTA),
"TRACKOBJ::vUpdateDrvDelta, Bad flags\n");
if (!pwo->erclExclude().bEmpty())
(*pfn)((WNDOBJ *)pwo, fl);
}
/******************************Public*Function*****************************\
* EngCreateWnd
*
* Create a WNDOBJ from a HWND. This function should only be called
* when the calling thread has the usercrit and devlock in that order.
* GDI will ensure that the thread acquires both locks before calling the
* DDIs that allow EngCreateWnd to be called. The driver should only call
* this function from those DDI entry points. Currently, GreSetPixelFormat
* acquires both locks before calling DrvSetPixelFormat; GreExtEscape for
* WNDOBJ_SETUP escape also acquires both locks before calling DrvEscape.
*
* This function allows tracking multiple surfaces (screen, bitmaps and
* printers). For each display surface being tracked, it further allows
* multiple TRACKOBJs to be created for each driver function. The TRACKOBJs
* on a device surface are identified by unique pfn function pointers.
* This allows a live video driver and an OpenGL driver to track windows
* independently of each other. The only restriction is that a window on
* a surface cannot be tracked by more than one TRACKOBJs on that surface.
*
* A WNDOBJ has an associated pixel format. Once a WNDOBJ is created with
* a given pixel format, it cannot be set to a different pixel format. If
* there is no pixel format associated with a WNDOBJ, e.g. live video,
* it should be set to zero.
*
* The WNDOBJ created in this function does not have the current states
* until the first driver update function is called. However, the driver
* may immediately associate its own data with the WNDOBJ by calling the
* WNDOBJ_vSetConsumer function.
*
* Once a WNDOBJ is created, it cannot be deleted by the driver. Gdi will
* will notifiy the driver of the deletion when the window goes away or
* when the associated surface is deleted.
*
* The given hwnd identifies the user window to be tracked. It must be 0
* if the surface is a printer or memory bitmap.
*
* Returns the new WNDOBJ pointer if a WNDOBJ is created; 0 if an error
* occurs; -1 if hwnd is already being tracked by this driver function.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Rewrote it.
* 27-Sep-1993 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/
// This is a private clean up class for this function.
class WO_CLEANUP
{
private:
BOOL bKeep; // TRUE if resouces should not be freed
PTRACKOBJ pto;
PEWNDOBJ pwoSurf;
PEWNDOBJ pwoClient;
PREGION prgnSurf;
PREGION prgnClient;
HSEMAPHORE hsemClient;
public:
WO_CLEANUP()
{
bKeep = FALSE;
pto = (PTRACKOBJ)NULL;
pwoSurf = (PEWNDOBJ)NULL;
pwoClient = (PEWNDOBJ)NULL;
prgnSurf = (PREGION)NULL;
prgnClient = (PREGION)NULL;
hsemClient = NULL;
}
VOID vSetTrackobj(PTRACKOBJ pto1) { pto = pto1; }
VOID vSetSurfWndobj(PEWNDOBJ pwo) { pwoSurf = pwo; }
VOID vSetClientWndobj(PEWNDOBJ pwo) { pwoClient = pwo; }
VOID vSetSurfRegion(RGNMEMOBJ &rmo) { prgnSurf = rmo.prgnGet();}
VOID vSetClientRegion(RGNMEMOBJ &rmo) { prgnClient = rmo.prgnGet();}
VOID vSetClientSem(HSEMAPHORE hsem) { hsemClient = hsem; }
PTRACKOBJ ptoGet() { return(pto); }
VOID vKeepAll() { bKeep = TRUE; }
~WO_CLEANUP()
{
if (bKeep) return;
DBGINFO("EngCreateWnd: no WNDOBJ created\n");
if (pto) { pto->ident = 0; VFREEMEM(pto); }
if (pwoSurf) { pwoSurf->ident = 0; VFREEMEM(pwoSurf); }
if (pwoClient) { pwoClient->ident = 0; VFREEMEM(pwoClient); }
if (prgnSurf) prgnSurf->vDeleteREGION();
if (prgnClient) prgnClient->vDeleteREGION();
if (hsemClient) GreDeleteSemaphore(hsemClient);
}
};
WNDOBJ * APIENTRY EngCreateWnd
(
SURFOBJ *pso,
HWND hwnd,
WNDOBJCHANGEPROC pfn,
FLONG fl,
int iPixelFormat
)
{
WO_CLEANUP cleanup; // prepare for clean up
PEWNDOBJ pwoClient;
PEWNDOBJ pwoSurf;
PEWNDOBJ pwo;
PTRACKOBJ pto;
PEWNDOBJ pwoGenericSibling = NULL;
DBGENTRY("EngCreateWnd\n");
PSURFACE pSurf = SURFOBJ_TO_SURFACE(pso);
// Assert that we are in user critical section and also hold the devlock.
// This ensures that no one is updating the hwnd.
if (!UserIsUserCritSecIn())
{
RIP("Driver may call EngCreateWnd only from WNDOBJ_SETUP escape\n"
"(or from OpenGL MCD or ICD escapes)");
return(NULL);
}
CHECKUSERCRITIN;
if (hwnd)
{
CHECKDEVLOCKIN2(pSurf);
}
// Validate flags.
#if ((WO_VALID_FLAGS & WO_INTERNAL_VALID_FLAGS) != 0)
#error "bad WO_INTERNAL_VALID_FLAGS"
#endif
if ((fl & ~WO_VALID_FLAGS) != 0)
return((WNDOBJ *)0);
// If this is the first time we need to track window object, create the
// semaphore for synchronization. We use a semaphore instead of mutex
// so that we can perform process cleanup of the semaphore.
// Enter the semphore for window object.
SEMOBJ so(ghsemWndobj);
// If the window is already being tracked by the same TRACKOBJ and the
// pixel format is the same, return -1. If the window is being tracked
// by a different TRACKOBJ, return 0. There may be multiple TRACKOBJs
// on the same device surface but a window can be tracked by only one
// TRACKOBJ. In addition, pixel format in a window cannot be modified
// once it is set.
for (pto = gpto; pto; pto = pto->ptoNext)
{
for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext)
{
if (pwo->hwnd == hwnd)
{
KdPrint(("Failing EngCreateWnd -- hwnd already has a WNDOBJ\n"));
if (pto->pfn == pfn && pwo->ipfd == iPixelFormat)
return((WNDOBJ *)-1); // return -1
else
return((WNDOBJ *)0); // tracked by a diff TRACKOBJ
}
}
}
// If this is the first time we track a device surface for this driver
// function, we have to allocate space for the TRACKOBJ. We determine
// if this is a new TRACKOBJ by comparing the pfn with those of the
// existing ones. This allows the live video, installable opengl, and
// generic opengl windows to be tracked separately.
for (pto = gpto; pto; pto = pto->ptoNext)
if (pto->pSurface == pSurf && pto->pfn == pfn)
break;
if (!pto)
{
// Allocate a new TRACKOBJ.
if (!(pto = (PTRACKOBJ) PALLOCMEM(sizeof(TRACKOBJ), 'dnwG')))
return((WNDOBJ *)0);
cleanup.vSetTrackobj(pto);
pto->ident = TRACKOBJ_IDENTIFIER;
// pto->ptoNext
pto->pwoSurf = (PEWNDOBJ)NULL;
pto->pwo = (PEWNDOBJ)NULL;
pto->pSurface = pSurf;
pto->pfn = pfn;
pto->fl = fl;
pto->erclSurf.left = 0;
pto->erclSurf.top = 0;
pto->erclSurf.right = pSurf->sizl().cx;
pto->erclSurf.bottom = pSurf->sizl().cy;
// Create a surface WNDOBJ for the TRACKOBJ if it requests WO_RGN_SURFACE or
// WO_RGN_SURFACE_DELTA.
if (fl & (WO_RGN_SURFACE|WO_RGN_SURFACE_DELTA))
{
if (!(pwoSurf = (PEWNDOBJ) PALLOCMEM(sizeof(EWNDOBJ), 'dnwG')))
return((WNDOBJ *)0);
cleanup.vSetSurfWndobj(pwoSurf);
// Create a surface client region that is the entire surface.
RGNMEMOBJ rmoSurf((BOOL)FALSE);
if (!rmoSurf.bValid())
return((WNDOBJ *)0);
cleanup.vSetSurfRegion(rmoSurf);
rmoSurf.vSet((RECTL *)&pto->erclSurf);
// Initialize the surface WNDOBJ.
pwoSurf->pto = pto; // pto used by vSetClip
rmoSurf.prgnGet()->vStamp(); // init iUniq
pwoSurf->vSetClip(rmoSurf.prgnGet(), pto->erclSurf);
pwoSurf->pvConsumer = 0;
pwoSurf->psoOwner = pSurf->pSurfobj();
pwoSurf->ident = EWNDOBJ_IDENTIFIER;
pwoSurf->pwoNext = (PEWNDOBJ)NULL; // no next pointer
pwoSurf->hwnd = 0; // no hwnd
pwoSurf->fl = fl | WO_SURFACE;
pwoSurf->ipfd = 0; // no pixel format
// Add WNDOBJ to the TRACKOBJ.
pto->pwoSurf = pwoSurf;
}
}
// The tracking flags must be consistent.
if ((pto->fl & ~WO_INTERNAL_VALID_FLAGS) != fl)
return((WNDOBJ *)0);
// Allocate a new client WNDOBJ.
if (!(pwoClient = (PEWNDOBJ) PALLOCMEM(sizeof(EWNDOBJ), 'dnwG')))
return((WNDOBJ *)0);
cleanup.vSetClientWndobj(pwoClient);
// Create an empty window client region. The client region is still being
// created. The driver window region update will be done in the parent gdi
// function.
ERECTL erclClient(0,0,0,0);
RGNMEMOBJ rmoClient((BOOL)FALSE);
if (!rmoClient.bValid())
return((WNDOBJ *)0);
cleanup.vSetClientRegion(rmoClient);
rmoClient.vSet((RECTL *)&erclClient);
// Initialize the per-WNDOBJ semaphore once per window.
{
pwoClient->hsem = GreCreateSemaphore();
if (pwoClient->hsem == NULL)
{
return NULL;
}
cleanup.vSetClientSem(pwoClient->hsem);
fl |= WO_HSEM_OWNER;
}
// Initialize the WNDOBJ.
pwoClient->pto = pto; // pto used by vSetClip
rmoClient.prgnGet()->vStamp(); // init iUniq
pwoClient->vSetClip(rmoClient.prgnGet(), erclClient);
pwoClient->pvConsumer = 0; // to be set by the driver
pwoClient->psoOwner = pSurf->pSurfobj();
pwoClient->ident = EWNDOBJ_IDENTIFIER;
pwoClient->hwnd = hwnd;
pwoClient->fl = fl;
pwoClient->ipfd = iPixelFormat;
// Add WNDOBJ to TRACKOBJ.
pwoClient->pwoNext = pto->pwo;
pto->pwo = pwoClient;
ASSERTGDI(offsetof(EWNDOBJ, pvConsumer) == offsetof(WNDOBJ, pvConsumer),
"EngCreateWnd: rclClient wrong offset\n");
// Add TRACKOBJ to global linked list.
if (cleanup.ptoGet())
{
pto->ptoNext = gpto;
gpto = pto;
}
// If hwnd is given, attach the WNDOBJ to the window in user.
// Otherwise, it is a printer surface or memory bitmap. Attach it to
// the surface.
if (hwnd)
{
UserAssociateHwnd(hwnd, (PVOID) pwoClient);
}
else
{
// Only one WNDOBJ per memory bitmap or printer surface.
ASSERTGDI(!pSurf->pwo(),
"EngCreateWnd: multiple WNDOBJs unexpected in memory DCs\n");
pSurf->pwo(pwoClient);
}
// Inform the parent gdi function that it needs to update the new WNDOBJ
// in the driver.
pto->fl |= WO_NEW_WNDOBJ;
pwoClient->fl |= WO_NEW_WNDOBJ;
gbWndobjUpdate = TRUE;
// Don't free the PDEV until the WNDOBJ is destroyed.
PDEVOBJ po(pSurf->hdev());
po.vReferencePdev();
// Everything is golden. Keep the created objects and return the new WNDOBJ.
cleanup.vKeepAll();
return((WNDOBJ *)pwoClient);
}
/******************************Public*Function*****************************\
* GreDeleteWnd
*
* This function is called when the window that is being tracked is deleted
* in user, or when the device surface (printer or memory bitmap) that
* is begin tracked is deleted. It deletes the WNDOBJ and notifies the
* driver that the WNDOBJ is going away.
*
* This function does not update the driver with the new client regions
* following the WNDOBJ deletion. It assumes that if the deletion is a
* window, user will update or has updated the client regions; and if the
* deleteion is a printer or memory bitmap, the TRACKOBJ is going away
* and therefore no need to notify driver.
*
* If the deletion is a window, the calling thread must have the usercrit.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Wrote it.
\**************************************************************************/
VOID APIENTRY GreDeleteWnd(PVOID _pwoDelete)
{
PEWNDOBJ pwoDelete = (PEWNDOBJ)_pwoDelete;
PEWNDOBJ pwo;
PTRACKOBJ pto;
DBGENTRY("GreDeleteWnd\n");
// Validate pwoDelete.
if (!pwoDelete->bValid())
{
ASSERTGDI(FALSE, "GreDeleteWnd: Invalid pwoDelete\n");
return;
}
// If hwnd is non 0, the user calling thread must hold the usercrit.
// This ensures that no one is updating the hwnd.
if (pwoDelete->hwnd)
{
CHECKUSERCRITIN;
CHECKDEVLOCKIN2(pwoDelete->pto->pSurface);
}
pto = pwoDelete->pto;
// Acquire the device lock. Note that this may be different from the
// device lock that USER is holding if the PDEV is marked as 'deleted':
PDEVOBJ po(pto->pSurface->hdev());
{
DEVLOCKOBJ dlo(po);
// Enter the semaphore for window object.
ASSERTGDI(ghsemWndobj, "GreDeleteWnd: bad ghsemWndobj\n");
SEMOBJ so(ghsemWndobj);
// Notify driver that the WNDOBJ is going away.
// Hold the WNDOBJ stable while doing so by grabbing the per-WNDOBJ semaphore.
{
SEMOBJ soClient(pwoDelete->hsem);
pto->vUpdateDrv(pwoDelete, WOC_DELETE);
}
// Unlink pwoDelete from chain.
if (pto->pwo == pwoDelete)
pto->pwo = pwoDelete->pwoNext;
else
for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext)
{
if (pwo->pwoNext == pwoDelete)
{
pwo->pwoNext = pwoDelete->pwoNext;
break;
}
}
// Free pwoDelete.
pwoDelete->bDelete(); // delete RGNOBJ
pwoDelete->ident = 0;
VFREEMEM(pwoDelete); // free memory
// Delete the tracking object if there are no more windows to track.
if (pto->pwo == (PEWNDOBJ)NULL)
{
// Unlink pto from chain.
if (pto == gpto)
gpto = pto->ptoNext;
else
for (PTRACKOBJ ptoTmp = gpto; ptoTmp; ptoTmp = ptoTmp->ptoNext)
{
if (ptoTmp->ptoNext == pto)
{
ptoTmp->ptoNext = pto->ptoNext;
break;
}
}
// Delete the pwoSurf if it exists.
if (pto->pwoSurf)
{
ASSERTGDI(pto->fl & (WO_RGN_SURFACE|WO_RGN_SURFACE_DELTA),
"GreDeleteWnd: WO_RGN_SURFACE or WO_RGN_SURFACE_DELTA not set\n");
pto->pwoSurf->bDelete(); // delete RGNOBJ
pto->pwoSurf->ident = 0;
VFREEMEM(pto->pwoSurf); // free memory
}
pto->ident = 0;
VFREEMEM(pto);
}
// Inform the sprite code that a WNDOBJ has been deleted.
vSpWndobjChange(po.hdev(), NULL);
}
// Remove the reference to the PDEV.
po.vUnreferencePdev();
}
/******************************Public*Function*****************************\
* EngDeleteWnd
*
* Driver-callable entry point to delete a WNDOBJ.
*
\**************************************************************************/
VOID EngDeleteWnd(WNDOBJ* _pwoDelete)
{
EWNDOBJ* pwoDelete = (EWNDOBJ*)_pwoDelete;
if (!UserIsUserCritSecIn())
{
RIP("Driver may call EngDeleteWnd only from WNDOBJ_SETUP escape\n"
"(or from OpenGL MCD or ICD escapes)");
}
else
{
// Tell USER to disassociate this WNDOBJ from its window:
if (pwoDelete->hwnd)
{
UserAssociateHwnd(pwoDelete->hwnd, NULL);
}
GreDeleteWnd(pwoDelete);
}
}
/******************************Public*Routine******************************\
* VOID vChangeWndObjs
*
* Transfers ownership of WNDOBJs between PDEVs.
*
* History:
* 8-Feb-1996 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/
VOID vChangeWndObjs(
SURFACE* pSurfaceOld,
HDEV hdevOld,
SURFACE* pSurfaceNew,
HDEV hdevNew)
{
TRACKOBJ* pto;
EWNDOBJ* pwo;
EWNDOBJ* pwoNext;
CHECKUSERCRITIN;
SEMOBJ so(ghsemWndobj);
// Note that we shouldn't use pSurfaceOld->hdev() or pSurfaceNew->hdev()
// becuase they haven't been updated yet:
PDEVOBJ poOld(hdevOld);
PDEVOBJ poNew(hdevNew);
// Changing drivers. Delete all WNDOBJs belonging to the old
// surface:
for (pto = gpto; pto != NULL; pto = pto->ptoNext)
{
if (pto->pSurface == pSurfaceOld)
{
// For every WNDOBJ belonging to this TRACKOBJ, transfer
// ownership to the new PDEV:
for (pwo = pto->pwo; pwo != NULL; pwo = pwo->pwoNext)
{
ASSERTGDI(pwo->psoOwner == pSurfaceOld->pSurfobj(),
"Old psoOwner mismatch");
poNew.vReferencePdev();
poOld.vUnreferencePdev();
}
}
else if (pto->pSurface == pSurfaceNew)
{
for (pwo = pto->pwo; pwo != NULL; pwo = pwo->pwoNext)
{
ASSERTGDI(pwo->psoOwner == pSurfaceNew->pSurfobj(),
"New psoOwner mismatch");
poOld.vReferencePdev();
poNew.vUnreferencePdev();
}
}
}
}
/******************************Public*Routine******************************\
* VOID vTransferWndObjs
*
* Transfers ownership of WNDOBJs from PDEV to other PDEV.
*
* History:
* 2-16-1999 -by- Hideyuki Nagase [hideyukn]
* Wrote it.
\**************************************************************************/
VOID vTransferWndObjs(
SURFACE* pSurface,
HDEV hdevOld,
HDEV hdevNew)
{
TRACKOBJ* pto;
EWNDOBJ* pwo;
EWNDOBJ* pwoNext;
CHECKUSERCRITIN;
SEMOBJ so(ghsemWndobj);
PDEVOBJ poOld(hdevOld);
PDEVOBJ poNew(hdevNew);
for (pto = gpto; pto != NULL; pto = pto->ptoNext)
{
if (pto->pSurface == pSurface)
{
for (pwo = pto->pwo; pwo != NULL; pwo = pwo->pwoNext)
{
ASSERTGDI(pwo->psoOwner == pSurface->pSurfobj(),
"New psoOwner mismatch");
KdPrint(("Transfer wndobj %x - hdevNew %x hdevOld %x \n",
pwo,hdevNew,hdevOld));
poNew.vReferencePdev();
poOld.vUnreferencePdev();
}
}
}
}
/******************************Public*Function*****************************\
* vForceClientRgnUpdate
*
* This function is called by gdi to force an update of the new WNDOBJ
* that is just created.
*
* This function should only be called when the calling thread has the
* usercrit and devlock in that order. Currently, it is called from
* GreSetPixelFormat and GreExtEscape for WNDOBJ_SETUP escape after
* they detected that the driver has created a new WNDOBJ.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Wrote it.
\**************************************************************************/
VOID vForceClientRgnUpdate()
{
PTRACKOBJ pto;
PEWNDOBJ pwo = (PEWNDOBJ)NULL;
DBGENTRY("vForceClientRgnUpdate\n");
// Assert that we are in user critical section and also hold the devlock.
// This ensures that no one is updating the hwnd.
CHECKUSERCRITIN;
// Update the new WNDOBJ that was just created.
// User has not changed the client regions because we are still in the
// user critical section. We are updating the client regions ourselves.
{
// Enter the semphore for window object.
SEMOBJ so(ghsemWndobj);
// Find the newly created WNDOBJ.
for (pto = gpto; pto; pto = pto->ptoNext)
{
if (!(pto->fl & WO_NEW_WNDOBJ))
continue;
pto->fl &= ~WO_NEW_WNDOBJ;
pto->fl |= WO_NOTIFIED;
for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext)
{
if (!(pwo->fl & WO_NEW_WNDOBJ))
continue;
pwo->fl &= ~WO_NEW_WNDOBJ;
pwo->fl |= WO_NOTIFIED;
break; // found it
}
break; // found it
}
if (!pwo)
{
ASSERTGDI(FALSE, "vForceClientRgnUpdate: no new WNDOBJ found\n");
return;
}
// We need to ensure that the caller holds the devlock before calling this
// function. Otherwise, some other threads may be drawing into the wrong
// client region. We do the check here because we don't have any pSurf
// information earlier.
if (pwo->hwnd)
{
CHECKDEVLOCKIN2(pto->pSurface);
}
// If hwnd exists, get the client region from user.
HRGN hrgnClient;
ERECTL erclClient;
if (pwo->hwnd)
{
hrgnClient = UserGetClientRgn(pwo->hwnd,
(LPRECT)&erclClient,
pwo->fl & WO_RGN_WINDOW);
}
else
{
// If hwnd does not exist, this is a memory bitmap or printer surface.
// The client region is the whole surface.
erclClient = pto->erclSurf;
hrgnClient = GreCreateRectRgnIndirect((LPRECT)&erclClient);
}
if (!hrgnClient)
{
ASSERTGDI(FALSE, "vForceClientRgnUpdate: hwnd has no rgn\n");
return;
}
// Update client region in the WNDOBJ.
GreSetRegionOwner(hrgnClient, OBJECT_OWNER_PUBLIC);
RGNOBJAPI roClient(hrgnClient,FALSE);
ASSERTGDI(roClient.bValid(), "vForceClientRgnUpdate: invalid hrgnClient\n");
#ifdef OPENGL_MM
// Under Multi-mon we need to adjust the client region and
// client rectangle by the offset of the surface.
// Additionally we need to clip the client region to the
// surface of the TRACKOBJ.
if (!(pwo->fl & WO_RGN_DESKTOP_COORD))
{
PDEVOBJ pdo(pwo->pto->pSurface->hdev());
if (pdo.bValid())
{
if (pdo.bPrimary(pwo->pto->pSurface))
{
POINTL ptlOrigin;
ptlOrigin.x = -pdo.pptlOrigin()->x;
ptlOrigin.y = -pdo.pptlOrigin()->y;
if ((ptlOrigin.x != 0) || (ptlOrigin.y != 0))
{
// offset the region and window rect if necessary
roClient.bOffset(&ptlOrigin);
erclClient += ptlOrigin; /* this offsets by ptl */
}
}
}
RGNMEMOBJTMP rmoTmp;
RGNMEMOBJTMP rmoRcl;
if (rmoTmp.bValid() && rmoRcl.bValid())
{
// this clips the client region to the pto surface
rmoRcl.vSet((RECTL *) &pto->erclSurf);
rmoTmp.bCopy(roClient);
roClient.iCombine(rmoTmp, rmoRcl, RGN_AND);
if (rmoTmp.iCombine(roClient,rmoRcl,RGN_AND) != ERROR)
{
roClient.bSwap(&rmoTmp);
}
}
}
#endif // OPENGL_MM
// We're going to modify the WNDOBJ now. Grab the per-WNDOBJ semaphore to
// keep it stable. Don't release until after the driver is called.
SEMOBJ soClient(pwo->hsem);
roClient.bSwap(pwo);
pwo->prgn->vStamp(); // init iUniq
pwo->vSetClip(pwo->prgn, erclClient);
roClient.bDeleteRGNOBJAPI(); // delete handle too
// Call driver with the new WNDOBJ.
if (pto->fl & WO_RGN_CLIENT_DELTA)
pto->vUpdateDrvDelta(pwo, WOC_RGN_CLIENT_DELTA);
if (pto->fl & WO_RGN_CLIENT)
pto->vUpdateDrv(pwo, WOC_RGN_CLIENT);
// Let the sprite code know that there's a new WNDOBJ, now completely
// formed.
vSpWndobjChange(pto->pSurface->hdev(), pwo);
}
// Update the remaining window client regions.
GreClientRgnUpdated(GCR_WNDOBJEXISTS);
}
/******************************Member*Function*****************************\
* GreWindowInsteadOfClient
*
* Returns TRUE if the window area instead of the client area should be
* used for the WNDOBJ regions.
*
\**************************************************************************/
BOOL GreWindowInsteadOfClient(PVOID _pwo)
{
PEWNDOBJ pwo = (PEWNDOBJ) _pwo;
return(pwo->fl & WO_RGN_WINDOW);
}
/******************************Member*Function*****************************\
* GreClientRgnUpdated
*
* User calls this function after having updated all the vis/client
* region changes and before releasing the devlock. We have to complete
* the remaining client region update operation.
*
* Gdi calls vForceClientRgnUpdate and this function after a new WNDOBJ
* is created to update the driver.
*
* This function should only be called when the calling thread has the
* usercrit and devlock in that order.
*
* If the GCR_DELAYFINALUPDATE flag is specified, the caller must
* subsequently call GreClientRgnDone. If the driver specified
* the WO_DRAW_NOTIFY flag, the GCR_DELAYFINALUPDATE will suppress
* the WOC_DRAWN notification until GreClientRgnDone is called (or
* the next time GreClientRgnUpdated is called without
* GCR_DELAYFINALUPDATE).
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Rewrote it.
* 11-Nov-1993 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/
VOID APIENTRY GreClientRgnUpdated(FLONG flUpdate)
{
PTRACKOBJ pto;
PEWNDOBJ pwo;
DBGENTRY("GreClientRgnUpdated\n");
// Since we are holding the DEVLOCK to the active display, we can
// increment the VisRgn count here without doing an atomic increment.
giVisRgnUniqueness++;
// Go any further only if some WNDOBJs exist.
if (!(flUpdate & GCR_WNDOBJEXISTS))
{
return;
}
// Assert that we are in user critical section and also hold the devlock.
// This ensures that no one is updating the hwnd.
CHECKUSERCRITIN;
// Enter the semphore for window object.
SEMOBJ so(ghsemWndobj);
// The surface client regions have changed. Complete the remaining
// client region update.
for (pto = gpto; pto; pto = pto->ptoNext)
{
if (!(pto->fl & WO_NOTIFIED))
continue;
pto->fl &= ~WO_NOTIFIED;
// We need to ensure that user holds the devlock before calling this function.
// Otherwise, some other threads may be drawing into the wrong client region.
// We do the check here because we don't have any pSurface information earlier.
//
// Also, we exclude the entire screen since we are going to touch windows all
// over the place and end with calling driver with WOC_COMPLETE which
// can have an effect anywhere on the screen.
DEVEXCLUDEOBJ dxo;
if (pto->pwo->hwnd)
{
RECTL rclSurf;
HDEV hdev = pto->pSurface->hdev();
PDEVOBJ po(hdev);
CHECKDEVLOCKIN2(pto->pSurface);
ASSERTGDI(po.bValid(), "GreClientRgnUpdated: invalid pdevobj\n");
if (po.bValid() && !po.bDisabled())
{
rclSurf.left = 0;
rclSurf.top = 0;
rclSurf.right = pto->pSurface->sizl().cx;
rclSurf.bottom = pto->pSurface->sizl().cy;
dxo.vExclude(hdev, &rclSurf, (ECLIPOBJ *) NULL);
}
}
// Traverse the chain and call the driver with un-changed windows if
// the WO_RGN_UPDATE_ALL and WO_RGN_CLIENT flags are set.
if ((pto->fl & (WO_RGN_CLIENT|WO_RGN_UPDATE_ALL))
== (WO_RGN_CLIENT|WO_RGN_UPDATE_ALL))
{
for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext)
if (pwo->fl & WO_NOTIFIED)
pwo->fl &= ~WO_NOTIFIED;
else
{
// Make WNDOBJ stable by holding the per-WNDOBJ semaphore
// while we call the driver.
SEMOBJ soClient(pwo->hsem);
pto->vUpdateDrv(pwo, WOC_RGN_CLIENT);
}
}
// Update the surface WNDOBJ if requested.
if (pto->fl & (WO_RGN_SURFACE|WO_RGN_SURFACE_DELTA))
{
PEWNDOBJ pwoSurf = pto->pwoSurf;
RGNMEMOBJTMP rmoTmp((BOOL)FALSE);
RGNMEMOBJTMP rmoSurfNew((BOOL)FALSE);
if (rmoTmp.bValid() && rmoSurfNew.bValid())
{
// Construct the new surface region which is the entire surface minus
// the combined client regions.
rmoSurfNew.vSet(&pwoSurf->rclClient);
for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext)
{
RGNOBJ ro(pwo->prgn);
if (rmoTmp.iCombine(rmoSurfNew, ro, RGN_DIFF) != ERROR)
rmoSurfNew.bSwap(&rmoTmp);
}
// If WO_RGN_SURFACE_DELTA is set, update the driver with the new surface delta.
if (pto->fl & WO_RGN_SURFACE_DELTA)
{
RGNOBJ roSurf(pwoSurf->prgn);
if (rmoTmp.iCombine(rmoSurfNew, roSurf, RGN_DIFF) != ERROR)
{
pwoSurf->bSwap(&rmoTmp);
pwoSurf->prgn->vStamp(); // new iUniq
pwoSurf->vSetClip(pwoSurf->prgn, *(ERECTL *)&pwoSurf->rclClient);
pto->vUpdateDrvDelta(pwoSurf, WOC_RGN_SURFACE_DELTA);
}
}
// Save the new surface region.
// The surface region may be the same as previous one here. This code can be
// optimized a little.
pwoSurf->bSwap(&rmoSurfNew);
pwoSurf->prgn->vStamp(); // new iUniq
pwoSurf->vSetClip(pwoSurf->prgn, *(ERECTL *)&pwoSurf->rclClient);
// Give the driver the new surface region.
if (pto->fl & WO_RGN_SURFACE)
pto->vUpdateDrv(pwoSurf, WOC_RGN_SURFACE);
}
} // if (pto->fl & (WO_RGN_SURFACE|WO_RGN_SURFACE_DELTA))
// Send down the WOC_CHANGED to signify notification complete.
pto->vUpdateDrv((PEWNDOBJ)NULL, WOC_CHANGED);
// Need WOC_DRAWN notification if requested. Notification is delayed
// if GCR_DELAYFINALUPDATE is set. Delayed notification is signaled
// by setting the WO_NEED_DRAW_NOTIFY flag in the trackobj and is
// handled in GreClientRgnDone.
if (pto->fl & WO_DRAW_NOTIFY)
{
if (flUpdate & GCR_DELAYFINALUPDATE)
{
pto->fl |= WO_NEED_DRAW_NOTIFY;
}
else
{
pto->vUpdateDrv((PEWNDOBJ)NULL, WOC_DRAWN);
pto->fl &= ~WO_NEED_DRAW_NOTIFY;
}
}
} // for (pto = gpto; pto; pto = pto->ptoNext)
}
/******************************Public*Routine******************************\
* GreClientRgnDone
*
* If USER calls GreClientRgnUpdated with the GCR_DELAYFINALUPDATE flag
* set, it must subsequently call this function to complete the update.
*
* If the driver calls EngCreateWnd with the WO_NEED_DRAW_NOTIFY flag,
* the GreClientRgnUpdated function will complete the notification with
* a WOC_CHANGED followed by a WOC_DRAWN. However, if the
* GCR_DELAYFINALUPDATE flag is specified when calling GreClientRgnUpdated,
* the WOC_DRAWN message is suppressed until GreClientRgnDone is called.
*
* If the driver does not call EngCreateWnd with the WO_NEED_DRAW_NOTIFY
* flag, GreClientRgnDone will only send the WOC_CHANGED notification and
* this function will have no effect.
*
* This function should only be called when the calling thread has the
* usercrit and devlock in that order.
*
* Note: Currently this function does not do anything if GCR_WNDOBJEXISTS
* is not set (GreClientRgnUpdated will update the vis rgn uniqneness and
* so must be called even if there are not WNDOBJs). Therefore, for now
* it is acceptable for the caller to skip GreClientRgnDone if no WNDOBJs
* exist.
*
* History:
* 19-Dec-1997 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/
VOID APIENTRY GreClientRgnDone(FLONG flUpdate)
{
PTRACKOBJ pto;
PEWNDOBJ pwo;
DBGENTRY("GreClientRgnDone\n");
// Go any further only if some WNDOBJs exist.
if (!(flUpdate & GCR_WNDOBJEXISTS))
{
return;
}
// Assert that we are in user critical section.
// This ensures that no one is updating the hwnd.
CHECKUSERCRITIN;
// Enter the semphore for window object.
SEMOBJ so(ghsemWndobj);
// Check which tracking objects need WOC_DRAWN notification.
for (pto = gpto; pto; pto = pto->ptoNext)
{
if (pto->pwo->hwnd)
{
CHECKDEVLOCKIN2(pto->pSurface);
}
if (pto->fl & WO_NEED_DRAW_NOTIFY)
{
pto->fl &= ~WO_NEED_DRAW_NOTIFY;
pto->vUpdateDrv((PEWNDOBJ)NULL, WOC_DRAWN);
}
// Inform the sprite code of the change in the WNDOBJ.
for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext)
{
vSpWndobjChange(pto->pSurface->hdev(), pwo);
}
} // for (pto = gpto; pto; pto = pto->ptoNext)
}
/******************************Public*Function*****************************\
* GreSetClientRgn
*
* User calls this function to update the client region in a WNDOBJ.
* After all the regions have been updated, user must call GreClientRgnUpdated
* to complete the update.
*
* User creates a new region to give to this function. This function must
* delete the region before it returns!
*
* This function should only be called when the calling thread has the
* usercrit and devlock in that order.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Wrote it.
\**************************************************************************/
VOID GreSetClientRgn(PVOID _pwoClient, HRGN hrgnClient, LPRECT prcClient)
{
PEWNDOBJ pwoClient = (PEWNDOBJ)_pwoClient;
DBGENTRY("GreSetClientRgn\n");
// Assert that we are in user critical section and also hold the devlock.
// This ensures that no one is updating the hwnd.
CHECKUSERCRITIN;
if (pwoClient->hwnd)
{
CHECKDEVLOCKIN2(pwoClient->pto->pSurface);
}
// Validate hrgnClient.
if (!hrgnClient)
{
ASSERTGDI(FALSE, "GreSetClientRgn: hrgnClient is NULL\n");
return;
}
// Validate pwoClient.
if (!pwoClient->bValid())
{
ASSERTGDI(FALSE, "GreSetClientRgn: Invalid pwoClient\n");
bDeleteRegion(hrgnClient);
return;
}
// The WNDOBJ should only be modified once per update. A complete
// update includes a call to GreClientRgnUpdated.
#if DBG
if ((pwoClient->fl & (WO_RGN_CLIENT|WO_RGN_UPDATE_ALL))
== (WO_RGN_CLIENT|WO_RGN_UPDATE_ALL))
if (pwoClient->fl & WO_NOTIFIED)
DbgPrint("GreSetClientRgn: WNDOBJ updated more than once!\n");
#endif // DBG
// Get new and old regions.
GreSetRegionOwner(hrgnClient, OBJECT_OWNER_PUBLIC);
RGNOBJAPI roClient(hrgnClient,FALSE);
ASSERTGDI(roClient.bValid(), "GreSetClientRgn: invalid hrgnClient\n");
RGNOBJ roOld(pwoClient->prgn);
ERECTL erclClient(prcClient->left, prcClient->top,
prcClient->right, prcClient->bottom);
#ifdef OPENGL_MM
// Under Multi-mon we need to adjust the client region and
// client rectangle by the offset of the surface.
// Additionally we need to clip the client region to the
// surface of the TRACKOBJ.
if (!(pwoClient->fl & WO_RGN_DESKTOP_COORD))
{
PDEVOBJ pdo(pwoClient->pto->pSurface->hdev());
if (pdo.bValid())
{
if (pdo.bPrimary(pwoClient->pto->pSurface))
{
POINTL ptlOrigin;
ptlOrigin.x = -pdo.pptlOrigin()->x;
ptlOrigin.y = -pdo.pptlOrigin()->y;
if ((ptlOrigin.x != 0) || (ptlOrigin.y != 0))
{
// offset the region and window rect if necessary
roClient.bOffset(&ptlOrigin);
erclClient += ptlOrigin; /* this offsets by ptl */
}
}
}
RGNMEMOBJTMP rmoTmp;
RGNMEMOBJTMP rmoRcl;
if (rmoTmp.bValid() && rmoRcl.bValid())
{
// this clips the client region to the pto surface
rmoRcl.vSet((RECTL *) &pwoClient->pto->erclSurf);
rmoTmp.bCopy(roClient);
roClient.iCombine(rmoTmp,rmoRcl,RGN_AND);
if (rmoTmp.iCombine(roClient,rmoRcl,RGN_AND) != ERROR)
{
roClient.bSwap(&rmoTmp);
}
}
}
#endif // OPENGL_MM
// If the regions are equal, no need to notify driver here.
if (roOld.bEqual(roClient)
&& ((ERECTL *)&pwoClient->rclClient)->bEqual(erclClient))
{
roClient.bDeleteRGNOBJAPI(); // delete handle too
return;
}
// Enter the semphore for window object.
SEMOBJ so(ghsemWndobj);
// Now the WNDOBJ is going to get updated. Hold the per-WNDOBJ semaphore
// and keep it until we are done modifying and the driver update call has
// been made.
SEMOBJ soClient(pwoClient->hsem);
// Give the driver the client region delta.
// The delta is valid for this call only!
if (pwoClient->fl & WO_RGN_CLIENT_DELTA)
{
RGNMEMOBJTMP rmoDiff((BOOL)FALSE);
if (rmoDiff.bValid() &&
(rmoDiff.iCombine(roClient, roOld, RGN_DIFF) != ERROR))
{
pwoClient->bSwap(&rmoDiff);
pwoClient->prgn->vStamp(); // new iUniq
pwoClient->vSetClip(pwoClient->prgn, erclClient);
pwoClient->pto->vUpdateDrvDelta(pwoClient, WOC_RGN_CLIENT_DELTA);
}
}
// Update the new client region in WNDOBJ.
roClient.bSwap(pwoClient);
pwoClient->prgn->vStamp(); // new iUniq
pwoClient->vSetClip(pwoClient->prgn, erclClient);
roClient.bDeleteRGNOBJAPI(); // delete handle too
// Give the driver the new client region.
if (pwoClient->fl & WO_RGN_CLIENT)
{
pwoClient->pto->vUpdateDrv(pwoClient, WOC_RGN_CLIENT);
}
// Mark that we have visited this WNDOBJ and TRACKOBJ.
pwoClient->fl |= WO_NOTIFIED;
pwoClient->pto->fl |= WO_NOTIFIED;
return;
}
/******************************Member*Function*****************************\
* WNDOBJ_cEnumStart
*
* Start the window client region enumeration for the window object.
*
* This function can be called from the wndobjchangeproc that is passed to
* EngCreateWnd. It can also be called from DDI function where a WNDOBJ
* is given.
*
* This function should only be called when the calling thread has the
* devlock to ensure that there is no client region change.
*
* In future, we may want to add the pvConsumer and WNDOBJ pointer to the
* CLIPOBJ that is passed to the existing DDI. In this way, the WNDOBJ
* is always available to the DDI instead of the selected few.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Rewrote it.
* 11-Nov-1993 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/
extern "C" ULONG WNDOBJ_cEnumStart(
WNDOBJ *pwo,
ULONG iType,
ULONG iDir,
ULONG cLimit)
{
DBGENTRY("WNDOBJ_cEnumStart\n");
ASSERTGDI(((PEWNDOBJ)pwo)->bValid(), "WNDOBJ_cEnumStart: Invalid pwo\n");
// We need to ensure that the caller holds the devlock before calling this
// function. Otherwise, some other threads may be drawing into the wrong
// client region.
if (((PEWNDOBJ)pwo)->hwnd)
{
CHECKDEVLOCKIN2(((PEWNDOBJ)pwo)->pto->pSurface);
}
return (*(XCLIPOBJ *)pwo).cEnumStart(TRUE, iType, iDir, cLimit);
}
/******************************Member*Function*****************************\
* WNDOBJ_bEnum
*
* Enumerate the client region object in the window object.
*
* This function can be called from the wndobjchangeproc that is passed to
* EngCreateWnd. It can also be called from DDI function where a WNDOBJ
* is given.
*
* This function should only be called when the calling thread has the
* devlock to ensure that there is no client region change.
*
* In future, we may want to add the pvConsumer and WNDOBJ pointer to the
* CLIPOBJ that is passed to the existing DDI. In this way, the WNDOBJ
* is always available to the DDI instead of the selected few.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Rewrote it.
* 11-Nov-1993 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/
extern "C" BOOL WNDOBJ_bEnum(
WNDOBJ *pwo,
ULONG cj,
ULONG *pul)
{
DBGENTRY("WNDOBJ_bEnum\n");
ASSERTGDI(((PEWNDOBJ)pwo)->bValid(), "WNDOBJ_bEnum: Invalid pwo\n");
// We need to ensure that the caller holds the devlock before calling this
// function. Otherwise, some other threads may be drawing into the wrong
// client region.
if (((PEWNDOBJ)pwo)->hwnd)
{
CHECKDEVLOCKIN2(((PEWNDOBJ)pwo)->pto->pSurface);
}
return (*(XCLIPOBJ *)pwo).bEnum(cj, (VOID *)pul);
}
/******************************Member*Function*****************************\
* WNDOBJ_vSetConsumer
*
* Set the driver pvConsumer value in the window object. It should be
* used to modify the existing pvConsumer value.
*
* This function can be called from the wndobjchangeproc that is passed to
* EngCreateWnd. It can also be called from DDI function where a WNDOBJ
* is given.
*
* This function should only be called when the calling thread has the
* devlock to ensure that there is no client region change.
*
* History:
* Thu Jan 13 09:55:23 1994 -by- Hock San Lee [hockl]
* Wrote it.
\**************************************************************************/
extern "C" VOID WNDOBJ_vSetConsumer(
WNDOBJ *_pwo,
PVOID pvConsumer)
{
PEWNDOBJ pwo = (PEWNDOBJ)_pwo;
DBGENTRY("WNDOBJ_vSetConsumer\n");
ASSERTGDI(pwo->bValid(), "WNDOBJ_vSetConsumer: Invalid pwo\n");
// We need to ensure that the caller holds the devlock before calling this
// function. Otherwise, some other threads may be drawing into the wrong
// client region.
if (pwo->hwnd)
{
CHECKDEVLOCKIN2(pwo->pto->pSurface);
}
// Do not allow changes to surface wndobj. One reason is that there is
// no delete notification for surface wndobj.
if (pwo == pwo->pto->pwoSurf)
{
KdPrint(("WNDOBJ_vSetConsumer: cannot modify surface wndobj!\n"));
return;
}
pwo->pvConsumer = pvConsumer;
}
/******************************Member*Function*****************************\
* EWNDOBJ::vOffset
*
* Offset the WNDOBJ by some vector.
\**************************************************************************/
VOID EWNDOBJ::vOffset(LONG x, LONG y)
{
if ((x != 0) || (y != 0))
{
EPOINTL eptl(x, y);
bOffset(&eptl);
*((ERECTL*) &rclBounds) += eptl;
*((ERECTL*) &rclClient) += eptl;
}
}