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
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;
|
|
}
|
|
}
|