2929 lines
93 KiB
C
2929 lines
93 KiB
C
/******************************Module*Header*******************************\
|
|
* Module Name: mcdsrv.c
|
|
*
|
|
* This module contains the trusted component of the MCD server-side engine.
|
|
* This module performs handle management and parameter-checking and validation
|
|
* to the extent possible. This module also makes the calls to the device
|
|
* driver, and provides callbacks to the driver for things such as handle
|
|
* referencing.
|
|
*
|
|
* Goals
|
|
* -----
|
|
*
|
|
* Pervasive throughout this implementation is the influence of the
|
|
* following goals:
|
|
*
|
|
* 1. Robustness
|
|
*
|
|
* Windows NT is first and foremost a robust operating system. There
|
|
* is a simple measure for this: a robust system should never crash.
|
|
* Because the display driver is a trusted component of the operating
|
|
* system, and because the MCD is directly callable from OpenGL from
|
|
* the client side of the OS (and thus untrusted), this has a significant
|
|
* impact on the way we must do things.
|
|
*
|
|
* 2. Performance
|
|
*
|
|
* Performance is the 'raison d'etre' of the MCD; we have tried to
|
|
* have as thin a layer above the rendering code as we could.
|
|
*
|
|
* 3. Portability
|
|
*
|
|
* This implementation is intended portable to different processor types,
|
|
* and to the Windows 95 operating system.
|
|
*
|
|
* Obviously, Windows 95 implementations may choose to have a different
|
|
* order of priority for these goals, and so some of the robustness
|
|
* code may be eliminated. But it is still recommended that it be kept;
|
|
* the overhead is reasonably minimal, and people really don't like it
|
|
* when their systems crash...
|
|
*
|
|
* The Rules of Robustness
|
|
* -----------------------
|
|
*
|
|
* 1. Nothing given by the caller can be trusted.
|
|
*
|
|
* For example, handles cannot be trusted to be valid. Handles passed
|
|
* in may actually be for objects not owned by the caller. Pointers
|
|
* and offsets may not be correctly aligned. Pointers, offsets, and
|
|
* coordinates may be out of bounds.
|
|
*
|
|
* 2. Parameters can be asynchronously modified at any time.
|
|
*
|
|
* Many commands come from shared memory sections, and any data therein
|
|
* may be asynchronously modified by other threads in the calling
|
|
* application. As such, parameters may never be validated in-place
|
|
* in the shared section, because the application may corrupt the data
|
|
* after validation but before its use. Instead, parameters must always
|
|
* be first copied out of the window, and then validated on the safe
|
|
* copy.
|
|
*
|
|
* 3. We must clean up.
|
|
*
|
|
* Applications may die at any time before calling the appropriate
|
|
* clean up functions. As such, we have to be prepared to clean up
|
|
* any resources ourselves when the application dies.
|
|
*
|
|
* Copyright (c) 1994, 1995, 1996 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include <stddef.h>
|
|
#include <stdarg.h>
|
|
#include <windows.h>
|
|
|
|
#include <wtypes.h>
|
|
|
|
#include <winddi.h>
|
|
#include <mcdesc.h>
|
|
|
|
#include "mcdrv.h"
|
|
#include <mcd2hack.h>
|
|
#include "mcd.h"
|
|
#include "mcdint.h"
|
|
#include "mcdrvint.h"
|
|
|
|
|
|
// Checks MCD version to see if the driver can accept direct buffer
|
|
// access. Direct access was introduced in 1.1.
|
|
#define SUPPORTS_DIRECT(pGlobal) \
|
|
((pGlobal)->verMinor >= 0x10 || (pGlobal)->verMajor > 1)
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// Declarations for internal support functions for an MCD locking mechanism
|
|
// that can be used to synchronize multiple processes/thread that use MCD.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG MCDSrvLock(MCDWINDOWPRIV *);
|
|
VOID MCDSrvUnlock(MCDWINDOWPRIV *);
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Declarations for internal per-driver-instance list that all global
|
|
// data is kept in. The list is indexed by pso.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Space for one old-style driver to hold its information statically.
|
|
MCDGLOBALINFO gStaticGlobalInfo;
|
|
|
|
BOOL MCDSrvInitGlobalInfo(void);
|
|
void MCDSrvUninitGlobalInfo(void);
|
|
MCDGLOBALINFO *MCDSrvAddGlobalInfo(SURFOBJ *pso);
|
|
MCDGLOBALINFO *MCDSrvGetGlobalInfo(SURFOBJ *pso);
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// Server subsystem entry points.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//****************************************************************************
|
|
//
|
|
// MCD initialization functions.
|
|
//
|
|
// NT 4.0 MCD support exported MCDEngInit which display drivers call
|
|
// to initialize the MCD server-side code. MCDEngInit only allowed
|
|
// one driver instance to initialize and never uninitialized.
|
|
//
|
|
// This doesn't work very well with mode changes or multimon so for
|
|
// NT 5.0 MCDEngInitEx was added. MCDEngInitEx has two differences
|
|
// from MCDEngInit:
|
|
// 1. MCDEngInitEx takes a table of global driver functions instead of
|
|
// just the MCDrvGetEntryPoints function. Currently the table only
|
|
// has one entry for MCDrvGetEntryPoints but it allows for future
|
|
// expansion.
|
|
// 2. Calling MCDEngInitEx implies that the driver will call MCDEngUninit
|
|
// so that per-driver-instance state can be cleaned up.
|
|
//
|
|
//****************************************************************************
|
|
|
|
BOOL MCDEngInternalInit(SURFOBJ *pso,
|
|
MCDGLOBALDRIVERFUNCS *pMCDGlobalDriverFuncs,
|
|
BOOL bAddPso)
|
|
{
|
|
MCDSURFACE mcdSurface;
|
|
MCDDRIVER mcdDriver;
|
|
MCDGLOBALINFO *pGlobal;
|
|
|
|
mcdSurface.pWnd = NULL;
|
|
mcdSurface.pwo = NULL;
|
|
mcdSurface.surfaceFlags = 0;
|
|
mcdSurface.pso = pso;
|
|
|
|
memset(&mcdDriver, 0, sizeof(MCDDRIVER));
|
|
mcdDriver.ulSize = sizeof(MCDDRIVER);
|
|
|
|
if (pMCDGlobalDriverFuncs->pMCDrvGetEntryPoints == NULL ||
|
|
!pMCDGlobalDriverFuncs->pMCDrvGetEntryPoints(&mcdSurface, &mcdDriver))
|
|
{
|
|
MCDBG_PRINT("MCDEngInit: Could not get driver entry points.");
|
|
return FALSE;
|
|
}
|
|
|
|
if (bAddPso)
|
|
{
|
|
if (!MCDSrvInitGlobalInfo())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pGlobal = MCDSrvAddGlobalInfo(pso);
|
|
if (pGlobal == NULL)
|
|
{
|
|
MCDSrvUninitGlobalInfo();
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pGlobal = &gStaticGlobalInfo;
|
|
}
|
|
|
|
// Guaranteed to be zero-filled and pso set so only fill in interesting
|
|
// fields.
|
|
// verMajor and verMinor can not be filled out yet so they are
|
|
// left at zero to indicate the most conservative possible version
|
|
// number. They are filled in with correct information when DRIVERINFO
|
|
// is processed.
|
|
pGlobal->mcdDriver = mcdDriver;
|
|
pGlobal->mcdGlobalFuncs = *pMCDGlobalDriverFuncs;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define MGDF_SIZE (sizeof(ULONG)+sizeof(void *))
|
|
|
|
BOOL WINAPI MCDEngInitEx(SURFOBJ *pso,
|
|
MCDGLOBALDRIVERFUNCS *pMCDGlobalDriverFuncs,
|
|
void *pReserved)
|
|
{
|
|
if (pso == NULL ||
|
|
pMCDGlobalDriverFuncs->ulSize != MGDF_SIZE ||
|
|
pReserved != NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return MCDEngInternalInit(pso, pMCDGlobalDriverFuncs, TRUE);
|
|
}
|
|
|
|
BOOL WINAPI MCDEngInit(SURFOBJ *pso,
|
|
MCDRVGETENTRYPOINTSFUNC pGetDriverEntryFunc)
|
|
{
|
|
MCDGLOBALDRIVERFUNCS mgdf;
|
|
|
|
// The old-style initialization function is being called so
|
|
// we must assume that the uninit function will not be called.
|
|
// This means that we cannot allocate resources for the global
|
|
// info list since we won't be able to clean them up. Without
|
|
// a global info list we are restricted to using global variables
|
|
// and thus only one old-style init is allowed per load.
|
|
|
|
if (pso == NULL ||
|
|
pGetDriverEntryFunc == NULL ||
|
|
gStaticGlobalInfo.pso != NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gStaticGlobalInfo.pso = pso;
|
|
|
|
memset(&mgdf, 0, sizeof(mgdf));
|
|
mgdf.ulSize = sizeof(ULONG)+sizeof(void *);
|
|
mgdf.pMCDrvGetEntryPoints = pGetDriverEntryFunc;
|
|
|
|
return MCDEngInternalInit(pso, &mgdf, FALSE);
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// BOOL MCDEngEscFilter(SURFOBJ *, ULONG, ULONG, VOID *, ULONG cjOut,
|
|
// VOID *pvOut)
|
|
//
|
|
// MCD escape filter. This function should return TRUE for any
|
|
// escapes functions which this filter processed, FALSE otherwise (in which
|
|
// case the caller should continue to process the escape).
|
|
//****************************************************************************
|
|
|
|
BOOL WINAPI MCDEngEscFilter(SURFOBJ *pso, ULONG iEsc,
|
|
ULONG cjIn, VOID *pvIn,
|
|
ULONG cjOut, VOID *pvOut, ULONG_PTR *pRetVal)
|
|
{
|
|
MCDEXEC MCDExec;
|
|
MCDESC_HEADER *pmeh;
|
|
MCDESC_HEADER_NTPRIVATE *pmehPriv;
|
|
|
|
switch (iEsc)
|
|
{
|
|
case QUERYESCSUPPORT:
|
|
|
|
// Note: we don't need to check cjIn for this case since
|
|
// NT's GDI validates this for use.
|
|
|
|
return (BOOL)(*pRetVal = (*(ULONG *) pvIn == MCDFUNCS));
|
|
|
|
case MCDFUNCS:
|
|
|
|
MCDExec.pmeh = pmeh = (MCDESC_HEADER *)pvIn;
|
|
|
|
// This is an MCD function. Under Windows NT, we've
|
|
// got an MCDESC_HEADER_NTPRIVATE structure which we may need
|
|
// to use if the escape does not use driver-created
|
|
// memory.
|
|
|
|
// Package the things we need into the MCDEXEC structure:
|
|
|
|
pmehPriv = (MCDESC_HEADER_NTPRIVATE *)(pmeh + 1);
|
|
|
|
MCDExec.ppwoMulti = (WNDOBJ **)pmehPriv->pExtraWndobj;
|
|
MCDExec.MCDSurface.pwo = pmehPriv->pwo;
|
|
|
|
if (pmeh->dwWindow != 0)
|
|
{
|
|
MCDWINDOWOBJ *pmwo;
|
|
|
|
// The client side code has given us back the handle
|
|
// to the MCDWINDOW structure as an identifier. Since it
|
|
// came from user-mode it is suspect and must be validated
|
|
// before continuing.
|
|
pmwo = (MCDWINDOWOBJ *)
|
|
MCDEngGetPtrFromHandle((MCDHANDLE)pmeh->dwWindow,
|
|
MCDHANDLE_WINDOW);
|
|
if (pmwo == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
MCDExec.pWndPriv = &pmwo->MCDWindowPriv;
|
|
}
|
|
else
|
|
{
|
|
MCDExec.pWndPriv = NULL;
|
|
}
|
|
|
|
MCDExec.MCDSurface.pso = pso;
|
|
MCDExec.MCDSurface.pWnd = (MCDWINDOW *)MCDExec.pWndPriv;
|
|
MCDExec.MCDSurface.surfaceFlags = 0;
|
|
|
|
MCDExec.pvOut = pvOut;
|
|
MCDExec.cjOut = cjOut;
|
|
|
|
if (!pmeh->hSharedMem) {
|
|
|
|
*pRetVal = (ULONG)FALSE;
|
|
|
|
if (!pmehPriv->pBuffer)
|
|
return (ULONG)TRUE;
|
|
|
|
if (pmehPriv->bufferSize < sizeof(MCDCMDI))
|
|
return (ULONG)TRUE;
|
|
|
|
MCDExec.pCmd = (MCDCMDI *)(pmehPriv->pBuffer);
|
|
MCDExec.pCmdEnd = (MCDCMDI *)((char *)MCDExec.pCmd +
|
|
pmehPriv->bufferSize);
|
|
MCDExec.inBufferSize = pmehPriv->bufferSize;
|
|
MCDExec.hMCDMem = (MCDHANDLE)NULL;
|
|
} else
|
|
MCDExec.hMCDMem = pmeh->hSharedMem;
|
|
|
|
ENTER_MCD_LOCK();
|
|
|
|
*pRetVal = MCDSrvProcess(&MCDExec);
|
|
|
|
LEAVE_MCD_LOCK();
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
return (ULONG)FALSE;
|
|
break;
|
|
}
|
|
|
|
return (ULONG)FALSE; // Should never get here...
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// BOOL MCDEngSetMemStatus(MCDMEM *pMCDMem, ULONG status);
|
|
//
|
|
// Sets the memory status to the desired value. This is called by the
|
|
// driver to set and reset the busy flags for a chunk of memory to allow
|
|
// DMA.
|
|
//****************************************************************************
|
|
|
|
|
|
BOOL WINAPI MCDEngSetMemStatus(MCDMEM *pMCDMem, ULONG status)
|
|
{
|
|
MCDMEMOBJ *pMemObj;
|
|
ULONG retVal;
|
|
|
|
pMemObj = (MCDMEMOBJ *)((char *)pMCDMem - sizeof(MCDHANDLETYPE));
|
|
|
|
if (pMemObj->type != MCDHANDLE_MEM) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (status) {
|
|
case MCDRV_MEM_BUSY:
|
|
pMemObj->lockCount++;
|
|
break;
|
|
case MCDRV_MEM_NOT_BUSY:
|
|
pMemObj->lockCount--;
|
|
break;
|
|
default:
|
|
return (ULONG)FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// Private server-side funtions.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//****************************************************************************
|
|
// CallGetBuffers
|
|
//
|
|
// Wrapper for MCDrvGetBuffers that does appropriate checks, setup,
|
|
// cache management and data translation.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
ULONG CallGetBuffers(MCDEXEC *pMCDExec, MCDRC *pRc, MCDRECTBUFFERS *pBuf)
|
|
{
|
|
ULONG ulRet;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvGetBuffers)
|
|
{
|
|
MCDBG_PRINT("MCDrvGetBuffers: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
// Clip lists need to be valid so drivers can do different
|
|
// things based on whether the surface is trivially visible or not.
|
|
GetScissorClip(pMCDExec->pWndPriv, pMCDExec->pRcPriv);
|
|
|
|
// Should be casting to MCDRECTBUFFERS with correct
|
|
// 1.1 header.
|
|
ulRet = (ULONG)(*pMCDExec->pGlobal->mcdDriver.pMCDrvGetBuffers)
|
|
(&pMCDExec->MCDSurface, pRc, (MCDBUFFERS *)pBuf);
|
|
|
|
// Update cached buffers information on success.
|
|
if (ulRet)
|
|
{
|
|
if (SUPPORTS_DIRECT(pMCDExec->pGlobal))
|
|
{
|
|
// This is a 1.1 or greater driver and has returned
|
|
// full MCDRECTBUFFERS information. Cache it
|
|
// for possible later use.
|
|
|
|
pMCDExec->pWndPriv->bBuffersValid = TRUE;
|
|
pMCDExec->pWndPriv->mbufCache = *pBuf;
|
|
}
|
|
else
|
|
{
|
|
MCDBUFFERS mbuf;
|
|
MCDRECTBUFFERS *mrbuf;
|
|
|
|
// This is a 1.0 driver and has only returned
|
|
// MCDBUFFERS information. Expand it into
|
|
// an MCDRECTBUFFERS. The rectangles don't
|
|
// really matter to software so they can
|
|
// be zeroed.
|
|
|
|
mbuf = *(MCDBUFFERS *)pBuf;
|
|
mrbuf = pBuf;
|
|
*(MCDBUF *)&mrbuf->mcdFrontBuf = mbuf.mcdFrontBuf;
|
|
memset(&mrbuf->mcdFrontBuf.bufPos, 0, sizeof(RECTL));
|
|
*(MCDBUF *)&mrbuf->mcdBackBuf = mbuf.mcdBackBuf;
|
|
memset(&mrbuf->mcdBackBuf.bufPos, 0, sizeof(RECTL));
|
|
*(MCDBUF *)&mrbuf->mcdDepthBuf = mbuf.mcdDepthBuf;
|
|
memset(&mrbuf->mcdDepthBuf.bufPos, 0, sizeof(RECTL));
|
|
}
|
|
}
|
|
|
|
return ulRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// ULONG_PTR MCDSrvProcess(MCDEXEC *pMCDExec)
|
|
//
|
|
// This is the main MCD function handler. At this point, there should
|
|
// be no platform-specific code since these should have been resolved by
|
|
// the entry function.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
ULONG_PTR MCDSrvProcess(MCDEXEC *pMCDExec)
|
|
{
|
|
UCHAR *pMaxMem;
|
|
UCHAR *pMinMem;
|
|
MCDESC_HEADER *pmeh = pMCDExec->pmeh;
|
|
MCDRC *pRc;
|
|
MCDMEM *pMCDMem;
|
|
MCDMEMOBJ *pMemObj;
|
|
MCDRCPRIV *pRcPriv;
|
|
ULONG_PTR ulRet;
|
|
|
|
// If the command buffer is in shared memory, dereference the memory
|
|
// from the handle and check the bounds.
|
|
|
|
if (pMCDExec->hMCDMem)
|
|
{
|
|
GET_MEMOBJ_RETVAL(pMemObj, pmeh->hSharedMem, FALSE);
|
|
|
|
pMinMem = pMemObj->MCDMem.pMemBase;
|
|
|
|
// Note: we ignore the memory size in the header since it doesn't
|
|
// really help us...
|
|
|
|
pMaxMem = pMinMem + pMemObj->MCDMem.memSize;
|
|
|
|
pMCDExec->pCmd = (MCDCMDI *)((char *)pmeh->pSharedMem);
|
|
pMCDExec->pCmdEnd = (MCDCMDI *)pMaxMem;
|
|
|
|
CHECK_MEM_RANGE_RETVAL(pMCDExec->pCmd, pMinMem, pMaxMem, FALSE);
|
|
|
|
pMCDExec->inBufferSize = pmeh->sharedMemSize;
|
|
|
|
pMCDExec->pMemObj = pMemObj;
|
|
} else
|
|
pMCDExec->pMemObj = (MCDMEMOBJ *)NULL;
|
|
|
|
|
|
// Get the rendering context if we have one, and process the command:
|
|
|
|
if (pmeh->hRC)
|
|
{
|
|
MCDRCOBJ *pRcObj;
|
|
|
|
pRcObj = (MCDRCOBJ *)MCDEngGetPtrFromHandle(pmeh->hRC, MCDHANDLE_RC);
|
|
|
|
if (!pRcObj)
|
|
{
|
|
MCDBG_PRINT("MCDSrvProcess: Invalid rendering context handle %x.",
|
|
pmeh->hRC);
|
|
return FALSE;
|
|
}
|
|
|
|
pMCDExec->pRcPriv = pRcPriv = pRcObj->pRcPriv;
|
|
|
|
if (!pRcPriv->bValid)
|
|
{
|
|
MCDBG_PRINT("MCDSrvProcess: RC has been invalidated for this window.");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((!pMCDExec->pWndPriv)) {
|
|
if (pMCDExec->pCmd->command != MCD_BINDCONTEXT) {
|
|
MCDBG_PRINT("MCDSrvProcess: NULL WndObj with RC.");
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
// Validate the window in the RC with the window for this escape:
|
|
|
|
if ((pRcPriv->hWnd != pMCDExec->pWndPriv->hWnd) &&
|
|
(pMCDExec->pCmd->command != MCD_BINDCONTEXT))
|
|
{
|
|
MCDBG_PRINT("MCDSrvProcess: Invalid RC for this window.");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// For Win95, we need to poll for the clip region:
|
|
// Clipping needs to be un-broken
|
|
if (pMCDExec->MCDSurface.pwo != NULL)
|
|
{
|
|
MCDEngUpdateClipList(pMCDExec->MCDSurface.pwo);
|
|
}
|
|
|
|
pMCDExec->MCDSurface.surfaceFlags |= pRcPriv->surfaceFlags;
|
|
|
|
} else {
|
|
pMCDExec->pRcPriv = (MCDRCPRIV *)NULL;
|
|
}
|
|
|
|
// Get global driver information.
|
|
if (pMCDExec->pWndPriv != NULL)
|
|
{
|
|
pMCDExec->pGlobal = pMCDExec->pWndPriv->pGlobal;
|
|
}
|
|
else if (pMCDExec->pRcPriv != NULL)
|
|
{
|
|
pMCDExec->pGlobal = pMCDExec->pRcPriv->pGlobal;
|
|
}
|
|
else
|
|
{
|
|
pMCDExec->pGlobal =
|
|
MCDSrvGetGlobalInfo(pMCDExec->MCDSurface.pso);
|
|
if (pMCDExec->pGlobal == NULL)
|
|
{
|
|
MCDBG_PRINT("Unable to find global information");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// If direct surface information was included then
|
|
// fill out the extra surface information in the MCDSURFACE
|
|
// NOCLIP setting?
|
|
|
|
#if MCD_VER_MAJOR >= 2 || (MCD_VER_MAJOR == 1 && MCD_VER_MINOR >= 0x10)
|
|
pMCDExec->MCDSurface.direct.mcdFrontBuf.bufFlags = 0;
|
|
pMCDExec->MCDSurface.direct.mcdBackBuf.bufFlags = 0;
|
|
pMCDExec->MCDSurface.direct.mcdDepthBuf.bufFlags = 0;
|
|
|
|
pMCDExec->MCDSurface.frontId = 0;
|
|
pMCDExec->MCDSurface.backId = 0;
|
|
pMCDExec->MCDSurface.depthId = 0;
|
|
|
|
if (pmeh->flags & MCDESC_FL_SURFACES)
|
|
{
|
|
pMCDExec->MCDSurface.surfaceFlags |= MCDSURFACE_DIRECT;
|
|
|
|
// Refresh cached buffer information if it's invalid
|
|
// and we need it
|
|
if (pmeh->msrfColor.hSurf == NULL &&
|
|
pmeh->msrfDepth.hSurf == NULL)
|
|
{
|
|
if (pMCDExec->pWndPriv == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!pMCDExec->pWndPriv->bBuffersValid)
|
|
{
|
|
MCDRECTBUFFERS mbuf;
|
|
|
|
if (!CallGetBuffers(pMCDExec, NULL, &mbuf))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pMCDExec->MCDSurface.direct = pMCDExec->pWndPriv->mbufCache;
|
|
}
|
|
else
|
|
{
|
|
if (pmeh->msrfColor.hSurf != NULL)
|
|
{
|
|
pMCDExec->MCDSurface.frontId = (DWORD)
|
|
pmeh->msrfColor.hSurf;
|
|
pMCDExec->MCDSurface.direct.mcdFrontBuf.bufFlags =
|
|
MCDBUF_ENABLED;
|
|
pMCDExec->MCDSurface.direct.mcdFrontBuf.bufOffset =
|
|
pmeh->msrfColor.lOffset;
|
|
pMCDExec->MCDSurface.direct.mcdFrontBuf.bufStride =
|
|
pmeh->msrfColor.lStride;
|
|
pMCDExec->MCDSurface.direct.mcdFrontBuf.bufPos =
|
|
pmeh->msrfColor.rclPos;
|
|
}
|
|
|
|
if (pmeh->msrfDepth.hSurf != NULL)
|
|
{
|
|
pMCDExec->MCDSurface.depthId = (DWORD)
|
|
pmeh->msrfDepth.hSurf;
|
|
pMCDExec->MCDSurface.direct.mcdDepthBuf.bufFlags =
|
|
MCDBUF_ENABLED;
|
|
pMCDExec->MCDSurface.direct.mcdDepthBuf.bufOffset =
|
|
pmeh->msrfDepth.lOffset;
|
|
pMCDExec->MCDSurface.direct.mcdDepthBuf.bufStride =
|
|
pmeh->msrfDepth.lStride;
|
|
pMCDExec->MCDSurface.direct.mcdDepthBuf.bufPos =
|
|
pmeh->msrfDepth.rclPos;
|
|
}
|
|
}
|
|
}
|
|
#endif // 1.1
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// If the drawing-batch flag is set, call the main driver drawing
|
|
// routine:
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
if (pmeh->flags & MCDESC_FL_BATCH)
|
|
{
|
|
CHECK_FOR_RC(pMCDExec);
|
|
CHECK_FOR_MEM(pMCDExec);
|
|
GetScissorClip(pMCDExec->pWndPriv, pMCDExec->pRcPriv);
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvDraw)
|
|
{
|
|
if (pMCDExec->pGlobal->mcdDriver.pMCDrvSync)
|
|
{
|
|
(*pMCDExec->pGlobal->mcdDriver.pMCDrvSync)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc);
|
|
}
|
|
return (ULONG_PTR)pMCDExec->pCmd;
|
|
}
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvDraw)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc, &pMemObj->MCDMem,
|
|
(UCHAR *)pMCDExec->pCmd, (UCHAR *)pMCDExec->pCmdEnd);
|
|
}
|
|
|
|
if (pmeh->flags & MCDESC_FL_CREATE_CONTEXT)
|
|
{
|
|
MCDCREATECONTEXT *pmcc = (MCDCREATECONTEXT *)pMCDExec->pCmd;
|
|
MCDRCINFOPRIV *pMcdRcInfo = pmcc->pRcInfo;
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDCREATECONTEXT);
|
|
CHECK_SIZE_OUT(pMCDExec, MCDRCINFOPRIV);
|
|
|
|
try {
|
|
EngProbeForRead(pMcdRcInfo, sizeof(MCDRCINFOPRIV),
|
|
sizeof(ULONG));
|
|
RtlCopyMemory(pMCDExec->pvOut, pMcdRcInfo,
|
|
sizeof(MCDRCINFOPRIV));
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
MCDBG_PRINT("MCDrvCreateContext: Invalid memory for MCDRCINFO.");
|
|
return FALSE;
|
|
}
|
|
|
|
pMcdRcInfo = (MCDRCINFOPRIV *)pMCDExec->pvOut;
|
|
pMcdRcInfo->mri.requestFlags = 0;
|
|
|
|
return (ULONG_PTR)MCDSrvCreateContext(&pMCDExec->MCDSurface,
|
|
pMcdRcInfo, pMCDExec->pGlobal,
|
|
pmcc->ipfd, pmcc->iLayer,
|
|
pmcc->escCreate.hwnd,
|
|
pmcc->escCreate.flags,
|
|
pmcc->mcdFlags);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Now, process all of the non-batched drawing and utility commands:
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
switch (pMCDExec->pCmd->command) {
|
|
|
|
case MCD_DESCRIBEPIXELFORMAT:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDPIXELFORMATCMDI);
|
|
|
|
if (pMCDExec->pvOut) {
|
|
CHECK_SIZE_OUT(pMCDExec, MCDPIXELFORMAT);
|
|
}
|
|
|
|
{
|
|
MCDPIXELFORMATCMDI *pMCDPixelFormat =
|
|
(MCDPIXELFORMATCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvDescribePixelFormat)
|
|
return 0;
|
|
|
|
return (*pMCDExec->pGlobal->mcdDriver.pMCDrvDescribePixelFormat)
|
|
(&pMCDExec->MCDSurface,
|
|
pMCDPixelFormat->iPixelFormat,
|
|
pMCDExec->cjOut,
|
|
pMCDExec->pvOut, 0);
|
|
}
|
|
|
|
case MCD_DRIVERINFO:
|
|
|
|
CHECK_SIZE_OUT(pMCDExec, MCDDRIVERINFOI);
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvInfo)
|
|
return FALSE;
|
|
|
|
ulRet = (*pMCDExec->pGlobal->mcdDriver.pMCDrvInfo)
|
|
(&pMCDExec->MCDSurface,
|
|
(MCDDRIVERINFO *)pMCDExec->pvOut);
|
|
|
|
if (ulRet)
|
|
{
|
|
// Copy driver function information so that the client
|
|
// side can optimize calls by checking for functions on the
|
|
// client side.
|
|
|
|
memcpy(&((MCDDRIVERINFOI *)pMCDExec->pvOut)->mcdDriver,
|
|
&pMCDExec->pGlobal->mcdDriver, sizeof(MCDDRIVER));
|
|
|
|
// Save version information in global info.
|
|
|
|
pMCDExec->pGlobal->verMajor =
|
|
((MCDDRIVERINFO *)pMCDExec->pvOut)->verMajor;
|
|
pMCDExec->pGlobal->verMinor =
|
|
((MCDDRIVERINFO *)pMCDExec->pvOut)->verMinor;
|
|
}
|
|
|
|
return ulRet;
|
|
|
|
case MCD_DELETERC:
|
|
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
return (ULONG_PTR)DestroyMCDObj(pmeh->hRC, MCDHANDLE_RC);
|
|
|
|
case MCD_ALLOC:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDALLOCCMDI);
|
|
CHECK_SIZE_OUT(pMCDExec, MCDHANDLE *);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDALLOCCMDI *pAllocCmd =
|
|
(MCDALLOCCMDI *)pMCDExec->pCmd;
|
|
|
|
return (ULONG_PTR)MCDSrvAllocMem(pMCDExec, pAllocCmd->numBytes,
|
|
pAllocCmd->flags,
|
|
(MCDHANDLE *)pMCDExec->pvOut);
|
|
}
|
|
|
|
case MCD_FREE:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDFREECMDI);
|
|
|
|
{
|
|
MCDFREECMDI *pFreeCmd =
|
|
(MCDFREECMDI *)pMCDExec->pCmd;
|
|
|
|
return (ULONG_PTR)DestroyMCDObj(pFreeCmd->hMCDMem, MCDHANDLE_MEM);
|
|
}
|
|
|
|
case MCD_STATE:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDSTATECMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
CHECK_FOR_MEM(pMCDExec);
|
|
|
|
{
|
|
MCDSTATECMDI *pStateCmd =
|
|
(MCDSTATECMDI *)pMCDExec->pCmd;
|
|
UCHAR *pStart = (UCHAR *)(pStateCmd + 1);
|
|
LONG totalBytes = pMCDExec->inBufferSize -
|
|
sizeof(MCDSTATECMDI);
|
|
|
|
if (totalBytes < 0) {
|
|
MCDBG_PRINT("MCDState: state buffer too small ( < 0).");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvState) {
|
|
MCDBG_PRINT("MCDrvState: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvState)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc, &pMemObj->MCDMem, pStart,
|
|
totalBytes, pStateCmd->numStates);
|
|
}
|
|
|
|
case MCD_VIEWPORT:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDVIEWPORTCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDVIEWPORTCMDI *pViewportCmd =
|
|
(MCDVIEWPORTCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvViewport) {
|
|
MCDBG_PRINT("MCDrvViewport: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvViewport)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc, &pViewportCmd->MCDViewport);
|
|
}
|
|
|
|
case MCD_QUERYMEMSTATUS:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDMEMSTATUSCMDI);
|
|
|
|
{
|
|
MCDMEMSTATUSCMDI *pQueryMemCmd =
|
|
(MCDMEMSTATUSCMDI *)pMCDExec->pCmd;
|
|
|
|
return MCDSrvQueryMemStatus(pMCDExec, pQueryMemCmd->hMCDMem);
|
|
}
|
|
|
|
|
|
case MCD_READSPAN:
|
|
case MCD_WRITESPAN:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDSPANCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
GetScissorClip(pMCDExec->pWndPriv, pMCDExec->pRcPriv);
|
|
|
|
{
|
|
MCDSPANCMDI *pSpanCmd =
|
|
(MCDSPANCMDI *)pMCDExec->pCmd;
|
|
|
|
GET_MEMOBJ_RETVAL(pMemObj, pSpanCmd->hMem, FALSE);
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvSpan) {
|
|
MCDBG_PRINT("MCDrvSpan: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
pMinMem = pMemObj->MCDMem.pMemBase;
|
|
pMaxMem = pMinMem + pMemObj->MCDMem.memSize;
|
|
|
|
// At least check that the first pixel is in range. The driver
|
|
// must validate the end pixel...
|
|
|
|
CHECK_MEM_RANGE_RETVAL(pSpanCmd->MCDSpan.pPixels, pMinMem, pMaxMem, FALSE);
|
|
|
|
if (pMCDExec->pCmd->command == MCD_READSPAN)
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvSpan)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc, &pMemObj->MCDMem, &pSpanCmd->MCDSpan, TRUE);
|
|
else
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvSpan)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc, &pMemObj->MCDMem, &pSpanCmd->MCDSpan, FALSE);
|
|
}
|
|
|
|
|
|
case MCD_CLEAR:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDCLEARCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
GetScissorClip(pMCDExec->pWndPriv, pMCDExec->pRcPriv);
|
|
|
|
{
|
|
MCDCLEARCMDI *pClearCmd =
|
|
(MCDCLEARCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvClear) {
|
|
MCDBG_PRINT("MCDrvClear: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvClear)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc, pClearCmd->buffers);
|
|
}
|
|
|
|
case MCD_SWAP:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDSWAPCMDI);
|
|
CHECK_FOR_WND(pMCDExec);
|
|
GetScissorClip(pMCDExec->pWndPriv, NULL);
|
|
|
|
{
|
|
MCDSWAPCMDI *pSwapCmd =
|
|
(MCDSWAPCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvSwap) {
|
|
MCDBG_PRINT("MCDrvSwap: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvSwap)
|
|
(&pMCDExec->MCDSurface,
|
|
pSwapCmd->flags);
|
|
}
|
|
|
|
case MCD_SCISSOR:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDSCISSORCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDSCISSORCMDI *pMCDScissor = (MCDSCISSORCMDI *)pMCDExec->pCmd;
|
|
|
|
return (ULONG_PTR)MCDSrvSetScissor(pMCDExec, &pMCDScissor->rect,
|
|
pMCDScissor->bEnabled);
|
|
}
|
|
break;
|
|
|
|
case MCD_ALLOCBUFFERS:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDALLOCBUFFERSCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDALLOCBUFFERSCMDI *pMCDAllocBuffersCmd = (MCDALLOCBUFFERSCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pWndPriv->bRegionValid)
|
|
pMCDExec->pWndPriv->MCDWindow.clientRect =
|
|
pMCDAllocBuffersCmd->WndRect;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvAllocBuffers) {
|
|
MCDBG_PRINT("MCDrvAllocBuffers: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvAllocBuffers)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_GETBUFFERS:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDGETBUFFERSCMDI);
|
|
CHECK_SIZE_OUT(pMCDExec, MCDRECTBUFFERS);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
return CallGetBuffers(pMCDExec, &pMCDExec->pRcPriv->MCDRc,
|
|
(MCDRECTBUFFERS *)pMCDExec->pvOut);
|
|
|
|
case MCD_LOCK:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDLOCKCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
return MCDSrvLock(pMCDExec->pWndPriv);
|
|
|
|
break;
|
|
|
|
case MCD_UNLOCK:
|
|
CHECK_SIZE_IN(pMCDExec, MCDLOCKCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
MCDSrvUnlock(pMCDExec->pWndPriv);
|
|
|
|
return TRUE;
|
|
|
|
break;
|
|
|
|
case MCD_BINDCONTEXT:
|
|
|
|
CHECK_SIZE_IN(pMCDExec, MCDBINDCONTEXTCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
ULONG_PTR retVal;
|
|
MCDBINDCONTEXTCMDI *pMCDBindContext = (MCDBINDCONTEXTCMDI *)pMCDExec->pCmd;
|
|
MCDWINDOW *pWndRes;
|
|
|
|
if ((!pMCDExec->pWndPriv)) {
|
|
pWndRes = MCDSrvNewMCDWindow(&pMCDExec->MCDSurface,
|
|
pMCDBindContext->hWnd,
|
|
pMCDExec->pGlobal,
|
|
pMCDExec->pRcPriv->hDev);
|
|
if (!pWndRes)
|
|
{
|
|
MCDBG_PRINT("MCDBindContext: Creation of window object failed.");
|
|
return 0;
|
|
}
|
|
|
|
pMCDExec->pWndPriv = (MCDWINDOWPRIV *)pWndRes;
|
|
|
|
}
|
|
|
|
if (!pMCDExec->MCDSurface.pWnd) {
|
|
MCDBG_PRINT("MCDrvBindContext: NULL surface.");
|
|
return 0;
|
|
}
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvBindContext) {
|
|
MCDBG_PRINT("MCDrvBindContext: missing entry point.");
|
|
return 0;
|
|
}
|
|
|
|
retVal = (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvBindContext)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc);
|
|
|
|
if (retVal)
|
|
{
|
|
pRcPriv->hWnd = pMCDBindContext->hWnd;
|
|
retVal = (ULONG_PTR)pMCDExec->pWndPriv->handle;
|
|
}
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_SYNC:
|
|
CHECK_SIZE_IN(pMCDExec, MCDSYNCCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvSync) {
|
|
MCDBG_PRINT("MCDrvSync: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvSync)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc);
|
|
|
|
break;
|
|
|
|
case MCD_CREATE_TEXTURE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDCREATETEXCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDCREATETEXCMDI *pMCDCreateTex =
|
|
(MCDCREATETEXCMDI *)pMCDExec->pCmd;
|
|
|
|
return (ULONG_PTR)MCDSrvCreateTexture(pMCDExec,
|
|
pMCDCreateTex->pTexData,
|
|
pMCDCreateTex->pSurface,
|
|
pMCDCreateTex->flags);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_DELETE_TEXTURE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDDELETETEXCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDDELETETEXCMDI *pMCDDeleteTex =
|
|
(MCDDELETETEXCMDI *)pMCDExec->pCmd;
|
|
|
|
return (ULONG_PTR)DestroyMCDObj(pMCDDeleteTex->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_UPDATE_SUB_TEXTURE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDUPDATESUBTEXCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDUPDATESUBTEXCMDI *pMCDUpdateSubTex =
|
|
(MCDUPDATESUBTEXCMDI *)pMCDExec->pCmd;
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)MCDEngGetPtrFromHandle((MCDHANDLE)pMCDUpdateSubTex->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
|
|
if (!pTexObj ||
|
|
!pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateSubTexture)
|
|
return FALSE;
|
|
|
|
pTexObj->MCDTexture.pMCDTextureData = pMCDUpdateSubTex->pTexData;
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateSubTexture)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
&pTexObj->MCDTexture,
|
|
pMCDUpdateSubTex->lod,
|
|
&pMCDUpdateSubTex->rect);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_UPDATE_TEXTURE_PALETTE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDUPDATETEXPALETTECMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDUPDATETEXPALETTECMDI *pMCDUpdateTexPalette =
|
|
(MCDUPDATETEXPALETTECMDI *)pMCDExec->pCmd;
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)MCDEngGetPtrFromHandle((MCDHANDLE)pMCDUpdateTexPalette->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
|
|
if (!pTexObj ||
|
|
!pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateTexturePalette)
|
|
return FALSE;
|
|
|
|
pTexObj->MCDTexture.pMCDTextureData = pMCDUpdateTexPalette->pTexData;
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateTexturePalette)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
&pTexObj->MCDTexture,
|
|
pMCDUpdateTexPalette->start,
|
|
pMCDUpdateTexPalette->numEntries);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_UPDATE_TEXTURE_PRIORITY:
|
|
CHECK_SIZE_IN(pMCDExec, MCDUPDATETEXPRIORITYCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDUPDATETEXPRIORITYCMDI *pMCDUpdateTexPriority =
|
|
(MCDUPDATETEXPRIORITYCMDI *)pMCDExec->pCmd;
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)MCDEngGetPtrFromHandle((MCDHANDLE)pMCDUpdateTexPriority->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
|
|
if (!pTexObj ||
|
|
!pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateTexturePriority)
|
|
return FALSE;
|
|
|
|
pTexObj->MCDTexture.pMCDTextureData = pMCDUpdateTexPriority->pTexData;
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateTexturePriority)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
&pTexObj->MCDTexture);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_UPDATE_TEXTURE_STATE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDUPDATETEXSTATECMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDUPDATETEXSTATECMDI *pMCDUpdateTexState =
|
|
(MCDUPDATETEXSTATECMDI *)pMCDExec->pCmd;
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)MCDEngGetPtrFromHandle((MCDHANDLE)pMCDUpdateTexState->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
|
|
if (!pTexObj ||
|
|
!pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateTextureState)
|
|
return FALSE;
|
|
|
|
pTexObj->MCDTexture.pMCDTextureData = pMCDUpdateTexState->pTexData;
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvUpdateTextureState)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
&pTexObj->MCDTexture);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_TEXTURE_STATUS:
|
|
CHECK_SIZE_IN(pMCDExec, MCDTEXSTATUSCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDTEXSTATUSCMDI *pMCDTexStatus =
|
|
(MCDTEXSTATUSCMDI *)pMCDExec->pCmd;
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)MCDEngGetPtrFromHandle((MCDHANDLE)pMCDTexStatus->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
|
|
if (!pTexObj ||
|
|
!pMCDExec->pGlobal->mcdDriver.pMCDrvTextureStatus)
|
|
return FALSE;
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvTextureStatus)(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
&pTexObj->MCDTexture);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case MCD_GET_TEXTURE_KEY:
|
|
CHECK_SIZE_IN(pMCDExec, MCDTEXKEYCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDTEXKEYCMDI *pMCDTexKey =
|
|
(MCDTEXKEYCMDI *)pMCDExec->pCmd;
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)MCDEngGetPtrFromHandle((MCDHANDLE)pMCDTexKey->hTex,
|
|
MCDHANDLE_TEXTURE);
|
|
|
|
if (!pTexObj)
|
|
return FALSE;
|
|
|
|
return pTexObj->MCDTexture.textureKey;
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_DESCRIBELAYERPLANE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDLAYERPLANECMDI);
|
|
|
|
if (pMCDExec->pvOut) {
|
|
CHECK_SIZE_OUT(pMCDExec, MCDLAYERPLANE);
|
|
}
|
|
|
|
{
|
|
MCDLAYERPLANECMDI *pMCDLayerPlane =
|
|
(MCDLAYERPLANECMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvDescribeLayerPlane)
|
|
return 0;
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvDescribeLayerPlane)
|
|
(&pMCDExec->MCDSurface,
|
|
pMCDLayerPlane->iPixelFormat,
|
|
pMCDLayerPlane->iLayerPlane,
|
|
pMCDExec->cjOut,
|
|
pMCDExec->pvOut, 0);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_SETLAYERPALETTE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDSETLAYERPALCMDI);
|
|
|
|
{
|
|
MCDSETLAYERPALCMDI *pMCDSetLayerPal =
|
|
(MCDSETLAYERPALCMDI *)pMCDExec->pCmd;
|
|
|
|
// Check to see if palette array is big enough.
|
|
|
|
CHECK_MEM_RANGE_RETVAL(&pMCDSetLayerPal->acr[pMCDSetLayerPal->cEntries-1],
|
|
pMCDExec->pCmd, pMCDExec->pCmdEnd, FALSE);
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvSetLayerPalette) {
|
|
MCDBG_PRINT("MCDrvSetLayerPalette: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(*pMCDExec->pGlobal->mcdDriver.pMCDrvSetLayerPalette)
|
|
(&pMCDExec->MCDSurface,
|
|
pMCDSetLayerPal->iLayerPlane,
|
|
pMCDSetLayerPal->bRealize,
|
|
pMCDSetLayerPal->cEntries,
|
|
&pMCDSetLayerPal->acr[0]);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_DRAW_PIXELS:
|
|
CHECK_SIZE_IN(pMCDExec, MCDDRAWPIXELSCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDDRAWPIXELSCMDI *pMCDPix =
|
|
(MCDDRAWPIXELSCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvDrawPixels) {
|
|
MCDBG_PRINT("MCDrvDrawPixels: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(pMCDExec->pGlobal->mcdDriver.pMCDrvDrawPixels)(
|
|
&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
pMCDPix->width,
|
|
pMCDPix->height,
|
|
pMCDPix->format,
|
|
pMCDPix->type,
|
|
pMCDPix->pPixels,
|
|
pMCDPix->packed);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_READ_PIXELS:
|
|
CHECK_SIZE_IN(pMCDExec, MCDREADPIXELSCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDREADPIXELSCMDI *pMCDPix =
|
|
(MCDREADPIXELSCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvReadPixels) {
|
|
MCDBG_PRINT("MCDrvReadPixels: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(pMCDExec->pGlobal->mcdDriver.pMCDrvReadPixels)(
|
|
&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
pMCDPix->x,
|
|
pMCDPix->y,
|
|
pMCDPix->width,
|
|
pMCDPix->height,
|
|
pMCDPix->format,
|
|
pMCDPix->type,
|
|
pMCDPix->pPixels);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_COPY_PIXELS:
|
|
CHECK_SIZE_IN(pMCDExec, MCDCOPYPIXELSCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDCOPYPIXELSCMDI *pMCDPix =
|
|
(MCDCOPYPIXELSCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvCopyPixels) {
|
|
MCDBG_PRINT("MCDrvCopyPixels: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(pMCDExec->pGlobal->mcdDriver.pMCDrvCopyPixels)(
|
|
&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
pMCDPix->x,
|
|
pMCDPix->y,
|
|
pMCDPix->width,
|
|
pMCDPix->height,
|
|
pMCDPix->type);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_PIXEL_MAP:
|
|
CHECK_SIZE_IN(pMCDExec, MCDPIXELMAPCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
|
|
{
|
|
MCDPIXELMAPCMDI *pMCDPix =
|
|
(MCDPIXELMAPCMDI *)pMCDExec->pCmd;
|
|
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvPixelMap) {
|
|
MCDBG_PRINT("MCDrvPixelMap: missing entry point.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ULONG_PTR)(pMCDExec->pGlobal->mcdDriver.pMCDrvPixelMap)(
|
|
&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc,
|
|
pMCDPix->mapType,
|
|
pMCDPix->mapSize,
|
|
pMCDPix->pMap);
|
|
}
|
|
|
|
break;
|
|
|
|
case MCD_DESTROY_WINDOW:
|
|
CHECK_SIZE_IN(pMCDExec, MCDDESTROYWINDOWCMDI);
|
|
{
|
|
if (pMCDExec->pWndPriv == NULL)
|
|
{
|
|
MCDBG_PRINT("MCDrvDestroyWindow: NULL window\n");
|
|
return FALSE;
|
|
}
|
|
|
|
MCDEngDeleteObject(pMCDExec->pWndPriv->handle);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case MCD_GET_TEXTURE_FORMATS:
|
|
CHECK_SIZE_IN(pMCDExec, MCDGETTEXTUREFORMATSCMDI);
|
|
{
|
|
MCDGETTEXTUREFORMATSCMDI *pmgtf =
|
|
(MCDGETTEXTUREFORMATSCMDI *)pMCDExec->pCmd;
|
|
|
|
if (pMCDExec->pvOut)
|
|
{
|
|
CHECK_SIZE_OUT(pMCDExec,
|
|
pmgtf->nFmts*sizeof(DDSURFACEDESC));
|
|
}
|
|
|
|
#if MCD_VER_MAJOR >= 2 || (MCD_VER_MAJOR == 1 && MCD_VER_MINOR >= 0x10)
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvGetTextureFormats)
|
|
{
|
|
MCDBG_PRINT("MCDrvGetTextureFormats: "
|
|
"missing entry point.");
|
|
return 0;
|
|
}
|
|
|
|
return (pMCDExec->pGlobal->mcdDriver.pMCDrvGetTextureFormats)(
|
|
&pMCDExec->MCDSurface,
|
|
pmgtf->nFmts,
|
|
(DDSURFACEDESC *)pMCDExec->pvOut);
|
|
#else
|
|
return 0;
|
|
#endif // 1.1
|
|
}
|
|
break;
|
|
|
|
case MCD_SWAP_MULTIPLE:
|
|
CHECK_SIZE_IN(pMCDExec, MCDSWAPMULTIPLECMDI);
|
|
|
|
{
|
|
MCDSWAPMULTIPLECMDI *pSwapCmd =
|
|
(MCDSWAPMULTIPLECMDI *)pMCDExec->pCmd;
|
|
MCDWINDOWPRIV *apWndPriv[MCDESC_MAX_EXTRA_WNDOBJ];
|
|
UINT i;
|
|
MCDWINDOWOBJ *pmwo;
|
|
MCDRVSWAPMULTIPLEFUNC pSwapMultFunc;
|
|
ULONG_PTR dwRet;
|
|
|
|
pSwapMultFunc = NULL;
|
|
for (i = 0; i < pSwapCmd->cBuffers; i++)
|
|
{
|
|
if (pMCDExec->ppwoMulti[i] != NULL)
|
|
{
|
|
pmwo = (MCDWINDOWOBJ *)
|
|
MCDEngGetPtrFromHandle((MCDHANDLE)
|
|
pSwapCmd->adwMcdWindow[i],
|
|
MCDHANDLE_WINDOW);
|
|
}
|
|
else
|
|
{
|
|
pmwo = NULL;
|
|
}
|
|
|
|
if (pmwo == NULL)
|
|
{
|
|
apWndPriv[i] = NULL;
|
|
}
|
|
else
|
|
{
|
|
apWndPriv[i] = &pmwo->MCDWindowPriv;
|
|
GetScissorClip(apWndPriv[i], NULL);
|
|
|
|
#if MCD_VER_MAJOR >= 2 || (MCD_VER_MAJOR == 1 && MCD_VER_MINOR >= 0x10)
|
|
if (pSwapMultFunc == NULL)
|
|
{
|
|
pSwapMultFunc = apWndPriv[i]->pGlobal->mcdDriver.
|
|
pMCDrvSwapMultiple;
|
|
}
|
|
else if (pSwapMultFunc !=
|
|
apWndPriv[i]->pGlobal->mcdDriver.
|
|
pMCDrvSwapMultiple)
|
|
{
|
|
MCDBG_PRINT("MCDrvSwapMultiple: "
|
|
"Mismatched SwapMultiple");
|
|
return FALSE;
|
|
}
|
|
#endif // 1.1
|
|
}
|
|
}
|
|
|
|
if (pSwapMultFunc != NULL)
|
|
{
|
|
dwRet = pSwapMultFunc(pMCDExec->MCDSurface.pwo->psoOwner,
|
|
pSwapCmd->cBuffers,
|
|
(MCDWINDOW **)apWndPriv,
|
|
(UINT *)pSwapCmd->auiFlags);
|
|
}
|
|
else
|
|
{
|
|
MCDSURFACE *pms;
|
|
|
|
dwRet = 0;
|
|
pms = &pMCDExec->MCDSurface;
|
|
for (i = 0; i < pSwapCmd->cBuffers; i++)
|
|
{
|
|
if (apWndPriv[i] == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (apWndPriv[i]->pGlobal->mcdDriver.
|
|
pMCDrvSwap == NULL)
|
|
{
|
|
MCDBG_PRINT("MCDrvSwapMultiple: Missing Swap");
|
|
}
|
|
else
|
|
{
|
|
pms->pWnd = (MCDWINDOW *)apWndPriv[i];
|
|
pms->pso = pMCDExec->ppwoMulti[i]->psoOwner;
|
|
pms->pwo = pMCDExec->ppwoMulti[i];
|
|
pms->surfaceFlags = 0;
|
|
|
|
if (apWndPriv[i]->pGlobal->mcdDriver.
|
|
pMCDrvSwap(pms, pSwapCmd->auiFlags[i]))
|
|
{
|
|
dwRet |= 1 << i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
break;
|
|
|
|
case MCD_PROCESS:
|
|
CHECK_SIZE_IN(pMCDExec, MCDPROCESSCMDI);
|
|
CHECK_FOR_RC(pMCDExec);
|
|
CHECK_FOR_MEM(pMCDExec);
|
|
{
|
|
MCDPROCESSCMDI *pmp = (MCDPROCESSCMDI *)pMCDExec->pCmd;
|
|
|
|
// Validate command buffer
|
|
GET_MEMOBJ_RETVAL(pMemObj, pmp->hMCDPrimMem,
|
|
(ULONG_PTR)pmp->pMCDFirstCmd);
|
|
|
|
pMinMem = pMemObj->MCDMem.pMemBase;
|
|
|
|
// Note: we ignore the memory size in the header since it
|
|
// doesn't really help us...
|
|
|
|
pMaxMem = pMinMem + pMemObj->MCDMem.memSize;
|
|
|
|
CHECK_MEM_RANGE_RETVAL(pmp->pMCDFirstCmd, pMinMem,
|
|
pMaxMem, (ULONG_PTR)pmp->pMCDFirstCmd);
|
|
|
|
// Validate user-mode pointers passed down.
|
|
__try
|
|
{
|
|
EngProbeForRead(pmp->pMCDTransform, sizeof(MCDTRANSFORM),
|
|
sizeof(DWORD));
|
|
// No meaningful check of the material changes can be
|
|
// done. The driver is responsible for probing
|
|
// addresses used.
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return (ULONG_PTR)pmp->pMCDFirstCmd;
|
|
}
|
|
|
|
GetScissorClip(pMCDExec->pWndPriv, pMCDExec->pRcPriv);
|
|
|
|
#if MCD_VER_MAJOR >= 2
|
|
if (!pMCDExec->pGlobal->mcdDriver.pMCDrvProcess)
|
|
{
|
|
if (pMCDExec->pGlobal->mcdDriver.pMCDrvSync)
|
|
{
|
|
(*pMCDExec->pGlobal->mcdDriver.pMCDrvSync)
|
|
(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc);
|
|
}
|
|
return (ULONG_PTR)pmp->pMCDFirstCmd;
|
|
}
|
|
|
|
return (pMCDExec->pGlobal->mcdDriver.pMCDrvProcess)(
|
|
&pMCDExec->MCDSurface, &pMCDExec->pRcPriv->MCDRc,
|
|
&pMemObj->MCDMem, (UCHAR *)pmp->pMCDFirstCmd, pMaxMem,
|
|
pmp->cmdFlagsAll, pmp->primFlags, pmp->pMCDTransform,
|
|
pmp->pMCDMatChanges);
|
|
#else
|
|
if (pMCDExec->pGlobal->mcdDriver.pMCDrvSync)
|
|
{
|
|
(*pMCDExec->pGlobal->mcdDriver.pMCDrvSync)
|
|
(&pMCDExec->MCDSurface,
|
|
&pMCDExec->pRcPriv->MCDRc);
|
|
}
|
|
return (ULONG_PTR)pmp->pMCDFirstCmd;
|
|
#endif // 2.0
|
|
}
|
|
break;
|
|
|
|
default:
|
|
MCDBG_PRINT("MCDSrvProcess: "
|
|
"Null rendering context invalid for command %d.",
|
|
pMCDExec->pCmd->command);
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE; // should never get here...
|
|
}
|
|
|
|
|
|
|
|
//****************************************************************************
|
|
// FreeRCObj()
|
|
//
|
|
// Engine callback for freeing the memory used for rendering-context
|
|
// handles.
|
|
//****************************************************************************
|
|
|
|
BOOL CALLBACK FreeRCObj(DRIVEROBJ *pDrvObj)
|
|
{
|
|
MCDRCOBJ *pRcObj = (MCDRCOBJ *)pDrvObj->pvObj;
|
|
MCDRCPRIV *pRcPriv = pRcObj->pRcPriv;
|
|
|
|
if ((pRcPriv->bDrvValid) &&
|
|
(pRcPriv->pGlobal->mcdDriver.pMCDrvDeleteContext))
|
|
{
|
|
(*pRcPriv->pGlobal->mcdDriver.pMCDrvDeleteContext)
|
|
(&pRcPriv->MCDRc, pDrvObj->dhpdev);
|
|
}
|
|
|
|
MCDSrvLocalFree((UCHAR *)pRcPriv);
|
|
MCDSrvLocalFree((UCHAR *)pRcObj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// MCDSrvCreateContext()
|
|
//
|
|
// Create a rendering context (RGBA or color-indexed) for the current
|
|
// hardware mode. This call will also initialize window-tracking for
|
|
// the context (which is associated with the specified window).
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
MCDHANDLE MCDSrvCreateContext(MCDSURFACE *pMCDSurface,
|
|
MCDRCINFOPRIV *pMcdRcInfo,
|
|
MCDGLOBALINFO *pGlobal,
|
|
LONG iPixelFormat,
|
|
LONG iLayer,
|
|
HWND hWnd,
|
|
ULONG surfaceFlags,
|
|
ULONG contextFlags)
|
|
{
|
|
MCDWINDOW *pWnd;
|
|
MCDWINDOWPRIV *pWndPriv;
|
|
MCDRCPRIV *pRcPriv;
|
|
MCDHANDLE retVal;
|
|
HWND hwnd;
|
|
MCDRCOBJ *newRcObject;
|
|
MCDRVTRACKWINDOWFUNC pTrackFunc = NULL;
|
|
|
|
if (pGlobal->mcdDriver.pMCDrvCreateContext == NULL)
|
|
{
|
|
MCDBG_PRINT("MCDSrvCreateContext: No MCDrvCreateContext.");
|
|
return NULL;
|
|
}
|
|
|
|
pRcPriv = (MCDRCPRIV *)MCDSrvLocalAlloc(0,sizeof(MCDRCPRIV));
|
|
|
|
if (!pRcPriv) {
|
|
MCDBG_PRINT("MCDSrvCreateContext: Could not allocate new context.");
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
pRcPriv->pGlobal = pGlobal;
|
|
|
|
// Cache the engine handle provided by the driver:
|
|
|
|
pRcPriv->hDev = (*pGlobal->mcdDriver.pMCDrvGetHdev)(pMCDSurface);
|
|
|
|
if (surfaceFlags & MCDSURFACE_HWND)
|
|
{
|
|
pMCDSurface->surfaceFlags |= MCDSURFACE_HWND;
|
|
}
|
|
if (surfaceFlags & MCDSURFACE_DIRECT)
|
|
{
|
|
pMCDSurface->surfaceFlags |= MCDSURFACE_DIRECT;
|
|
}
|
|
|
|
// cache the surface flags away in the private RC:
|
|
|
|
pRcPriv->surfaceFlags = pMCDSurface->surfaceFlags;
|
|
|
|
// Initialize tracking of this window with a MCDWINDOW
|
|
// (via and WNDOBJ on NT) if we are not already tracking the
|
|
// window:
|
|
|
|
pWnd = MCDSrvNewMCDWindow(pMCDSurface, hWnd, pGlobal,
|
|
pRcPriv->hDev);
|
|
if (pWnd == NULL)
|
|
{
|
|
MCDSrvLocalFree((HLOCAL)pRcPriv);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
pWndPriv = (MCDWINDOWPRIV *)pWnd;
|
|
|
|
pRcPriv->hWnd = hWnd;
|
|
|
|
newRcObject = (MCDRCOBJ *)MCDSrvLocalAlloc(0,sizeof(MCDRCOBJ));
|
|
if (!newRcObject) {
|
|
MCDSrvLocalFree((HLOCAL)pRcPriv);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
retVal = MCDEngCreateObject(newRcObject, FreeRCObj, pRcPriv->hDev);
|
|
|
|
if (retVal) {
|
|
newRcObject->pid = MCDEngGetProcessID();
|
|
newRcObject->type = MCDHANDLE_RC;
|
|
newRcObject->size = sizeof(MCDRCPRIV);
|
|
newRcObject->pRcPriv = pRcPriv;
|
|
newRcObject->handle = (MCDHANDLE)retVal;
|
|
|
|
// Add the object to the list in the MCDWINDOW
|
|
|
|
newRcObject->next = pWndPriv->objectList;
|
|
pWndPriv->objectList = newRcObject;
|
|
} else {
|
|
MCDBG_PRINT("MCDSrvCreateContext: Could not create new handle.");
|
|
|
|
MCDSrvLocalFree((HLOCAL)pRcPriv);
|
|
MCDSrvLocalFree((HLOCAL)newRcObject);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
pRcPriv->bValid = TRUE;
|
|
pRcPriv->scissorsEnabled = FALSE;
|
|
pRcPriv->scissorsRect.left = 0;
|
|
pRcPriv->scissorsRect.top = 0;
|
|
pRcPriv->scissorsRect.right = 0;
|
|
pRcPriv->scissorsRect.bottom = 0;
|
|
pRcPriv->MCDRc.createFlags = contextFlags;
|
|
pRcPriv->MCDRc.iPixelFormat = iPixelFormat;
|
|
pRcPriv->MCDRc.iLayerPlane = iLayer;
|
|
|
|
if (!(*pGlobal->mcdDriver.pMCDrvCreateContext)(pMCDSurface,
|
|
&pRcPriv->MCDRc,
|
|
&pMcdRcInfo->mri)) {
|
|
DestroyMCDObj((HANDLE)retVal, MCDHANDLE_RC);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
// Return window private handle
|
|
pMcdRcInfo->dwMcdWindow = (ULONG_PTR)pWndPriv->handle;
|
|
|
|
// Now valid to call driver for deletion...
|
|
|
|
pRcPriv->bDrvValid = TRUE;
|
|
|
|
return (MCDHANDLE)retVal;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// FreeTexObj()
|
|
//
|
|
// Engine callback for freeing the memory used for a texture.
|
|
//****************************************************************************
|
|
|
|
BOOL CALLBACK FreeTexObj(DRIVEROBJ *pDrvObj)
|
|
{
|
|
MCDTEXOBJ *pTexObj = (MCDTEXOBJ *)pDrvObj->pvObj;
|
|
|
|
// We should never get called if the driver is missing this entry point,
|
|
// but the extra check can't hurt!
|
|
//
|
|
// pGlobal can be NULL for partially constructed objects. It
|
|
// is only NULL prior to calling the driver for creation, so if
|
|
// it's NULL there's no reason to call the driver for cleanup.
|
|
|
|
if (pTexObj->pGlobal != NULL &&
|
|
pTexObj->pGlobal->mcdDriver.pMCDrvDeleteTexture != NULL)
|
|
{
|
|
(*pTexObj->pGlobal->mcdDriver.pMCDrvDeleteTexture)
|
|
(&pTexObj->MCDTexture, pDrvObj->dhpdev);
|
|
}
|
|
|
|
MCDSrvLocalFree((HLOCAL)pTexObj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// MCDSrvCreateTexture()
|
|
//
|
|
// Creates an MCD texture.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
MCDHANDLE MCDSrvCreateTexture(MCDEXEC *pMCDExec, MCDTEXTUREDATA *pTexData,
|
|
VOID *pSurface, ULONG flags)
|
|
{
|
|
MCDRCPRIV *pRcPriv;
|
|
MCDHANDLE hTex;
|
|
MCDTEXOBJ *pTexObj;
|
|
|
|
pRcPriv = pMCDExec->pRcPriv;
|
|
|
|
if ((!pMCDExec->pGlobal->mcdDriver.pMCDrvDeleteTexture) ||
|
|
(!pMCDExec->pGlobal->mcdDriver.pMCDrvCreateTexture)) {
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
pTexObj = (MCDTEXOBJ *) MCDSrvLocalAlloc(0,sizeof(MCDTEXOBJ));
|
|
if (!pTexObj) {
|
|
MCDBG_PRINT("MCDCreateTexture: Could not allocate texture object.");
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
hTex = MCDEngCreateObject(pTexObj, FreeTexObj, pRcPriv->hDev);
|
|
|
|
if (!hTex) {
|
|
MCDBG_PRINT("MCDSrvCreateTexture: Could not create texture object.");
|
|
MCDSrvLocalFree((HLOCAL)pTexObj);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
// Initialize driver public information for driver call, but not
|
|
// the private information. The private information is not filled out
|
|
// until after the driver call succeeds so that FreeTexObj knows
|
|
// whether to call the driver or not when destroying a texture object.
|
|
pTexObj->MCDTexture.pSurface = pSurface;
|
|
pTexObj->MCDTexture.pMCDTextureData = pTexData;
|
|
pTexObj->MCDTexture.createFlags = flags;
|
|
|
|
// Call the driver if everything has gone well...
|
|
|
|
if (!(*pMCDExec->pGlobal->mcdDriver.pMCDrvCreateTexture)
|
|
(&pMCDExec->MCDSurface,
|
|
&pRcPriv->MCDRc,
|
|
&pTexObj->MCDTexture)) {
|
|
//MCDBG_PRINT("MCDSrvCreateTexture: Driver could not create texture.");
|
|
MCDEngDeleteObject(hTex);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
if (!pTexObj->MCDTexture.textureKey) {
|
|
MCDBG_PRINT("MCDSrvCreateTexture: Driver returned null key.");
|
|
MCDEngDeleteObject(hTex);
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
pTexObj->pid = MCDEngGetProcessID();
|
|
pTexObj->type = MCDHANDLE_TEXTURE;
|
|
pTexObj->size = sizeof(MCDTEXOBJ);
|
|
pTexObj->pGlobal = pMCDExec->pGlobal;
|
|
|
|
return (MCDHANDLE)hTex;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// FreeMemObj()
|
|
//
|
|
// Engine callback for freeing memory used by shared-memory handles.
|
|
//****************************************************************************
|
|
|
|
BOOL CALLBACK FreeMemObj(DRIVEROBJ *pDrvObj)
|
|
{
|
|
MCDMEMOBJ *pMemObj = (MCDMEMOBJ *)pDrvObj->pvObj;
|
|
|
|
// Free the memory using our engine ONLY if it is the same memory
|
|
// we allocated in the first place.
|
|
|
|
if (pMemObj->pMemBaseInternal)
|
|
MCDEngFreeSharedMem(pMemObj->pMemBaseInternal);
|
|
|
|
// pGlobal can be NULL for partially constructed objects. It
|
|
// is only NULL prior to calling the driver for creation, so if
|
|
// it's NULL there's no reason to call the driver for cleanup.
|
|
if (pMemObj->pGlobal != NULL &&
|
|
pMemObj->pGlobal->mcdDriver.pMCDrvDeleteMem != NULL)
|
|
{
|
|
(*pMemObj->pGlobal->mcdDriver.pMCDrvDeleteMem)
|
|
(&pMemObj->MCDMem, pDrvObj->dhpdev);
|
|
}
|
|
|
|
MCDSrvLocalFree((HLOCAL)pMemObj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// MCDSrvAllocMem()
|
|
//
|
|
// Creates a handle associated with the specified memory.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
UCHAR * MCDSrvAllocMem(MCDEXEC *pMCDExec, ULONG numBytes,
|
|
ULONG flags, MCDHANDLE *phMem)
|
|
{
|
|
MCDRCPRIV *pRcPriv;
|
|
MCDHANDLE hMem;
|
|
MCDMEMOBJ *pMemObj;
|
|
|
|
pRcPriv = pMCDExec->pRcPriv;
|
|
|
|
*phMem = (MCDHANDLE)FALSE;
|
|
|
|
pMemObj = (MCDMEMOBJ *) MCDSrvLocalAlloc(0,sizeof(MCDMEMOBJ));
|
|
|
|
if (!pMemObj) {
|
|
MCDBG_PRINT("MCDSrvAllocMem: Could not allocate memory object.");
|
|
return (MCDHANDLE)NULL;
|
|
}
|
|
|
|
hMem = MCDEngCreateObject(pMemObj, FreeMemObj, pRcPriv->hDev);
|
|
|
|
if (!hMem) {
|
|
MCDBG_PRINT("MCDSrvAllocMem: Could not create memory object.");
|
|
MCDSrvLocalFree((HLOCAL)pMemObj);
|
|
return (UCHAR *)NULL;
|
|
}
|
|
|
|
pMemObj->MCDMem.pMemBase = pMemObj->pMemBaseInternal =
|
|
MCDEngAllocSharedMem(numBytes);
|
|
|
|
if (!pMemObj->MCDMem.pMemBase) {
|
|
MCDBG_PRINT("MCDSrvAllocMem: Could not allocate memory.");
|
|
MCDEngDeleteObject(hMem);
|
|
return (UCHAR *)NULL;
|
|
}
|
|
|
|
// Call the driver if everything has gone well, and the driver
|
|
// entry points exist...
|
|
|
|
if ((pMCDExec->pGlobal->mcdDriver.pMCDrvCreateMem) &&
|
|
(pMCDExec->pGlobal->mcdDriver.pMCDrvDeleteMem)) {
|
|
|
|
if (!(*pMCDExec->pGlobal->mcdDriver.pMCDrvCreateMem)
|
|
(&pMCDExec->MCDSurface,
|
|
&pMemObj->MCDMem)) {
|
|
MCDBG_PRINT("MCDSrvAllocMem: "
|
|
"Driver not create memory type %x.", flags);
|
|
MCDEngDeleteObject(hMem);
|
|
return (UCHAR *)NULL;
|
|
}
|
|
}
|
|
|
|
// Free the memory allocated with our engine if the driver has substituted
|
|
// its own allocation...
|
|
|
|
if (pMemObj->MCDMem.pMemBase != pMemObj->pMemBaseInternal) {
|
|
MCDEngFreeSharedMem(pMemObj->pMemBaseInternal);
|
|
pMemObj->pMemBaseInternal = (UCHAR *)NULL;
|
|
}
|
|
|
|
// Set up the private portion of memory object:
|
|
|
|
pMemObj->pid = MCDEngGetProcessID();
|
|
pMemObj->type = MCDHANDLE_MEM;
|
|
pMemObj->size = sizeof(MCDMEMOBJ);
|
|
pMemObj->pGlobal = pMCDExec->pGlobal;
|
|
pMemObj->MCDMem.memSize = numBytes;
|
|
pMemObj->MCDMem.createFlags = flags;
|
|
|
|
*phMem = hMem;
|
|
|
|
return pMemObj->MCDMem.pMemBase;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
ULONG MCDSrvQueryMemStatus(MCDEXEC *pMCDExec, MCDHANDLE hMCDMem)
|
|
{
|
|
MCDMEMOBJ *pMemObj;
|
|
|
|
pMemObj = (MCDMEMOBJ *)MCDEngGetPtrFromHandle(hMCDMem, MCDHANDLE_MEM);
|
|
|
|
if (!pMemObj)
|
|
return MCD_MEM_INVALID;
|
|
|
|
if (pMemObj->lockCount)
|
|
return MCD_MEM_BUSY;
|
|
else
|
|
return MCD_MEM_READY;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
BOOL MCDSrvSetScissor(MCDEXEC *pMCDExec, RECTL *pRect, BOOL bEnabled)
|
|
{
|
|
MCDRCPRIV *pRcPriv;
|
|
MCDRCOBJ *pRcObj;
|
|
HWND hWnd;
|
|
ULONG retVal = FALSE;
|
|
|
|
pRcPriv = pMCDExec->pRcPriv;
|
|
|
|
pRcPriv->scissorsEnabled = bEnabled;
|
|
pRcPriv->scissorsRect = *pRect;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// DestroyMCDObj()
|
|
//
|
|
// Deletes the specified object. This can be memory, textures, or rendering
|
|
// contexts.
|
|
//
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
BOOL DestroyMCDObj(MCDHANDLE handle, MCDHANDLETYPE handleType)
|
|
{
|
|
CHAR *pObject;
|
|
|
|
pObject = (CHAR *)MCDEngGetPtrFromHandle(handle, handleType);
|
|
|
|
if (!pObject)
|
|
return FALSE;
|
|
|
|
//!!! Check for PID here...
|
|
|
|
return (MCDEngDeleteObject(handle) != 0);
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// DecoupleMCDWindowObj()
|
|
//
|
|
// Breaks any existing links between an MCDWINDOW and its WNDOBJ
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
VOID DecoupleMCDWindow(MCDWINDOWPRIV *pWndPriv)
|
|
{
|
|
// Clean up any outstanding lock
|
|
MCDSrvUnlock(pWndPriv);
|
|
|
|
// Delete reference in WNDOBJ. WNDOBJ itself will be cleaned
|
|
// up through normal window cleanup.
|
|
if (pWndPriv->pwo != NULL)
|
|
{
|
|
if (pWndPriv->pGlobal->mcdDriver.pMCDrvTrackWindow)
|
|
{
|
|
(*pWndPriv->pGlobal->mcdDriver.pMCDrvTrackWindow)
|
|
(pWndPriv->pwo, (MCDWINDOW *)pWndPriv, WOC_DELETE);
|
|
}
|
|
|
|
WNDOBJ_vSetConsumer(pWndPriv->pwo, NULL);
|
|
|
|
pWndPriv->pwo = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// DestroyMCDWindowObj()
|
|
//
|
|
// Destroy the specified MCDWINDOW and any associated handles (such rendering
|
|
// contexts).
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
VOID DestroyMCDWindowObj(MCDWINDOWOBJ *pmwo)
|
|
{
|
|
MCDWINDOWPRIV *pWndPriv = &pmwo->MCDWindowPriv;
|
|
MCDRCOBJ *nextObject;
|
|
|
|
DecoupleMCDWindow(pWndPriv);
|
|
|
|
// Delete all of the rendering contexts associated with the window:
|
|
|
|
#if _WIN95_
|
|
while (pWndPriv->objectList)
|
|
{
|
|
nextObject = pWndPriv->objectList->next;
|
|
MCDEngDeleteObject(pWndPriv->objectList->handle);
|
|
pWndPriv->objectList = nextObject;
|
|
}
|
|
#endif
|
|
|
|
if (pWndPriv->pAllocatedClipBuffer)
|
|
MCDSrvLocalFree(pWndPriv->pAllocatedClipBuffer);
|
|
|
|
// Free the memory
|
|
|
|
MCDSrvLocalFree((HLOCAL)pmwo);
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// GetScissorClip()
|
|
//
|
|
// Generate a new clip list based on the current list of clip rectanges
|
|
// for the window, and the specified scissor rectangle.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
VOID GetScissorClip(MCDWINDOWPRIV *pWndPriv, MCDRCPRIV *pRcPriv)
|
|
{
|
|
MCDWINDOW *pWnd;
|
|
MCDENUMRECTS *pClipUnscissored;
|
|
MCDENUMRECTS *pClipScissored;
|
|
RECTL *pRectUnscissored;
|
|
RECTL *pRectScissored;
|
|
RECTL rectScissor;
|
|
ULONG numUnscissoredRects;
|
|
ULONG numScissoredRects;
|
|
|
|
pWnd = (MCDWINDOW *)pWndPriv;
|
|
|
|
if (!pRcPriv || !pRcPriv->scissorsEnabled)
|
|
{
|
|
// Scissors aren't enabled, so the unscissored and scissored
|
|
// clip lists are identical:
|
|
|
|
pWnd->pClip = pWnd->pClipUnscissored = pWndPriv->pClipUnscissored;
|
|
}
|
|
else
|
|
{
|
|
// The scissored list will go in the second half of our clip
|
|
// buffer:
|
|
|
|
pClipUnscissored
|
|
= pWndPriv->pClipUnscissored;
|
|
|
|
pClipScissored
|
|
= (MCDENUMRECTS*) ((BYTE*) pClipUnscissored + pWndPriv->sizeClipBuffer / 2);
|
|
|
|
pWnd->pClip = pWndPriv->pClipScissored = pClipScissored;
|
|
pWnd->pClipUnscissored = pClipUnscissored;
|
|
|
|
// Convert scissor to screen coordinates:
|
|
|
|
rectScissor.left = pRcPriv->scissorsRect.left + pWndPriv->MCDWindow.clientRect.left;
|
|
rectScissor.right = pRcPriv->scissorsRect.right + pWndPriv->MCDWindow.clientRect.left;
|
|
rectScissor.top = pRcPriv->scissorsRect.top + pWndPriv->MCDWindow.clientRect.top;
|
|
rectScissor.bottom = pRcPriv->scissorsRect.bottom + pWndPriv->MCDWindow.clientRect.top;
|
|
|
|
pRectUnscissored = &pClipUnscissored->arcl[0];
|
|
pRectScissored = &pClipScissored->arcl[0];
|
|
numScissoredRects = 0;
|
|
|
|
for (numUnscissoredRects = pClipUnscissored->c;
|
|
numUnscissoredRects != 0;
|
|
numUnscissoredRects--, pRectUnscissored++)
|
|
{
|
|
// Since our clipping rectangles are ordered from top to
|
|
// bottom, we can early-out if the tops of the remaining
|
|
// rectangles are not in the scissor rectangle
|
|
|
|
if (rectScissor.bottom <= pRectUnscissored->top)
|
|
break;
|
|
|
|
// Continue without updating new clip list is there is
|
|
// no overlap.
|
|
|
|
if ((rectScissor.left >= pRectUnscissored->right) ||
|
|
(rectScissor.top >= pRectUnscissored->bottom) ||
|
|
(rectScissor.right <= pRectUnscissored->left))
|
|
continue;
|
|
|
|
// If we reach this point, we must intersect the given rectangle
|
|
// with the scissor.
|
|
|
|
MCDIntersectRect(pRectScissored, pRectUnscissored, &rectScissor);
|
|
|
|
numScissoredRects++;
|
|
pRectScissored++;
|
|
}
|
|
|
|
pClipScissored->c = numScissoredRects;
|
|
}
|
|
}
|
|
|
|
//****************************************************************************
|
|
// GetClipLists()
|
|
//
|
|
// Updates the clip list for the specified window. Space is also allocated
|
|
// the scissored clip list.
|
|
//
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
VOID GetClipLists(WNDOBJ *pwo, MCDWINDOWPRIV *pWndPriv)
|
|
{
|
|
MCDENUMRECTS *pDefault;
|
|
ULONG numClipRects;
|
|
char *pClipBuffer;
|
|
ULONG sizeClipBuffer;
|
|
|
|
pDefault = (MCDENUMRECTS*) &pWndPriv->defaultClipBuffer[0];
|
|
|
|
#if 1
|
|
if (pwo->coClient.iDComplexity == DC_TRIVIAL)
|
|
{
|
|
if ((pwo->rclClient.left >= pwo->rclClient.right) ||
|
|
(pwo->rclClient.top >= pwo->rclClient.bottom))
|
|
{
|
|
pDefault->c = 0;
|
|
}
|
|
else
|
|
{
|
|
pDefault->c = 1;
|
|
pDefault->arcl[0] = pwo->rclClient;
|
|
}
|
|
}
|
|
else if (pwo->coClient.iDComplexity == DC_RECT)
|
|
#else
|
|
if (pwo->coClient.iDComplexity == DC_RECT)
|
|
#endif
|
|
{
|
|
if (pWndPriv->pAllocatedClipBuffer)
|
|
MCDSrvLocalFree(pWndPriv->pAllocatedClipBuffer);
|
|
pWndPriv->pAllocatedClipBuffer = NULL;
|
|
pWndPriv->pClipUnscissored = pDefault;
|
|
pWndPriv->pClipScissored = pDefault;
|
|
pWndPriv->sizeClipBuffer = SIZE_DEFAULT_CLIP_BUFFER;
|
|
|
|
if ((pwo->coClient.rclBounds.left >= pwo->coClient.rclBounds.right) ||
|
|
(pwo->coClient.rclBounds.top >= pwo->coClient.rclBounds.bottom))
|
|
{
|
|
// Full-screen VGA mode is represented by a DC_RECT clip object
|
|
// with an empty bounding rectangle. We'll denote it by
|
|
// setting the rectangle count to zero:
|
|
|
|
pDefault->c = 0;
|
|
}
|
|
else
|
|
{
|
|
pDefault->c = 1;
|
|
pDefault->arcl[0] = pwo->coClient.rclBounds;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WNDOBJ_cEnumStart(pwo, CT_RECTANGLES, CD_RIGHTDOWN, 0);
|
|
|
|
// Note that this is divide-by-2 for the buffer size because we
|
|
// need room for two copies of the rectangle list:
|
|
|
|
if (WNDOBJ_bEnum(pwo, SIZE_DEFAULT_CLIP_BUFFER / 2, (ULONG*) pDefault))
|
|
{
|
|
// Okay, the list of rectangles won't fit in our default buffer.
|
|
// Unfortunately, there is no way to obtain the total count of clip
|
|
// rectangles other than by enumerating them all, as cEnumStart
|
|
// will occasionally give numbers that are far too large (because
|
|
// GDI itself doesn't feel like counting them all).
|
|
//
|
|
// Note that we can use the full default buffer here for this
|
|
// enumeration loop:
|
|
|
|
numClipRects = pDefault->c;
|
|
while (WNDOBJ_bEnum(pwo, SIZE_DEFAULT_CLIP_BUFFER, (ULONG*) pDefault))
|
|
numClipRects += pDefault->c;
|
|
|
|
// Don't forget that we are given a valid output buffer even
|
|
// when 'bEnum' returns FALSE:
|
|
|
|
numClipRects += pDefault->c;
|
|
|
|
pClipBuffer = pWndPriv->pAllocatedClipBuffer;
|
|
sizeClipBuffer = 2 * (numClipRects * sizeof(RECTL) + sizeof(ULONG));
|
|
|
|
if ((pClipBuffer == NULL) || (sizeClipBuffer > pWndPriv->sizeClipBuffer))
|
|
{
|
|
// Our allocated buffer is too small; we have to free it and
|
|
// allocate a new one. Take the opportunity to add some
|
|
// growing room to our allocation:
|
|
|
|
sizeClipBuffer += 8 * sizeof(RECTL); // Arbitrary growing room
|
|
|
|
if (pClipBuffer)
|
|
MCDSrvLocalFree(pClipBuffer);
|
|
|
|
pClipBuffer = (char *) MCDSrvLocalAlloc(LMEM_FIXED, sizeClipBuffer);
|
|
|
|
if (pClipBuffer == NULL)
|
|
{
|
|
// Oh no: we couldn't allocate enough room for the clip list.
|
|
// So pretend we have no visible area at all:
|
|
|
|
pWndPriv->pAllocatedClipBuffer = NULL;
|
|
pWndPriv->pClipUnscissored = pDefault;
|
|
pWndPriv->pClipScissored = pDefault;
|
|
pWndPriv->sizeClipBuffer = SIZE_DEFAULT_CLIP_BUFFER;
|
|
pDefault->c = 0;
|
|
return;
|
|
}
|
|
|
|
pWndPriv->pAllocatedClipBuffer = pClipBuffer;
|
|
pWndPriv->pClipUnscissored = (MCDENUMRECTS*) pClipBuffer;
|
|
pWndPriv->pClipScissored = (MCDENUMRECTS*) pClipBuffer;
|
|
pWndPriv->sizeClipBuffer = sizeClipBuffer;
|
|
}
|
|
|
|
// Now actually get all the clip rectangles:
|
|
|
|
WNDOBJ_cEnumStart(pwo, CT_RECTANGLES, CD_RIGHTDOWN, 0);
|
|
WNDOBJ_bEnum(pwo, sizeClipBuffer, (ULONG*) pClipBuffer);
|
|
}
|
|
else
|
|
{
|
|
// How nice, there are no more clip rectangles, which meant that
|
|
// the entire list fits in our default clip buffer, with room
|
|
// for the scissored version of the list:
|
|
|
|
if (pWndPriv->pAllocatedClipBuffer)
|
|
MCDSrvLocalFree(pWndPriv->pAllocatedClipBuffer);
|
|
pWndPriv->pAllocatedClipBuffer = NULL;
|
|
pWndPriv->pClipUnscissored = pDefault;
|
|
pWndPriv->pClipScissored = pDefault;
|
|
pWndPriv->sizeClipBuffer = SIZE_DEFAULT_CLIP_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// WndObjChangeProc()
|
|
//
|
|
// This is the callback function for window-change notification. We update
|
|
// our clip list, and also allow the hardware to respond to the client
|
|
// and surface deltas, as well as the client message itself.
|
|
//****************************************************************************
|
|
|
|
VOID CALLBACK WndObjChangeProc(WNDOBJ *pwo, FLONG fl)
|
|
{
|
|
MCDGLOBALINFO *pGlobal;
|
|
|
|
if (pwo)
|
|
{
|
|
MCDWINDOWPRIV *pWndPriv = (MCDWINDOWPRIV *)pwo->pvConsumer;
|
|
|
|
//MCDBG_PRINT("WndObjChangeProc: %s, pWndPriv = 0x%08lx\n",
|
|
// fl == WOC_RGN_CLIENT ? "WOC_RGN_CLIENT " :
|
|
// fl == WOC_RGN_CLIENT_DELTA ? "WOC_RGN_CLIENT_DELTA " :
|
|
// fl == WOC_RGN_SURFACE ? "WOC_RGN_SURFACE " :
|
|
// fl == WOC_RGN_SURFACE_DELTA ? "WOC_RGN_SURFACE_DELTA" :
|
|
// fl == WOC_DELETE ? "WOC_DELETE " :
|
|
// "unknown",
|
|
// pWndPriv);
|
|
|
|
//!!!HACK -- surface region tracking doesn't have an MCDWINDOWPRIV (yet...)
|
|
|
|
// Client region tracking and deletion requires a valid MCDWINDOWPRIV.
|
|
|
|
if (((fl == WOC_RGN_CLIENT) || (fl == WOC_RGN_CLIENT_DELTA) ||
|
|
(fl == WOC_DELETE)))
|
|
{
|
|
if (!pWndPriv)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Invalidate cache because buffers may have moved
|
|
pWndPriv->bBuffersValid = FALSE;
|
|
}
|
|
|
|
switch (fl)
|
|
{
|
|
case WOC_RGN_CLIENT: // Capture the clip list
|
|
|
|
GetClipLists(pwo, pWndPriv);
|
|
|
|
pWndPriv->MCDWindow.clientRect = pwo->rclClient;
|
|
pWndPriv->MCDWindow.clipBoundsRect = pwo->coClient.rclBounds;
|
|
pWndPriv->bRegionValid = TRUE;
|
|
if (pWndPriv->pGlobal->mcdDriver.pMCDrvTrackWindow != NULL)
|
|
{
|
|
(*pWndPriv->pGlobal->mcdDriver.pMCDrvTrackWindow)
|
|
(pwo, (MCDWINDOW *)pWndPriv, fl);
|
|
}
|
|
break;
|
|
|
|
case WOC_RGN_CLIENT_DELTA:
|
|
if (pWndPriv->pGlobal->mcdDriver.pMCDrvTrackWindow != NULL)
|
|
{
|
|
(*pWndPriv->pGlobal->mcdDriver.pMCDrvTrackWindow)
|
|
(pwo, (MCDWINDOW *)pWndPriv, fl);
|
|
}
|
|
break;
|
|
|
|
case WOC_RGN_SURFACE:
|
|
case WOC_RGN_SURFACE_DELTA:
|
|
|
|
//!!!HACK -- use NULL for pWndPriv; we didn't set it, so we can't
|
|
//!!! trust it
|
|
|
|
pGlobal = MCDSrvGetGlobalInfo(pwo->psoOwner);
|
|
if (pGlobal != NULL &&
|
|
pGlobal->mcdDriver.pMCDrvTrackWindow != NULL)
|
|
{
|
|
(pGlobal->mcdDriver.pMCDrvTrackWindow)
|
|
(pwo, (MCDWINDOW *)NULL, fl);
|
|
}
|
|
break;
|
|
|
|
case WOC_DELETE:
|
|
//MCDBG_PRINT("WndObjChangeProc: WOC_DELETE.");
|
|
|
|
// Window is being deleted, so destroy our private window data,
|
|
// and set the consumer field of the WNDOBJ to NULL:
|
|
|
|
if (pWndPriv)
|
|
{
|
|
DecoupleMCDWindow(pWndPriv);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//****************************************************************************
|
|
// FreeMCDWindowObj()
|
|
//
|
|
// Callback to clean up MCDWINDOWs
|
|
//****************************************************************************
|
|
|
|
BOOL CALLBACK FreeMCDWindowObj(DRIVEROBJ *pDrvObj)
|
|
{
|
|
MCDWINDOWOBJ *pmwo = (MCDWINDOWOBJ *)pDrvObj->pvObj;
|
|
|
|
DestroyMCDWindowObj(pmwo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// NewMCDWindowObj()
|
|
//
|
|
// Creates and initializes a new MCDWINDOW and initializes tracking of the
|
|
// associated window through callback notification.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
MCDWINDOWOBJ *NewMCDWindowObj(MCDSURFACE *pMCDSurface,
|
|
MCDGLOBALINFO *pGlobal,
|
|
HDEV hdev)
|
|
{
|
|
MCDWINDOW *pWnd;
|
|
MCDWINDOWPRIV *pWndPriv;
|
|
MCDWINDOWOBJ *pmwo;
|
|
MCDENUMRECTS *pDefault;
|
|
MCDHANDLE handle;
|
|
|
|
pmwo = (MCDWINDOWOBJ *)MCDSrvLocalAlloc(0, sizeof(MCDWINDOWOBJ));
|
|
if (!pmwo)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Create a driver object for this window
|
|
handle = MCDEngCreateObject(pmwo, FreeMCDWindowObj, hdev);
|
|
if (handle == 0)
|
|
{
|
|
MCDBG_PRINT("NewMCDWindow: Could not create new handle.");
|
|
MCDSrvLocalFree((UCHAR *)pmwo);
|
|
return NULL;
|
|
}
|
|
|
|
pWndPriv = &pmwo->MCDWindowPriv;
|
|
pWnd = &pWndPriv->MCDWindow;
|
|
|
|
// Initialize the structure members:
|
|
|
|
pmwo->type = MCDHANDLE_WINDOW;
|
|
pWndPriv->objectList = NULL;
|
|
pWndPriv->handle = handle;
|
|
pWndPriv->bBuffersValid = FALSE;
|
|
pWndPriv->pGlobal = pGlobal;
|
|
|
|
// Initialize the clipping:
|
|
|
|
pDefault = (MCDENUMRECTS*) &pWndPriv->defaultClipBuffer[0];
|
|
pDefault->c = 0;
|
|
pWndPriv->pAllocatedClipBuffer = NULL;
|
|
pWndPriv->pClipUnscissored = pDefault;
|
|
pWndPriv->sizeClipBuffer = SIZE_DEFAULT_CLIP_BUFFER;
|
|
pWndPriv->sizeClipBuffer = SIZE_DEFAULT_CLIP_BUFFER;
|
|
pWnd->pClip = pDefault;
|
|
|
|
return pmwo;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// MCDSrvNewWndObj()
|
|
//
|
|
// Creates a new WNDOBJ for window tracking.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
WNDOBJ *MCDSrvNewWndObj(MCDSURFACE *pMCDSurface, HWND hWnd, WNDOBJ *pwoIn,
|
|
MCDGLOBALINFO *pGlobal, HDEV hdev)
|
|
{
|
|
MCDWINDOW *pWnd;
|
|
MCDWINDOWPRIV *pWndPriv;
|
|
WNDOBJ *pwo;
|
|
MCDWINDOWOBJ *pmwo;
|
|
|
|
pmwo = NewMCDWindowObj(pMCDSurface, pGlobal, hdev);
|
|
if (!pmwo)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pWndPriv = &pmwo->MCDWindowPriv;
|
|
pWnd = &pWndPriv->MCDWindow;
|
|
|
|
pWndPriv->hWnd = hWnd;
|
|
|
|
// Handle the case where a WNDOBJ already exists but hasn't been
|
|
// initialized for MCD usage in addition to the new creation case.
|
|
if (pwoIn == NULL)
|
|
{
|
|
pwo = MCDEngCreateWndObj(pMCDSurface, hWnd, WndObjChangeProc);
|
|
|
|
if (!pwo || ((LONG_PTR)pwo == -1))
|
|
{
|
|
MCDBG_PRINT("NewMCDWindowTrack: could not create WNDOBJ.");
|
|
MCDEngDeleteObject(pmwo->MCDWindowPriv.handle);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pwo = pwoIn;
|
|
}
|
|
|
|
// Set the consumer field in the WNDOBJ:
|
|
|
|
WNDOBJ_vSetConsumer(pwo, (PVOID)pWndPriv);
|
|
|
|
// Point back to the WNDOBJ
|
|
pWndPriv->pwo = pwo;
|
|
|
|
return pwo;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// MCDSrvNewMcdWindow()
|
|
//
|
|
// Creates a new MCDWINDOW for window tracking.
|
|
//****************************************************************************
|
|
|
|
PRIVATE
|
|
MCDWINDOW *MCDSrvNewMCDWindow(MCDSURFACE *pMCDSurface, HWND hWnd,
|
|
MCDGLOBALINFO *pGlobal, HDEV hdev)
|
|
{
|
|
MCDWINDOW *pWnd;
|
|
MCDWINDOWPRIV *pWndPriv;
|
|
MCDWINDOWOBJ *pmwo;
|
|
|
|
// Initialize tracking of this window with a MCDWINDOW
|
|
// (via a WNDOBJ on NT) if we are not already tracking the
|
|
// window:
|
|
|
|
if (pMCDSurface->surfaceFlags & MCDSURFACE_HWND)
|
|
{
|
|
WNDOBJ *pwo;
|
|
|
|
pwo = MCDEngGetWndObj(pMCDSurface);
|
|
|
|
// Sometimes a WNDOBJ has been used and the MCD state destroyed so
|
|
// the consumer is NULL but the WNDOBJ exists. In that case
|
|
// we need to create a new MCDWINDOW for it.
|
|
if (!pwo || !pwo->pvConsumer)
|
|
{
|
|
pwo = MCDSrvNewWndObj(pMCDSurface, hWnd, pwo, pGlobal, hdev);
|
|
|
|
if (!pwo)
|
|
{
|
|
MCDBG_PRINT("MCDSrvNewMcdWindow: "
|
|
"Creation of window object failed.");
|
|
return NULL;
|
|
}
|
|
|
|
((MCDWINDOW *)pwo->pvConsumer)->pvUser = NULL;
|
|
}
|
|
|
|
pWnd = (MCDWINDOW *)pwo->pvConsumer;
|
|
}
|
|
else
|
|
{
|
|
#if MCD_VER_MAJOR >= 2 || (MCD_VER_MAJOR == 1 && MCD_VER_MINOR >= 0x10)
|
|
MCDENUMRECTS *pDefault;
|
|
PDD_SURFACE_GLOBAL pGbl;
|
|
|
|
pmwo = NewMCDWindowObj(pMCDSurface, pGlobal, hdev);
|
|
if (!pmwo)
|
|
{
|
|
MCDBG_PRINT("MCDSrvNewMcdWindow: "
|
|
"Creation of window object failed.");
|
|
return NULL;
|
|
}
|
|
|
|
pWnd = &pmwo->MCDWindowPriv.MCDWindow;
|
|
|
|
// Real clipping info
|
|
pWndPriv = (MCDWINDOWPRIV *)pWnd;
|
|
|
|
pGbl = ((PDD_SURFACE_LOCAL)pMCDSurface->frontId)->lpGbl;
|
|
pWndPriv->MCDWindow.clientRect.left = pGbl->xHint;
|
|
pWndPriv->MCDWindow.clientRect.top = pGbl->yHint;
|
|
pWndPriv->MCDWindow.clientRect.right = pGbl->xHint+pGbl->wWidth;
|
|
pWndPriv->MCDWindow.clientRect.bottom = pGbl->yHint+pGbl->wHeight;
|
|
pWndPriv->MCDWindow.clipBoundsRect = pWndPriv->MCDWindow.clientRect;
|
|
pWndPriv->bRegionValid = TRUE;
|
|
|
|
pDefault = (MCDENUMRECTS*) &pWndPriv->defaultClipBuffer[0];
|
|
pDefault->c = 1;
|
|
pDefault->arcl[0] = pWndPriv->MCDWindow.clientRect;
|
|
#else
|
|
return NULL;
|
|
#endif // 1.1
|
|
}
|
|
|
|
pMCDSurface->pWnd = pWnd;
|
|
pWndPriv = (MCDWINDOWPRIV *)pWnd;
|
|
pWndPriv->hWnd = hWnd;
|
|
|
|
return pWnd;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// MCD locking support.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//****************************************************************************
|
|
// ULONG MCDSrvLock(MCDWINDOWPRIV *pWndPriv);
|
|
//
|
|
// Lock the MCD driver for the specified window. Fails if lock is already
|
|
// held by another window.
|
|
//****************************************************************************
|
|
|
|
ULONG MCDSrvLock(MCDWINDOWPRIV *pWndPriv)
|
|
{
|
|
ULONG ulRet = MCD_LOCK_BUSY;
|
|
MCDLOCKINFO *pLockInfo;
|
|
|
|
pLockInfo = &pWndPriv->pGlobal->lockInfo;
|
|
if (!pLockInfo->bLocked || pLockInfo->pWndPrivOwner == pWndPriv)
|
|
{
|
|
pLockInfo->bLocked = TRUE;
|
|
pLockInfo->pWndPrivOwner = pWndPriv;
|
|
ulRet = MCD_LOCK_TAKEN;
|
|
}
|
|
|
|
return ulRet;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// VOID MCDSrvUnlock(MCDWINDOWPRIV *pWndPriv);
|
|
//
|
|
// Releases the MCD driver lock if held by the specified window.
|
|
//****************************************************************************
|
|
|
|
VOID MCDSrvUnlock(MCDWINDOWPRIV *pWndPriv)
|
|
{
|
|
MCDLOCKINFO *pLockInfo;
|
|
|
|
//!!!dbug -- could add a lock count, but not really needed right now
|
|
|
|
pLockInfo = &pWndPriv->pGlobal->lockInfo;
|
|
if (pLockInfo->pWndPrivOwner == pWndPriv)
|
|
{
|
|
pLockInfo->bLocked = FALSE;
|
|
pLockInfo->pWndPrivOwner = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
//
|
|
// Per-driver-instance information list handling.
|
|
//
|
|
//****************************************************************************
|
|
|
|
#define GLOBAL_INFO_BLOCK 8
|
|
|
|
ENGSAFESEMAPHORE ssemGlobalInfo;
|
|
MCDGLOBALINFO *pGlobalInfo;
|
|
int iGlobalInfoAllocated = 0;
|
|
int iGlobalInfoUsed = 0;
|
|
|
|
BOOL MCDSrvInitGlobalInfo(void)
|
|
{
|
|
return EngInitializeSafeSemaphore(&ssemGlobalInfo);
|
|
}
|
|
|
|
MCDGLOBALINFO *MCDSrvAddGlobalInfo(SURFOBJ *pso)
|
|
{
|
|
MCDGLOBALINFO *pGlobal;
|
|
|
|
EngAcquireSemaphore(ssemGlobalInfo.hsem);
|
|
|
|
// Ensure space for new entry
|
|
if (iGlobalInfoUsed >= iGlobalInfoAllocated)
|
|
{
|
|
pGlobal = (MCDGLOBALINFO *)
|
|
MCDSrvLocalAlloc(0, (iGlobalInfoAllocated+GLOBAL_INFO_BLOCK)*
|
|
sizeof(MCDGLOBALINFO));
|
|
if (pGlobal != NULL)
|
|
{
|
|
// Copy old data if necessary
|
|
if (iGlobalInfoAllocated > 0)
|
|
{
|
|
memcpy(pGlobal, pGlobalInfo, iGlobalInfoAllocated*
|
|
sizeof(MCDGLOBALINFO));
|
|
MCDSrvLocalFree((UCHAR *)pGlobalInfo);
|
|
}
|
|
|
|
// Set new information
|
|
pGlobalInfo = pGlobal;
|
|
iGlobalInfoAllocated += GLOBAL_INFO_BLOCK;
|
|
iGlobalInfoUsed++;
|
|
|
|
// pGlobal is guaranteed zero-filled because of MCDSrvLocalAlloc's
|
|
// behavior, so just fill in the pso.
|
|
pGlobal += iGlobalInfoAllocated;
|
|
pGlobal->pso = pso;
|
|
}
|
|
else
|
|
{
|
|
// Falls out and returns NULL
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MCDGLOBALINFO *pGlobal;
|
|
int i;
|
|
|
|
pGlobal = pGlobalInfo;
|
|
for (i = 0; i < iGlobalInfoAllocated; i++)
|
|
{
|
|
if (pGlobal->pso == pso)
|
|
{
|
|
// This should never happen.
|
|
MCDBG_PRINT("MCDSrvAddGlobalInfo: duplicate pso");
|
|
pGlobal = NULL;
|
|
break;
|
|
}
|
|
|
|
if (pGlobal->pso == NULL)
|
|
{
|
|
iGlobalInfoUsed++;
|
|
|
|
// Initialize pso for use.
|
|
memset(pGlobal, 0, sizeof(*pGlobal));
|
|
pGlobal->pso = pso;
|
|
break;
|
|
}
|
|
|
|
pGlobal++;
|
|
}
|
|
}
|
|
|
|
EngReleaseSemaphore(ssemGlobalInfo.hsem);
|
|
|
|
return pGlobal;
|
|
}
|
|
|
|
MCDGLOBALINFO *MCDSrvGetGlobalInfo(SURFOBJ *pso)
|
|
{
|
|
MCDGLOBALINFO *pGlobal;
|
|
int i;
|
|
|
|
// For backwards compatibility we handle one instance
|
|
// using global data. If the incoming pso matches the
|
|
// pso in the static data then just return it.
|
|
// It is important to check this before entering the semaphore
|
|
// since the semaphore is not created if only legacy drivers
|
|
// have attached.
|
|
if (pso == gStaticGlobalInfo.pso)
|
|
{
|
|
return &gStaticGlobalInfo;
|
|
}
|
|
|
|
// Technically we shouldn't have to check this, since MCD processing
|
|
// should not occur unless:
|
|
// 1. It's an old style driver and hits the static case above.
|
|
// 2. It's a new style driver and the semaphore has been created.
|
|
// Unfortunately not all drivers are well-behaved, plus there's a
|
|
// potentialy legacy driver bug where drivers don't check for init
|
|
// failure and try to call MCD anyway.
|
|
if (ssemGlobalInfo.hsem == NULL)
|
|
{
|
|
MCDBG_PRINT("MCDSrvGetGlobalInfo: no hsem");
|
|
return NULL;
|
|
}
|
|
|
|
EngAcquireSemaphore(ssemGlobalInfo.hsem);
|
|
|
|
pGlobal = pGlobalInfo;
|
|
for (i = 0; i < iGlobalInfoAllocated; i++)
|
|
{
|
|
if (pGlobal->pso == pso)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pGlobal++;
|
|
}
|
|
|
|
// Technically we shouldn't have to check this, because if
|
|
// we made it into the non-static code path a matching pso should
|
|
// be registered. As with the above check, though, it's better
|
|
// safe than sorry.
|
|
if (i >= iGlobalInfoAllocated)
|
|
{
|
|
MCDBG_PRINT("MCDSrvGetGlobalInfo: no pso match");
|
|
pGlobal = NULL;
|
|
}
|
|
|
|
EngReleaseSemaphore(ssemGlobalInfo.hsem);
|
|
|
|
return pGlobal;
|
|
}
|
|
|
|
void MCDSrvUninitGlobalInfo(void)
|
|
{
|
|
EngDeleteSafeSemaphore(&ssemGlobalInfo);
|
|
}
|
|
|
|
void WINAPI MCDEngUninit(SURFOBJ *pso)
|
|
{
|
|
MCDGLOBALINFO *pGlobal;
|
|
int i;
|
|
|
|
// This should never happen.
|
|
if (ssemGlobalInfo.hsem == NULL)
|
|
{
|
|
MCDBG_PRINT("MCDEngUninit: no hsem");
|
|
return;
|
|
}
|
|
|
|
EngAcquireSemaphore(ssemGlobalInfo.hsem);
|
|
|
|
pGlobal = pGlobalInfo;
|
|
for (i = 0; i < iGlobalInfoAllocated; i++)
|
|
{
|
|
if (pGlobal->pso == pso)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pGlobal++;
|
|
}
|
|
|
|
if (i >= iGlobalInfoAllocated)
|
|
{
|
|
// This should never happen.
|
|
MCDBG_PRINT("MCDEngUninit: No pso match");
|
|
}
|
|
else if (--iGlobalInfoUsed == 0)
|
|
{
|
|
MCDSrvLocalFree((UCHAR *)pGlobalInfo);
|
|
iGlobalInfoAllocated = 0;
|
|
}
|
|
else
|
|
{
|
|
pGlobal->pso = NULL;
|
|
}
|
|
|
|
EngReleaseSemaphore(ssemGlobalInfo.hsem);
|
|
MCDSrvUninitGlobalInfo();
|
|
}
|
|
|
|
//****************************************************************************
|
|
// BOOL HalInitSystem(ULONG a, ULONG b)
|
|
//
|
|
// This is a dummy function needed to use the standard makefile.def since
|
|
// we're pretending we're an NT HAL.
|
|
//****************************************************************************
|
|
|
|
BOOL HalInitSystem(ULONG a, ULONG b)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//******************************Public*Routine******************************
|
|
//
|
|
// BOOL WINAPI DllEntry(HINSTANCE hDLLInst, DWORD fdwReason,
|
|
// LPVOID lpvReserved);
|
|
//
|
|
// DLL entry point invoked for each process and thread that attaches to
|
|
// this DLL.
|
|
//
|
|
//**************************************************************************
|
|
|
|
BOOL WINAPI DllEntry(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
|
|
{
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
// The DLL is being loaded for the first time by a given process.
|
|
// Perform per-process initialization here. If the initialization
|
|
// is successful, return TRUE; if unsuccessful, return FALSE.
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
// The DLL is being unloaded by a given process. Do any
|
|
// per-process clean up here, such as undoing what was done in
|
|
// DLL_PROCESS_ATTACH. The return value is ignored.
|
|
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
// A thread is being created in a process that has already loaded
|
|
// this DLL. Perform any per-thread initialization here. The
|
|
// return value is ignored.
|
|
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
// A thread is exiting cleanly in a process that has already
|
|
// loaded this DLL. Perform any per-thread clean up here. The
|
|
// return value is ignored.
|
|
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|