/***************************************************************************** * * DIDev.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * The standard implementation of IDirectInputDevice. * * This is the device-independent part. the device-dependent * part is handled by the IDirectInputDeviceCallback. * * And the IDirectInputEffect support lives in didevef.c. * * Contents: * * CDIDev_CreateInstance * *****************************************************************************/ #include "dinputpr.h" #define INCLUDED_BY_DIDEV #include "didev.h" /***************************************************************************** * * Declare the interfaces we will be providing. * *****************************************************************************/ Interface_Template_Begin(CDIDev) Primary_Interface_Template(CDIDev, TFORM(ThisInterfaceT)) Secondary_Interface_Template(CDIDev, SFORM(ThisInterfaceT)) Interface_Template_End(CDIDev) /***************************************************************************** * * @doc INTERNAL * * @global PDD * | g_rgpddForeground | * * A list of all devices which have obtained foreground * acquisition. Items on the list have been * held (not AddRef'd). * * @global UINT | g_cpddForeground | * * The number of entries in

. When foreground * activation is lost, all objects in the array are unacquired. * * * @global UINT | g_cpddForegroundMax | * * The size of the

array, including dummy * spaces that are not yet in use. * *****************************************************************************/ /* * ISSUE-2001/03/29-timgill We assume that all-zeros is a valid initialization. */ GPA g_gpaExcl; #define g_hgpaExcl (&g_gpaExcl) /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | NotAcquired | * * Check that the device is not acquired. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns * if all is well, or if * the device is acquired. * *****************************************************************************/ #ifndef XDEBUG #define IDirectInputDevice_NotAcquired_(pdd, z) \ _IDirectInputDevice_NotAcquired_(pdd) \ #endif HRESULT INLINE IDirectInputDevice_NotAcquired_(PDD this, LPCSTR s_szProc) { HRESULT hres; if(!this->fAcquired) { hres = S_OK; } else { RPF("ERROR %s: May not be called while device is acquired", s_szProc); hres = DIERR_ACQUIRED; } return hres; } #define IDirectInputDevice_NotAcquired(pdd) \ IDirectInputDevice_NotAcquired_(pdd, s_szProc) \ /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | IsExclAcquired | * * Check that the device is acquired exclusively. * * The device critical section must already be held. * * @cwrap PDD | this * * @returns * * if the device is exclusively acquired. * * if acquisition has been lost. * * the device is acquired, * but not exclusively, or if the device is not acquired * at all. * *****************************************************************************/ STDMETHODIMP CDIDev_IsExclAcquired_(PDD this, LPCSTR s_szProc) { HRESULT hres; AssertF(CDIDev_InCrit(this)); if(this->discl & DISCL_EXCLUSIVE) { if(this->fAcquired) { hres = S_OK; } else { hres = this->hresNotAcquired; if(hres == DIERR_NOTACQUIRED) { hres = DIERR_NOTEXCLUSIVEACQUIRED; } } } else { hres = DIERR_NOTEXCLUSIVEACQUIRED; } if(s_szProc && hres == DIERR_NOTEXCLUSIVEACQUIRED) { RPF("ERROR %s: Device is not acquired in exclusive mode", s_szProc); } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputDevice | EnterCrit | * * Enter the object critical section. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * *****************************************************************************/ void EXTERNAL CDIDev_EnterCrit_(struct CDIDev *this, LPCTSTR lptszFile, UINT line) { #ifdef XDEBUG if( ! _TryEnterCritSec(&this->crst) ) { SquirtSqflPtszV(sqflCrit, TEXT("Device CritSec blocked @%s,%d"), lptszFile, line); EnterCriticalSection(&this->crst); } SquirtSqflPtszV(sqflCrit, TEXT("Device CritSec Entered @%s,%d"), lptszFile, line); #else EnterCriticalSection(&this->crst); #endif this->thidCrit = GetCurrentThreadId(); InterlockedIncrement(&this->cCrit); } /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputDevice | LeaveCrit | * * Leave the object critical section. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * *****************************************************************************/ void EXTERNAL CDIDev_LeaveCrit_(struct CDIDev *this, LPCTSTR lptszFile, UINT line) { #ifdef XDEBUG AssertF(this->cCrit); AssertF(this->thidCrit == GetCurrentThreadId()); SquirtSqflPtszV(sqflCrit | sqflVerbose, TEXT("Device CritSec Leaving @%s,%d"), lptszFile, line); #endif if(InterlockedDecrement(&this->cCrit) == 0) { this->thidCrit = 0; } LeaveCriticalSection(&this->crst); } /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputDevice | SetNotifyEvent | * * Set the event associated with the device, if any. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * *****************************************************************************/ void EXTERNAL CDIDev_SetNotifyEvent(PDD this) { if(this->hNotify) { SetEvent(this->hNotify); } } /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputDevice | SetForcedUnacquiredFlag | * * When forced unacquired happens, set the fOnceForcedUnacquired flag. * *****************************************************************************/ void EXTERNAL CDIDev_SetForcedUnacquiredFlag(PDD this) { /* * This is an internal interface, so we can skimp on validation. */ this->fOnceForcedUnacquired = 1; } /***************************************************************************** * * @doc INTERNAL * * @method BOOL | CDIDev | InCrit | * * Nonzero if we are in the critical section. * *****************************************************************************/ #ifdef DEBUG BOOL INTERNAL CDIDev_InCrit(PDD this) { return this->cCrit && this->thidCrit == GetCurrentThreadId(); } #endif #ifdef DEBUG /***************************************************************************** * * @doc INTERNAL * * @method BOOL | IDirectInputDevice | IsConsistent | * * Check that various state variables are consistent. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * *****************************************************************************/ #define VerifyF(f,m) if( !(f) ) { fRc = 0; RPF( m ); } BOOL INTERNAL CDIDev_IsConsistent(PDD this) { BOOL fRc = 1; /* * If acquired, then we must have a translation table, a state manager, * and a device callback. */ if(this->fAcquired) { VerifyF( this->pdix, "Acquired device has no translation table" ) VerifyF( this->GetState, "Acquired device has no state manager" ) VerifyF( this->pdcb != c_pdcbNil, "Acquired device has no device callback" ) } /* * If buffering, then must have a device callback. */ if(this->celtBuf) { VerifyF( this->pdcb != c_pdcbNil, "Buffered device has no device callback" ) } /* * If managing an instance, then make sure buffer variables are * consistent. */ if(this->pvi) { if(this->celtBuf) { VerifyF( this->pvi->pBuffer, "Null internal data buffer" ) VerifyF( this->pvi->pEnd, "Null internal end of buffer pointer" ) VerifyF( this->pvi->pHead, "Null internal head of buffer pointer" ) VerifyF( this->pvi->pTail, "Null internal tail of buffer pointer" ) VerifyF( this->pvi->pBuffer < this->pvi->pEnd, "Internal buffer pointers invalid" ) VerifyF(fInOrder((DWORD)(UINT_PTR)this->pvi->pBuffer, (DWORD)(UINT_PTR)this->pvi->pHead, (DWORD)(UINT_PTR)this->pvi->pEnd), "Head of internal buffer pointer invalid" ) VerifyF(fInOrder((DWORD)(UINT_PTR)this->pvi->pBuffer, (DWORD)(UINT_PTR)this->pvi->pTail, (DWORD)(UINT_PTR)this->pvi->pEnd), "Tail of internal buffer pointer invalid" ) } else { VerifyF( ( this->pvi->pBuffer == 0 &&this->pvi->pEnd == 0 &&this->pvi->pHead == 0 &&this->pvi->pTail == 0), "Inactive internal buffer has non-zero pointers" ) } } /* * The cooperative levels must match the cached window handle. */ VerifyF(fLimpFF(this->discl & (DISCL_FOREGROUND | DISCL_EXCLUSIVE), this->hwnd), "Cooperative level does not match window" ); return fRc; } #undef VerifyF #endif /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | QueryInterface | * * Gives a client access to other interfaces on an object. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * * @returns * * Returns a COM error code. * * @xref OLE documentation for . * ***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | AddRef | * * Increments the reference count for the interface. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | Release | * * Decrements the reference count for the interface. * If the reference count on the object falls to zero, * the object is freed from memory. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | QIHelper | * * We don't have any dynamic interfaces and simply forward * to . * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * *****************************************************************************/ #ifdef DEBUG Default_QueryInterface(CDIDev) Default_AddRef(CDIDev) Default_Release(CDIDev) #else #define CDIDev_QueryInterface Common_QueryInterface #define CDIDev_AddRef Common_AddRef #define CDIDev_Release Common_Release #endif #define CDIDev_QIHelper Common_QIHelper /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | CDIDev_Reset | * * Releases all the resources of a generic device that * are associated with a particular device instance. * * This method is called in preparation for reinitialization. * * It is the responsibility of the caller to have taken * any necessary critical sections. * * * @parm PV | pvObj | * * Object being reset. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ STDMETHODIMP CDIDev_Reset(PDD this) { HRESULT hres; if(!this->fAcquired) { /* * Note! that we must release the driver before releasing * the callback, because the callback will unload the * driver DLL. * * We cannot allow people to reset the device * while there are still effects outstanding, * because that would cause us to throw away the * callback while the effects are still using * the effect driver! */ if(this->gpaEff.cpv == 0) { Invoke_Release(&this->pes); Invoke_Release(&this->pdcb); this->pdcb = c_pdcbNil; FreePpv(&this->pdix); FreePpv(&this->rgiobj); FreePpv(&this->pvBuffer); FreePpv(&this->pvLastBuffer); FreePpv(&this->rgdwAxesOfs); FreePpv(&this->rgemi); FreePpv(&this->rgdwPOV); AssertF(!this->fAcquired); AssertF(!this->fAcquiredInstance); if(this->hNotify) { CloseHandle(this->hNotify); } ZeroBuf(&this->hwnd, FIELD_OFFSET(DD, celtBufMax) - FIELD_OFFSET(DD, hwnd)); ZeroX(this->guid); this->celtBufMax = DEVICE_MAXBUFFERSIZE; this->GetDeviceState = CDIDev_GetAbsDeviceState; this->hresNotAcquired = DIERR_NOTACQUIRED; this->fCook = 0; AssertF(this->hNotify == 0); AssertF(this->cemi == 0); AssertF(this->didcFF == 0); this->dwGain = 10000; /* Default to full gain */ this->dwAutoCenter = DIPROPAUTOCENTER_ON; /* Default to centered */ GPA_InitFromZero(&this->gpaEff); hres = S_OK; } else { RPF("IDirectInputDevice::Initialize: Device still has effects"); hres = DIERR_HASEFFECTS; } } else { RPF("IDirectInputDevice::Initialize: Device is busy"); hres = DIERR_ACQUIRED; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func void | CDIDev_AppFinalize | * * The application has performed its final release. * * If the device is acquired, then unacquire it. * * Release the holds on all the created effects that * we have been hanging onto for enumeration purposes. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ void INTERNAL CDIDev_AppFinalize(PV pvObj) { PDD this = pvObj; if(this->fAcquired) { RPF("IDirectInputDevice::Release: Forgot to call Unacquire()"); CDIDev_InternalUnacquire(pvObj); /* * If our refcount is wrong, we may have just freed ourselves. * PREFIX picks this up (mb:34651) however there is no point in * adding checks in case we have bugs in refcounting as that has to * be bug free. */ } if(this->fCritInited) { /* * Stop all the effects, if they are playing. * * Then unhold them (because we're done). * * ISSUE-2001/03/29-timgill Need to totally remove all effects created by a destroyed device * We also need to neuter them so they don't * do anything any more. Otherwise, an app might * destroy the parent device and then try to mess with * an effect created by that device after the device * is gone. * * Note that we cannot call the effect while inside our * private little critical section, because the effect * may need to do crazy things to stop itself. * * (It will almost certainly call back up into the device * to remove itself from the list of created effects.) */ UINT ipdie; PPDIE rgpdie; UINT cpdie; CDIDev_EnterCrit(this); rgpdie = (PV)this->gpaEff.rgpv; cpdie = this->gpaEff.cpv; GPA_Init(&this->gpaEff); CDIDev_LeaveCrit(this); for(ipdie = 0; ipdie < cpdie; ipdie++) { AssertF(rgpdie[ipdie]); // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers. SquirtSqflPtszV(sqfl | sqflError, TEXT("Device %p forgot to destroy effect %p"), this, rgpdie[ipdie]); IDirectInputEffect_Stop(rgpdie[ipdie]); Common_Unhold(rgpdie[ipdie]); } FreePpv(&rgpdie); } } /***************************************************************************** * * @doc INTERNAL * * @func void | CDIDev_Finalize | * * Releases the resources of a generic device. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ void INTERNAL CDIDev_Finalize(PV pvObj) { HRESULT hres; PDD this = pvObj; #ifdef XDEBUG if(this->cCrit) { RPF("IDirectInputDevice::Release: Another thread is using the object; crash soon!"); } #endif AssertF(!this->fAcquired); Invoke_Release(&this->pMS); /* * Note that we cannot take the critical section because it * might not exist. (We might've died during initialization.) * Fortunately, we finalize only after every possible client * (both internal and external) has done its final Release(), * so it's impossible for any other method to get called at * this point. */ hres = CDIDev_Reset(this); AssertF(SUCCEEDED(hres)); if(this->fCritInited) { DeleteCriticalSection(&this->crst); } } /***************************************************************************** * * @doc INTERNAL * * @method void | CDIDev | GetVersions | * * Obtain version information from the device. * * First try to get it from the effect driver. If * that doesn't work, then get it from the VxD. * And if that doesn't work, then tough. * * @parm IN OUT LPDIDRIVERVERSIONS | pvers | * * Receives version information. * *****************************************************************************/ void INLINE CDIDev_GetVersions(PDD this, LPDIDRIVERVERSIONS pvers) { HRESULT hres; /* * Pre-fill with zeros in case nobody implements GetVersions. */ pvers->dwSize = cbX(*pvers); pvers->dwFirmwareRevision = 0; pvers->dwHardwareRevision = 0; pvers->dwFFDriverVersion = 0; hres = CDIDev_CreateEffectDriver(this); /* * Prefix raises a warning (mb:34561) that this->pes could be NULL * however CDIDev_CreateEffectDriver only succeeds if it is not. */ if(SUCCEEDED(hres) && SUCCEEDED(hres = this->pes->lpVtbl-> GetVersions(this->pes, pvers))) { } else { hres = this->pdcb->lpVtbl->GetVersions(this->pdcb, pvers); } } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | GetCapabilitiesHelper | * * Obtains information about the device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

or *

parameter is invalid. * *****************************************************************************/ STDMETHODIMP CDIDev_GetCapabilitiesHelper(PV pdd) { HRESULT hres; PDD this = _thisPv(pdd); LPDIDEVCAPS pdc = (PV)&this->dc3; pdc->dwSize = cbX(this->dc3); hres = this->pdcb->lpVtbl->GetCapabilities(this->pdcb, pdc); if(SUCCEEDED(hres)) { /* * We'll handle the DIDC_EMULATED and * DIDC_POLLEDDATAFORMAT bits to save the callback * some trouble. */ AssertF(this->pvi); if(this->pvi->fl & VIFL_EMULATED) { pdc->dwFlags |= DIDC_EMULATED; } if(this->fPolledDataFormat) { pdc->dwFlags |= DIDC_POLLEDDATAFORMAT; } /* * Add in the force feedback flags, too. */ pdc->dwFlags |= this->didcFF; hres = S_OK; } return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | GetCapabilities | * * Obtains information about the device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN OUT LPDIDEVCAPS | lpdc | * * Points to a structure that is filled in * by the function. The * field "must" be filled in * by the application before calling this method. * See the documentation of the structure * for additional information. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

or *

parameter is invalid. * *****************************************************************************/ STDMETHODIMP CDIDev_GetCapabilities(PV pdd, LPDIDEVCAPS pdc _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetCapabilities, (_ "pp", pdd, pdc)); if(SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = hresFullValidWritePxCb2(pdc, DIDEVCAPS_DX5, DIDEVCAPS_DX3, 1))) { PDD this = _thisPv(pdd); CDIDev_EnterCrit(this); /* * For the convenience of the callback, zero out all the fields, * save for the dwSize. */ ZeroBuf(pvAddPvCb(pdc, cbX(DWORD)), pdc->dwSize - cbX(DWORD)); hres = this->pdcb->lpVtbl->GetCapabilities(this->pdcb, pdc); if(SUCCEEDED(hres)) { /* * We'll handle the DIDC_EMULATED and * DIDC_POLLEDDATAFORMAT bits to save the callback * some trouble. */ AssertF(this->pvi); if(this->pvi->fl & VIFL_EMULATED) { pdc->dwFlags |= DIDC_EMULATED; } if(this->fPolledDataFormat) { pdc->dwFlags |= DIDC_POLLEDDATAFORMAT; } /* * Add in the force feedback flags, too. */ pdc->dwFlags |= this->didcFF; /* * If the caller wants force feedback parameters, then * set them, too. */ if(pdc->dwSize >= cbX(DIDEVCAPS_DX5)) { DIDRIVERVERSIONS vers; pdc->dwFFSamplePeriod = this->ffattr.dwFFSamplePeriod; pdc->dwFFMinTimeResolution = this->ffattr.dwFFMinTimeResolution; CDIDev_GetVersions(this, &vers); pdc->dwFirmwareRevision = vers.dwFirmwareRevision; pdc->dwHardwareRevision = vers.dwHardwareRevision; pdc->dwFFDriverVersion = vers.dwFFDriverVersion; } hres = S_OK; } CDIDev_LeaveCrit(this); ScrambleBit(&pdc->dwDevType, DIDEVTYPE_RANDOM); ScrambleBit(&pdc->dwFlags, DIDC_RANDOM); } ExitOleProcR(); return hres; } #ifdef XDEBUG CSET_STUBS(GetCapabilities, (PV pdd, LPDIDEVCAPS pdc), (pdd, pdc THAT_)) #else #define CDIDev_GetCapabilitiesA CDIDev_GetCapabilities #define CDIDev_GetCapabilitiesW CDIDev_GetCapabilities #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | GetDataFormat | * * Get the data format for the device if we don't have it already. * * @parm PDD | this | * * Device object. * * @returns * * COM return code. * *****************************************************************************/ STDMETHODIMP CDIDev_GetDataFormat(PDD this) { HRESULT hres; LPDIDATAFORMAT pdf; /* * If the DIDATAFORMAT structure changes, you also need to invent * a new DCB message (DIDM_GETDATAFORMAT2), and then do * the right thing when faced with a mix of old and new. */ hres = this->pdcb->lpVtbl->GetDataFormat(this->pdcb, &pdf); /* * Note! We don't support external drivers in this release, * so it's okay to treat these are Assert's and not try to recover. */ if(SUCCEEDED(hres)) { AssertF(pdf->dwSize == sizeof(this->df)); this->df = *pdf; AssertF(!IsBadReadPtr(pdf->rgodf, cbCxX(pdf->dwNumObjs, ODF))); /* * Prepare the axis goo in case the app sets relative mode. */ if(SUCCEEDED(hres = ReallocCbPpv(pdf->dwDataSize, &this->pvLastBuffer)) && SUCCEEDED(hres = ReallocCbPpv(cbCdw(pdf->dwNumObjs), &this->rgdwAxesOfs))) { UINT iobj; this->cAxes = 0; for(iobj = 0; iobj < pdf->dwNumObjs; iobj++) { AssertF(pdf->rgodf[iobj].dwOfs < pdf->dwDataSize); if(pdf->rgodf[iobj].dwType & DIDFT_AXIS) { this->rgdwAxesOfs[this->cAxes++] = pdf->rgodf[iobj].dwOfs; } } } } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | GetPolled | * * Determine whether the device is polled. * * @parm PDD | this | * * Device object. * * @returns * * COM result code. * *****************************************************************************/ STDMETHODIMP CDIDev_GetPolled(PDD this) { HRESULT hres; DIDEVCAPS_DX3 dc; /* * We intentionally use a DIDEVCAPS_DX3 because going for * a full DIDEVCAPS_DX5 requires us to load the force * feedback driver which is pointless for our current * goal. */ ZeroX(dc); dc.dwSize = cbX(dc); hres = this->pdcb->lpVtbl->GetCapabilities(this->pdcb, (PV)&dc); if(SUCCEEDED(hres)) { if(dc.dwFlags & DIDC_POLLEDDEVICE) { this->hresPolled = DI_POLLEDDEVICE; } else { this->hresPolled = S_OK; } } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | GetObjectInfoHelper | * * Set up all the information we can deduce ourselves and * have the callback finish. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm LPCDIPROPINFO | ppropi | * * Object descriptor. * * @parm LPDIDEVICEOBJECTINSTANCEW | pdoiW | * * Structure to receive result. * *****************************************************************************/ STDMETHODIMP CDIDev_GetObjectInfoHelper(PDD this, LPCDIPROPINFO ppropi, LPDIDEVICEOBJECTINSTANCEW pdoiW) { HRESULT hres; AssertF(IsValidSizeDIDEVICEOBJECTINSTANCEW(pdoiW->dwSize)); pdoiW->guidType = *this->df.rgodf[ppropi->iobj].pguid; /* * Always report the internal data format offset here. * This is the way DX3 and DX5 worked until DX7 changed the behavior to * report the user data format value if there was one. That was very * confusing so revert it for DX8. */ pdoiW->dwOfs = this->df.rgodf[ppropi->iobj].dwOfs; pdoiW->dwType = this->df.rgodf[ppropi->iobj].dwType; pdoiW->dwFlags = this->df.rgodf[ppropi->iobj].dwFlags; ScrambleBit(&pdoiW->dwFlags, DIDOI_RANDOM); /* * Wipe out everything starting at tszName. */ ZeroBuf(&pdoiW->tszName, pdoiW->dwSize - FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName)); hres = this->pdcb->lpVtbl->GetObjectInfo(this->pdcb, ppropi, pdoiW); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | EnumObjects | * * Enumerate the input sources (buttons, axes) * available on a device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN LPDIENUMDEVICEOBJECTSCALLBACK | lpCallback | * * Callback function. * * @parm IN LPVOID | pvRef | * * Reference data (context) for callback. * * @parm IN DWORD | fl | * * Flags specifying the type(s) of objects to be enumerated. * See the section "DirectInput Data Format Types" for a * list of flags that can be passed. * * Furthermore, the enumeration can be restricted to objects * from a single HID link collection by using the * macro. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * Note that if the callback stops the enumeration prematurely, * the enumeration is considered to have succeeded. * * = : The *

parameter contains invalid flags, or the callback * procedure returned an invalid status code. * * @cb BOOL CALLBACK | DIEnumDeviceObjectsProc | * * An application-defined callback function that receives * DirectInputDevice objects as a result of a call to the * method. * * @parm IN LPCDIDEVICEOBJECTINSTANCE | lpddoi | * * A structure which describes * the object being enumerated. * * @parm IN OUT LPVOID | pvRef | * Specifies the application-defined value given in the * function. * * @returns * * Returns to continue the enumeration * or to stop the enumeration. * * @ex * * To enumerate all axis objects: * * | * * // C++ * HRESULT hr = pDevice->EnumObjects(EnumProc, RefData, DIDFT_AXIS); * * // C * hr = IDirectInputDevice_EnumObjects(pDevice, EnumProc, RefData, * DIDFT_AXIS); * * @ex * * To enumerate all objects in the third HID link collection: * * | * * // C++ * HRESULT hr = pDevice->EnumObjects(EnumProc, RefData, * DIDFT_ENUMCOLLECTION(3)); * * // C * hr = IDirectInputDevice_EnumObjects(pDevice, EnumProc, RefData, * DIDFT_ENUMCOLLECTION(3)); * *****************************************************************************/ STDMETHODIMP CDIDev_EnumObjectsW (PV pddW, LPDIENUMDEVICEOBJECTSCALLBACKW pec, LPVOID pvRef, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputDevice8::EnumObjectsW, (_ "ppx", pddW, pec, fl)); if(SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidPfn(pec, 1)) && SUCCEEDED(hres = hresFullValidFl(fl, DIDFT_ENUMVALID, 3))) { PDD this = _thisPvNm(pddW, ddW); DWORD flExclude; WORD wCollection; /* * We snapshot the object information underneath the critical * section so we don't blow up if another thread Reset()s * the device in the middle of an enumeration. The DIDATAFORMAT * contains pointers to the dcb, so we need to AddRef the * dcb as well. */ AssertF(!CDIDev_InCrit(this)); CDIDev_EnterCrit(this); if(this->pdcb != c_pdcbNil) { DIPROPINFO propi; DIDATAFORMAT df; IDirectInputDeviceCallback *pdcb; pdcb = this->pdcb; OLE_AddRef(pdcb); df = this->df; CDIDev_LeaveCrit(this); AssertF(!CDIDev_InCrit(this)); flExclude = 0; /* Exclude alises if necessary */ if( !(fl & DIDFT_ALIAS) ) { flExclude |= DIDFT_ALIAS; } else { fl &= ~DIDFT_ALIAS; } /* Exclude Vendor Defined fields */ if( !(fl & DIDFT_VENDORDEFINED) ) { flExclude |= DIDFT_VENDORDEFINED; } else { fl &= ~DIDFT_VENDORDEFINED; } if(fl == DIDFT_ALL) { fl = DIDFT_ALLOBJS; } /* * Pull out the link collection we are enumerating. * Note: Backwards compatibility hack. We can't * use link collection 0 to mean "no parent" because * 0 means "don't care". So instead, we use 0xFFFF * to mean "no parent". This means that we need to * interchange 0 and 0xFFFF before entering the main * loop. */ wCollection = DIDFT_GETCOLLECTION(fl); switch(wCollection) { case 0: wCollection = 0xFFFF; break; case DIDFT_GETCOLLECTION(DIDFT_NOCOLLECTION): wCollection = 0; break; } propi.pguid = 0; for(propi.iobj = 0; propi.iobj < df.dwNumObjs; propi.iobj++) { propi.dwDevType = df.rgodf[propi.iobj].dwType; if((propi.dwDevType & fl & DIDFT_TYPEMASK) && fHasAllBitsFlFl(propi.dwDevType, fl & DIDFT_ATTRMASK) && !(propi.dwDevType & flExclude)) { DIDEVICEOBJECTINSTANCEW doiW; doiW.dwSize = cbX(doiW); hres = CDIDev_GetObjectInfoHelper(this, &propi, &doiW); if(SUCCEEDED(hres) && fLimpFF(wCollection != 0xFFFF, doiW.wCollectionNumber == wCollection)) { BOOL fRc = Callback(pec, &doiW, pvRef); switch(fRc) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: RPF("IDirectInputDevice::EnumObjects: Invalid return value from enumeration callback"); ValidationException(); break; } } else { if( hres == DIERR_OBJECTNOTFOUND ) { // This can only happen on Keyboard. continue; } else { goto enumdonefail; } } } } enumdoneok:; hres = S_OK; enumdonefail:; OLE_Release(pdcb); } else { CDIDev_LeaveCrit(this); RPF("ERROR: IDirectInputDevice: Not initialized"); hres = DIERR_NOTINITIALIZED; } } ExitOleProcR(); return hres; } #define CDIDev_EnumObjects2W CDIDev_EnumObjectsW /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDeviceA | EnumObjectsCallbackA | * * Custom callback that wraps * which * translates the UNICODE string to ANSI. * * @parm IN LPCDIENUMDEVICEOBJECTINSTANCE | pdoiW | * * Structure to be translated to ANSI. * * @parm IN LPVOID | pvRef | * * Pointer to which describes * the original callback. * * @returns * * Returns whatever the original callback returned. * *****************************************************************************/ typedef struct ENUMOBJECTSINFO { LPDIENUMDEVICEOBJECTSCALLBACKA pecA; PV pvRef; } ENUMOBJECTSINFO, *PENUMOBJECTSINFO; BOOL CALLBACK CDIDev_EnumObjectsCallbackA(LPCDIDEVICEOBJECTINSTANCEW pdoiW, PV pvRef) { PENUMOBJECTSINFO peoi = pvRef; BOOL fRc; DIDEVICEOBJECTINSTANCEA doiA; EnterProc(CDIDev_EnumObjectsCallbackA, (_ "GxxWp", &pdoiW->guidType, pdoiW->dwOfs, pdoiW->dwType, pdoiW->tszName, pvRef)); doiA.dwSize = cbX(doiA); ObjectInfoWToA(&doiA, pdoiW); fRc = peoi->pecA(&doiA, peoi->pvRef); ExitProcX(fRc); return fRc; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDeviceA | EnumObjects | * * Enumerate the input sources (buttons, axes) * available on a device, in ANSI. * See for more information. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN LPDIENUMDEVICEOBJECTSCALLBACK | lpCallback | * * Same as , except in ANSI. * * @parm IN LPVOID | pvRef | * * Same as . * * @parm IN DWORD | fl | * * Same as . * *****************************************************************************/ STDMETHODIMP CDIDev_EnumObjectsA (PV pddA, LPDIENUMDEVICEOBJECTSCALLBACKA pec, LPVOID pvRef, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputDevice8A::EnumDevices, (_ "pppx", pddA, pec, pvRef, fl)); /* * EnumObjectsW will validate the rest. */ if(SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidPfn(pec, 1))) { ENUMOBJECTSINFO eoi = { pec, pvRef}; PDD this = _thisPvNm(pddA, ddA); hres = CDIDev_EnumObjectsW(&this->ddW, CDIDev_EnumObjectsCallbackA, &eoi, fl); } ExitOleProcR(); return hres; } #define CDIDev_EnumObjects2A CDIDev_EnumObjectsA /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | SetEventNotification | * * Specify the event that should be set when the device * state changes, or turns off such notifications. * * A device state change is defined as any of the following: * * - A change in the position of an axis. * * - A change in the state (pressed or released) of a button. * * - A change in the direction of a POV control. * * - Loss of acquisition. * * "It is an error" to call on the event * while it has been selected into an * object. You must call * with the *

parameter set to NULL before closing the * event handle. * * The event notification handle cannot be changed while the * device is acquired. * * If the function is successful, then the application can * use the event handle in the same manner as any other * Win32 event handle. Examples of usage are shown below. * For additional information on using Win32 wait functions, * see the Win32 SDK and related documentation. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN HANDLE | hEvent | * * Specifies the event handle which will be set when the * device state changes. It "must" be an event * handle. DirectInput will the handle when * the state of the device changes. * * The application should create the handle via the * function. If the event is created as * an automatic-reset event, then the operating system will * automatically reset the event once a wait has been * satisfied. If the event is created as a manual-reset * event, then it is the application's responsibility to * call to reset it. DirectInput will not * call for event notification handles. * * If the

is zero, then notification is disabled. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The object * already has an event notification handle * associated with it. DirectInput supports only one event * notification handle per object. * * : The object * has been acquired. You must * the device before you can change the notification state. * * : The thing isn't an event handle. * * @ex * * To check if the handle is currently set without blocking: * * | * * dwResult = WaitForSingleObject(hEvent, 0); * if (dwResult == WAIT_OBJECT_0) { * // Event is set. If the event was created as * // automatic-reset, then it has also been reset. * } * * @ex * * The following example illustrates blocking * indefinitely until the event is set. Note that this * behavior is discouraged because the thread * will not respond to the system until the wait is * satisfied. (In particular, the thread will not respond * to Windows messages.) * * | * * dwResult = WaitForSingleObject(hEvent, INFINITE); * if (dwResult == WAIT_OBJECT_0) { * // Event has been set. If the event was created as * // automatic-reset, then it has also been reset. * } * * @ex * * The following example illustrates a typical message loop * for a message-based application that uses two events. * * | * * HANDLE ah[2] = { hEvent1, hEvent2 }; * * while (TRUE) { * * dwResult = MsgWaitForMultipleObjects(2, ah, FALSE, * INFINITE, QS_ALLINPUT); * switch (dwResult) { * case WAIT_OBJECT_0: * // Event 1 has been set. If the event was created as * // automatic-reset, then it has also been reset. * ProcessInputEvent1(); * break; * * case WAIT_OBJECT_0 + 1: * // Event 2 has been set. If the event was created as * // automatic-reset, then it has also been reset. * ProcessInputEvent2(); * break; * * case WAIT_OBJECT_0 + 2: * // A Windows message has arrived. Process messages * // until there aren't any more. * while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { * if (msg.message == WM_QUIT) { * goto exitapp; * } * TranslateMessage(&msg); * DispatchMessage(&msg); * } * break; * * default: * // Unexpected error. * Panic(); * break; * } * } * * @ex * * The following example illustrates a typical application loop * for a non-message-based application that uses two events. * * | * * HANDLE ah[2] = { hEvent1, hEvent2 }; * DWORD dwWait = 0; * * while (TRUE) { * * dwResult = MsgWaitForMultipleObjects(2, ah, FALSE, * dwWait, QS_ALLINPUT); * dwWait = 0; * * switch (dwResult) { * case WAIT_OBJECT_0: * // Event 1 has been set. If the event was created as * // automatic-reset, then it has also been reset. * ProcessInputEvent1(); * break; * * case WAIT_OBJECT_0 + 1: * // Event 2 has been set. If the event was created as * // automatic-reset, then it has also been reset. * ProcessInputEvent2(); * break; * * case WAIT_OBJECT_0 + 2: * // A Windows message has arrived. Process messages * // until there aren't any more. * while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { * if (msg.message == WM_QUIT) { * goto exitapp; * } * TranslateMessage(&msg); * DispatchMessage(&msg); * } * break; * * default: * // No input or messages waiting. * // Do a frame of the game. * // If the game is idle, then tell the next wait * // to wait indefinitely for input or a message. * if (!DoGame()) { * dwWait = INFINITE; * } * // Poll for data in case the device is not * // interrupt-driven. * IDirectInputDevice8_Poll(pdev); * break; * } * } * * *****************************************************************************/ STDMETHODIMP CDIDev_SetEventNotification(PV pdd, HANDLE h _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::SetEventNotification, (_ "px", pdd, h)); if(SUCCEEDED(hres = hresPvT(pdd))) { PDD this = _thisPv(pdd); /* * Must protect with the critical section to prevent somebody from * acquiring or setting a new event handle while we're changing it. */ CDIDev_EnterCrit(this); if(!this->fAcquired) { /* * Don't operate on the original handle because * the app might decide to do something *interesting* to it * on another thread. */ hres = DupEventHandle(h, &h); if(SUCCEEDED(hres)) { /* * Resetting the event serves two purposes. * * 1. It performs parameter validation for us, and * 2. The event must be reset while the device is * not acquired. */ if(fLimpFF(h, ResetEvent(h))) { if(!this->hNotify || !h) { hres = this->pdcb->lpVtbl-> SetEventNotification(this->pdcb, h); /* * All dcb's use default handling for now so * assert the callback returns S_FALSE * so we are reminded to change this if need be. * An uninitialized device would fail but don't * break if we hit one of those, just assert that * we won't accidentally call a HEL on it. */ AssertF( ( hres == S_FALSE ) || ( ( hres == DIERR_NOTINITIALIZED ) && !this->pvi ) ); if(this->pvi) { VXDDWORDDATA vhd; vhd.pvi = this->pvi; vhd.dw = (DWORD)(UINT_PTR)h; hres = Hel_SetNotifyHandle(&vhd); AssertF(SUCCEEDED(hres)); /* Should never fail */ h = (HANDLE)(UINT_PTR)pvExchangePpvPv64(&this->hNotify, (UINT_PTR)h); hres = this->hresPolled; } else { /* * ISSUE-2001/03/29-timgill Is this actually an error case? * Can this ever validly occur? * if yes, don't we need any of the above? */ RPF( "Device internal data missing on SetEventNotification" ); } } else { hres = DIERR_HANDLEEXISTS; } } else { RPF( "Handle not for Event in SetEventNotification" ); hres = E_HANDLE; } /* * Close the old handle if we swapped one out * or our duplicate if something went wrong */ if(h != 0) { CloseHandle(h); } } } else { hres = DIERR_ACQUIRED; } CDIDev_LeaveCrit(this); } ExitOleProc(); return hres; } #ifdef XDEBUG CSET_STUBS(SetEventNotification, (PV pdd, HANDLE h), (pdd, h THAT_)) #else #define CDIDev_SetEventNotificationA CDIDev_SetEventNotification #define CDIDev_SetEventNotificationW CDIDev_SetEventNotification #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | hresMapHow | * * Map the

and

fields into an object index. * * @parm DWORD | dwObj | * * Object identifier. * * @parm DWORD | dwHow | * * How

should be interpreted. * * @parm OUT LPDIPROPINFO | ppropi | * * Receives object index and

. * *****************************************************************************/ #ifndef XDEBUG #define CDIDev_hresMapHow_(this, dwObj, dwHow, ppropi, z) \ _CDIDev_hresMapHow_(this, dwObj, dwHow, ppropi) \ #endif STDMETHODIMP CDIDev_hresMapHow_(PDD this, DWORD dwObj, DWORD dwHow, LPDIPROPINFO ppropi, LPCSTR s_szProc) { HRESULT hres; if(this->pdcb != c_pdcbNil) { int iobj = 0; switch(dwHow) { case DIPH_DEVICE: if(dwObj == 0) { ppropi->iobj = 0xFFFFFFFF; ppropi->dwDevType = 0; hres = S_OK; } else { RPF("%s: dwObj must be zero if DIPH_DEVICE", s_szProc); hres = E_INVALIDARG; } break; case DIPH_BYOFFSET: if(this->pdix && this->rgiobj) { if(dwObj < this->dwDataSize) { iobj = this->rgiobj[dwObj]; if(iobj >= 0) { AssertF(this->pdix[iobj].dwOfs == dwObj); ppropi->iobj = iobj; ppropi->dwDevType = this->df.rgodf[iobj].dwType; hres = S_OK; goto done; } else { AssertF(iobj == -1); } } SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%S: Invalid offset in dwObj. You may use DIPH_BYID to enum it."), s_szProc); //RPF("%s: Invalid offset in dwObj", s_szProc); } else { RPF("%s: Must have a data format to use if DIPH_BYOFFSET", s_szProc); } hres = DIERR_OBJECTNOTFOUND; goto done; case DIPH_BYID: for(iobj = this->df.dwNumObjs; --iobj >= 0; ) { if(DIDFT_FINDMATCH(this->df.rgodf[iobj].dwType, dwObj)) { ppropi->iobj = iobj; ppropi->dwDevType = this->df.rgodf[iobj].dwType; hres = S_OK; goto done; } } RPF("%s: Invalid ID in dwObj", s_szProc); hres = DIERR_OBJECTNOTFOUND; break; case DIPH_BYUSAGE: hres = IDirectInputDeviceCallback_MapUsage(this->pdcb, dwObj, &ppropi->iobj); if(SUCCEEDED(hres)) { ppropi->dwDevType = this->df.rgodf[ppropi->iobj].dwType; } break; default: RPF("%s: Invalid dwHow", s_szProc); hres = E_INVALIDARG; break; } done:; } else { RPF("ERROR: IDirectInputDevice: Not initialized"); hres = DIERR_NOTINITIALIZED; } return hres; } #define CDIDev_hresMapHow(this, dwObj, dwHow, pdwOut) \ CDIDev_hresMapHow_(this, dwObj, dwHow, pdwOut, s_szProc) \ /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | GetObjectInfo | * * Obtains information about an object. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm OUT LPDIDEVICEOBJECTINSTANCE | pdidoi | * * Receives information about the object. * The caller "must" initialize the * field before calling this method. * * @parm DWORD | dwObj | * * Identifies the object for which the property is to be * accessed. The meaning of this value depends on the * value of the

parameter. * See the documentation of the * structure for additional information. * * @parm DWORD | dwHow | * * Identifies how

is to be interpreted. * It must be one of the values. * See the documentation of the * structure for additional information. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * * : The specified object does not * exist. * *****************************************************************************/ STDMETHODIMP CDIDev_GetObjectInfoW(PV pddW, LPDIDEVICEOBJECTINSTANCEW pdoiW, DWORD dwObj, DWORD dwHow) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetObjectInfo, (_ "ppxx", pddW, pdoiW, dwObj, dwHow)); if(SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidWritePxCb2(pdoiW, DIDEVICEOBJECTINSTANCE_DX5W, DIDEVICEOBJECTINSTANCE_DX3W, 1))) { PDD this = _thisPvNm(pddW, ddW); DIPROPINFO propi; /* * Must protect with the critical section to prevent * another thread from Reset()ing behind our back. */ CDIDev_EnterCrit(this); propi.pguid = 0; /* * Prefix picks up that ppropi->iobj is uninitialized (mb:34557) in * the case of a BY_USAGE dwHow. However, the value is only an * output for MapUsage which will always either set it or fail so * there is no error. */ hres = CDIDev_hresMapHow(this, dwObj, dwHow, &propi); if(SUCCEEDED(hres)) { if(dwHow != DIPH_DEVICE) { hres = CDIDev_GetObjectInfoHelper(this, &propi, pdoiW); } else { RPF("%s: Invalid dwHow", s_szProc); hres = E_INVALIDARG; } } if(FAILED(hres)) { ScrambleBuf(&pdoiW->guidType, pdoiW->dwSize - FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, guidType)); } CDIDev_LeaveCrit(this); } ExitBenignOleProcR(); return hres; } #define CDIDev_GetObjectInfo2W CDIDev_GetObjectInfoW /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDeviceA | GetObjectInfo | * * ANSI version of same. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm OUT LPDIDEVICEOBJECTINSTANCEA | pdoiA | * * Receives information about the device's identity. * * @parm DWORD | dwObj | * * Identifies the object for which the property is to be * accessed. * * @parm DWORD | dwHow | * * Identifies how

is to be interpreted. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ STDMETHODIMP CDIDev_GetObjectInfoA(PV pddA, LPDIDEVICEOBJECTINSTANCEA pdoiA, DWORD dwObj, DWORD dwHow) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetObjectInfo, (_ "ppxx", pddA, pdoiA, dwObj, dwHow)); if(SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidWritePxCb2(pdoiA, DIDEVICEOBJECTINSTANCE_DX5A, DIDEVICEOBJECTINSTANCE_DX3A, 1))) { PDD this = _thisPvNm(pddA, ddA); DIDEVICEOBJECTINSTANCEW doiW; doiW.dwSize = cbX(DIDEVICEOBJECTINSTANCEW); hres = CDIDev_GetObjectInfoW(&this->ddW, &doiW, dwObj, dwHow); if(SUCCEEDED(hres)) { ObjectInfoWToA(pdoiA, &doiW); hres = S_OK; } } ExitBenignOleProcR(); return hres; } #define CDIDev_GetObjectInfo2A CDIDev_GetObjectInfoA /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | GetDeviceInfo | * * Obtains information about the device's identity. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm OUT LPDIDEVICEINSTANCE | pdidi | * * Receives information about the device's identity. * The caller "must" initialize the * field before calling this method. * * If is equal to the size of * the structure, then a * DirectX 3.0-compatible structure is returned instead of * a DirectX 5.0 structure. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ STDMETHODIMP CDIDev_GetDeviceInfoW(PV pddW, LPDIDEVICEINSTANCEW pdidiW) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetDeviceInfo, (_ "pp", pddW, pdidiW)); if(SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidWritePxCb2(pdidiW, DIDEVICEINSTANCE_DX5W, DIDEVICEINSTANCE_DX3W, 1))) { PDD this = _thisPvNm(pddW, ddW); /* * Must protect with the critical section to prevent * another thread from Reset()ing behind our back. */ CDIDev_EnterCrit(this); pdidiW->guidInstance = this->guid; pdidiW->guidProduct = this->guid; /* * Don't overwrite the dwSize, guidInstance, or guidProduct. * Start at the dwDevType. */ ZeroBuf(&pdidiW->dwDevType, pdidiW->dwSize - FIELD_OFFSET(DIDEVICEINSTANCEW, dwDevType)); hres = this->pdcb->lpVtbl->GetDeviceInfo(this->pdcb, pdidiW); if(FAILED(hres)) { ScrambleBuf(&pdidiW->guidInstance, cbX(DIDEVICEINSTANCEW) - FIELD_OFFSET(DIDEVICEINSTANCEW, guidInstance)); } CDIDev_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func void | DeviceInfoWToA | * * Convert a to a * or a . * * @parm LPDIDIDEVICEINSTANCEA | pdiA | * * Destination. * * @parm LPCDIDIDEVICEINSTANCEW | pdiW | * * Source. * *****************************************************************************/ void EXTERNAL DeviceInfoWToA(LPDIDEVICEINSTANCEA pdiA, LPCDIDEVICEINSTANCEW pdiW) { EnterProc(DeviceInfoWToA, (_ "pp", pdiA, pdiW)); AssertF(pdiW->dwSize == sizeof(DIDEVICEINSTANCEW)); AssertF(IsValidSizeDIDEVICEINSTANCEA(pdiA->dwSize)); pdiA->guidInstance = pdiW->guidInstance; pdiA->guidProduct = pdiW->guidProduct; pdiA->dwDevType = pdiW->dwDevType; UToA(pdiA->tszInstanceName, cA(pdiA->tszInstanceName), pdiW->tszInstanceName); UToA(pdiA->tszProductName, cA(pdiA->tszProductName), pdiW->tszProductName); if(pdiA->dwSize >= cbX(DIDEVICEINSTANCE_DX5A)) { pdiA->guidFFDriver = pdiW->guidFFDriver; pdiA->wUsage = pdiW->wUsage; pdiA->wUsagePage = pdiW->wUsagePage; } ExitProc(); } /***************************************************************************** * * @doc INTERNAL * * @func void | Device8WTo8A | * * Convert a to a . * * @parm LPDIRECTINPUTDEVICE8A * | ppdid8A | * * Destination. * * @parm LPDIRECTINPUTDEVICE8W | pdid8W | * * Source. * *****************************************************************************/ void EXTERNAL Device8WTo8A(LPDIRECTINPUTDEVICE8A *ppdid8A, LPDIRECTINPUTDEVICE8W pdid8W) { PDD this; EnterProc(Device8WTo8A, (_ "pp", ppdid8A, pdid8W)); this = _thisPvNm(pdid8W, ddW); *ppdid8A = (ThisInterfaceA*)&this->ddA; ExitProc(); } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDeviceA | GetDeviceInfo | * * ANSI version of same. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm OUT LPDIDEVICEINSTANCEA | pdidiA | * * Receives information about the device's identity. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ STDMETHODIMP CDIDev_GetDeviceInfoA(PV pddA, LPDIDEVICEINSTANCEA pdidiA) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetDeviceInfo, (_ "pp", pddA, pdidiA)); if(SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidWritePxCb2(pdidiA, DIDEVICEINSTANCE_DX5A, DIDEVICEINSTANCE_DX3A, 1))) { PDD this = _thisPvNm(pddA, ddA); DIDEVICEINSTANCEW diW; diW.dwSize = cbX(DIDEVICEINSTANCEW); hres = CDIDev_GetDeviceInfoW(&this->ddW, &diW); if(SUCCEEDED(hres)) { DeviceInfoWToA(pdidiA, &diW); hres = S_OK; } } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method void | CDIDev | UnhookCwp | * * Remove the CallWndProc handler. * * See for details. * *****************************************************************************/ HWND g_hwndExclusive; HHOOK g_hhkCwp; void INTERNAL CDIDev_UnhookCwp(void) { DllEnterCrit(); if(g_hhkCwp) { UnhookWindowsHookEx(g_hhkCwp); g_hhkCwp = 0; g_hwndExclusive = 0; } DllLeaveCrit(); } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | AddForegroundDevice | * * Add ourselves to the list of devices that need to be * unacquired when the window loses foreground activation. * * @parm PDD | this | * * Device to be added. * * @devnote * * Note that we do not need to AddRef the device, because * will unacquire the device for us * automatically, so we will never be freed while still * acquired. * * (Note that if we did choose to AddRef, it must be done * outside the DLL critical * section, in order to preserve the semaphore hierarchy.) * *****************************************************************************/ STDMETHODIMP CDIDev_AddForegroundDevice(PDD this) { HRESULT hres; DllEnterCrit(); hres = GPA_Append(g_hgpaExcl, this); Common_Hold(this); DllLeaveCrit(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method void | CDIDev | DelForegroundDevice | * * Remove ourselves from the list, if we're there. * It is not an error if we aren't on the list, because * in the case of forced unacquire, the list is blanked * out in order to avoid race conditions where somebody * tries to acquire a device immediately upon receiving * foreground activation, before we get a chance to * unacquire all the old guys completely. * * @parm PDD | this | * * Device to be removed. * * @devnote * * Note that the Unhold must be done outside the DLL critical * section, in order to preserve the semaphore hierarchy. * * Theoretically, the unhold will never drop the reference count * to zero (because the latest it could be called is during * the AppFinalize, where there is stll the outstanding refcount * from the external ref that hasn't been released yet). * * But it's better to play it safe and always release the * object outside. * *****************************************************************************/ void INTERNAL CDIDev_DelForegroundDevice(PDD this) { HRESULT hres; DllEnterCrit(); hres = GPA_DeletePtr(g_hgpaExcl, this); if(hres == hresUs(0)) { /* If the last one went away */ GPA_Term(g_hgpaExcl); /* Free the tracking memory */ CDIDev_UnhookCwp(); /* Then unhook ourselves */ } DllLeaveCrit(); if(SUCCEEDED(hres)) { Common_Unhold(this); } } /***************************************************************************** * * @doc INTERNAL * * @func LRESULT | CDIDev_CallWndProc | * * Thread-specific CallWndProc handler. * * Note that we need only one of these, since only the foreground * window will require a hook. * * @parm int | nCode | * * Notification code. * * @parm WPARAM | wp | * * "Specifies whether the message is sent by the current process." * We don't care. * * @parm LPARAM | lp | * * Points to a which describes the message. * * @returns * * Always chains to the next hook. * *****************************************************************************/ LRESULT CALLBACK CDIDev_CallWndProc(int nCode, WPARAM wp, LPARAM lp) { LRESULT rc; LPCWPSTRUCT pcwp = (LPCWPSTRUCT)lp; #ifdef WINNT static BOOL fKillFocus = FALSE; static BOOL fIconic = FALSE; fIconic = FALSE; /* * This part of code is to fix Windows bug 430051. * The logic is: if WM_KILLFOCUS is followed by WM_SIZE(minimized), * then the app is minimized from full screen mode. * This combination should only happen to full screen mode game using DDraw. */ if(pcwp->message == WM_KILLFOCUS) { fKillFocus = TRUE; } else if(pcwp->message == WM_SETFOCUS) { fKillFocus = FALSE; } else if (pcwp->message == WM_SIZE) { if(pcwp->wParam == SIZE_MINIMIZED){ if( fKillFocus ) { fIconic = TRUE; fKillFocus = FALSE; }else{ fKillFocus = FALSE; } } else { fKillFocus = FALSE; } } #endif rc = CallNextHookEx(g_hhkCwp, nCode, wp, lp); if( nCode == HC_ACTION && (pcwp->message == WM_ACTIVATE #ifdef WINNT || fIconic #endif ) ) { PDD *rgpdid; UINT ipdid, cpdid; #ifdef WINNT fIconic = FALSE; #endif /* * We cannot mess with items while inside the DLL critical section, * because that would violate our semaphore hierarchy. * * Instead, we stash the active item list and replace it with * an empty list. Then, outside the DLL critical section, we * calmly operate on each item. */ DllEnterCrit(); rgpdid = (PV)g_hgpaExcl->rgpv; cpdid = g_hgpaExcl->cpv; GPA_Init(g_hgpaExcl); /* * Some sanity checking here. */ for(ipdid = 0; ipdid < cpdid; ipdid++) { AssertF(rgpdid[ipdid]); } DllLeaveCrit(); /* * Note that InternalUnacquire will set the notification * event so the app knows that input was lost. */ for(ipdid = 0; ipdid < cpdid; ipdid++) { AssertF(rgpdid[ipdid]); // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers. SquirtSqflPtszV(sqfl, TEXT("Forcing unacquire of %p due to focus loss"), rgpdid[ipdid]); CDIDev_InternalUnacquire(rgpdid[ipdid]); /* * Prefix notices (mb:34651) that the above could release the * interface, causing the following to party on garbage but this * should be OK as long as we don't have a refcounting bug. */ Common_Unhold(rgpdid[ipdid]); } FreePpv(&rgpdid); CDIDev_UnhookCwp(); } return rc; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | CanAcquire | * * Determine whether the device may be acquired exclusively. * * If exclusive access is not requested, then the function * succeeds vacuously. * * If exclusive access is requested, then the window must * be the foreground window and must belong to the current * process. * *****************************************************************************/ STDMETHODIMP CDIDev_CanAcquire(PDD this) { HRESULT hres; AssertF(CDIDev_IsConsistent(this)); if(this->discl & DISCL_FOREGROUND) { HWND hwndForeground = GetForegroundWindow(); AssertF(this->hwnd); /* * Note that we don't have to do an IsWindow() on this->hwnd, * because GetForegroundWindow() will always return a valid * window or NULL. Since we already tested this->hwnd != 0 * above, the only way the equality can occur is if the window * handle is indeed valid. */ if(this->hwnd == hwndForeground && !IsIconic(this->hwnd)) { /* * Need to make sure that the window "still" belongs to * this process, in case the window handle got recycled. */ DWORD idProcess; GetWindowThreadProcessId(this->hwnd, &idProcess); if(idProcess == GetCurrentProcessId()) { hres = S_OK; } else { /* * Put a permanently invalid handle here so that we * won't accidentally take a new window that happens * to get a recycled handle value. */ this->hwnd = INVALID_HANDLE_VALUE; RPF("Error: Window destroyed while associated with a device"); hres = E_INVALIDARG; } } else { hres = DIERR_OTHERAPPHASPRIO; } } else { /* No window; vacuous success */ hres = S_OK; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | InstallCwp | * * Install the CallWndProc handler. * * There is a bit of subtlety in the way this works. * Since only foreground windows may acquire exclusive access, * we need only one hook (for there is but one foreground * window in the system). * * _NT_: Does NT handle this correctly in the face of * multiple window stations? * * The tricky part is that input loss occurs asynchronously. * So a device that registers a hook doesn't * find out that the input has been lost until the app next * calls . * * So the way this is done is via a collection of global * variables (which must be accessed atomically). * *

is the hook handle itself. It is zero when * no hook is installed. * * Note that we install the windows hook while inside both the * object critical section and the DLL critical section. * You might think we'd risk deadlocking with the hook procedure, * in case the window asynchronously deactivates while we're * installing the hook. But your worries are unfounded: * If the window is on the current thread, then window messages * won't be dispatched because we never call or * go into a modal loop. And if the window is on another thread, * then that other thread will simply have to wait until we're done. * *****************************************************************************/ STDMETHODIMP CDIDev_InstallCwp(PDD this) { HRESULT hres; if(this->discl & DISCL_FOREGROUND) { AssertF(this->hwnd); hres = CDIDev_CanAcquire(this); if(SUCCEEDED(hres)) { hres = CDIDev_AddForegroundDevice(this); if(SUCCEEDED(hres)) { DllEnterCrit(); if(!g_hhkCwp) { /* We're the first one */ g_hwndExclusive = this->hwnd; g_hhkCwp = SetWindowsHookEx(WH_CALLWNDPROC, CDIDev_CallWndProc, g_hinst, GetWindowThreadProcessId(this->hwnd, 0)); } else { AssertF(g_hwndExclusive == this->hwnd); } DllLeaveCrit(); /* * There is a race condition up above, where the foreground * window can change between the call to CanAcquire() and * the call to SetWindowsHookEx(). Close the window by * checking a second time after the hook is installed. * * If we leave the window open, it's possible that we will * perform a physical acquire while the wrong window has * foreground activation. Then, of course, we are never told * that *our* window lost activation, and the physical device * remains acquired forever. */ hres = CDIDev_CanAcquire(this); if(SUCCEEDED(hres)) { } else { SquirtSqflPtszV(sqflError, TEXT("Window no longer foreground; ") TEXT("punting acquire")); CDIDev_InternalUnacquire(this); } } } else { hres = DIERR_OTHERAPPHASPRIO; } } else { /* No window; vacuous success */ hres = S_OK; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | RealUnacquire | * * Release access to the device, even if the device was only * partially acquired. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * None. * *****************************************************************************/ STDMETHODIMP CDIDev_RealUnacquire(PDD this) { HRESULT hres; hres = this->pdcb->lpVtbl->Unacquire(this->pdcb); if(hres == S_FALSE) { if(this->fAcquiredInstance) { this->fAcquiredInstance = 0; hres = Hel_UnacquireInstance(this->pvi); AssertF(SUCCEEDED(hres)); } else { hres = S_OK; } } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | FFAcquire | * * The device has been successfully acquired. Do any * necessary force feedback related acquisition goo. * * @cwrap PDD | pdd * *****************************************************************************/ STDMETHODIMP CDIDev_FFAcquire(PDD this) { HRESULT hres; AssertF(CDIDev_InCrit(this)); if(this->pes && (this->discl & DISCL_EXCLUSIVE)) { if(SUCCEEDED(hres = this->pes->lpVtbl->SendForceFeedbackCommand( this->pes, &this->sh, DISFFC_FORCERESET))) { CDIDev_RefreshGain(this); /* * If the center spring is to be disabled, * then disable the center spring. */ if(!this->dwAutoCenter) { this->pes->lpVtbl->SendForceFeedbackCommand( this->pes, &this->sh, DISFFC_STOPALL); } hres = S_OK; } } else { hres = S_OK; } return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | Acquire | * * Obtains access to the device. * * Device acquisition does not reference-count. If a device is * acquired twice then unacquired once, the device is unacquired. * * Before the device can be acquired, a data format must * first be set via the * method. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The device has already been acquired. Note * that this value is a success code. * * : Access to the device was not granted. * The most common cause of this is attempting to acquire a * device with the cooperative level when * the associated window is not foreground. * * This error code is also returned if an attempt to * acquire a device in exclusive mode fails because the device * is already acquired in exclusive mode by somebody else. * * = : The device * does not have a selected data format. */ /* * The point at which we take the exclusive semaphore is important. * We should do it after preliminary validation of foreground * permission, so that we don't accidentally lock out somebody * else who legitimately has permission. * *****************************************************************************/ STDMETHODIMP CDIDev_Acquire(PV pdd _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::Acquire, (_ "p", pdd)); if(SUCCEEDED(hres = hresPvT(pdd))) { PDD this = _thisPv(pdd); /* * Must protect with the critical section to prevent somebody from * acquiring or changing the data format while we're acquiring. */ CDIDev_EnterCrit(this); /* * The app explicitly messed with acquisition. Any problems * retrieving data are now the apps' fault. */ this->hresNotAcquired = DIERR_NOTACQUIRED; //We now need a pvi even in where the device doesn't use VxDs if(this->pdix && this->pvi) { if(this->pvi->fl & VIFL_ACQUIRED) { hres = S_FALSE; } else if(SUCCEEDED(hres = CDIDev_CanAcquire(this))) { hres = Excl_Acquire(&this->guid, this->hwnd, this->discl); if(SUCCEEDED(hres)) { if(SUCCEEDED(hres = CDIDev_FFAcquire(this))) { hres = this->pdcb->lpVtbl->Acquire(this->pdcb); if(SUCCEEDED(hres)) { if(hres == S_FALSE) { hres = Hel_AcquireInstance(this->pvi); if(SUCCEEDED(hres)) { this->fAcquiredInstance = 1; /* * If relative mode, need to prime the * pvLastBuffer with the current state. */ if(this->pvi->fl & VIFL_RELATIVE) { hres = this->pdcb->lpVtbl->GetDeviceState( this->pdcb, this->pvLastBuffer); if(FAILED(hres)) { goto unacquire; } } } else { goto unacquire; } } /* * Note that InstallCwp must be the last thing * we do, because it will add us to the foreground * list, and none of our error exit paths remove us. */ hres = CDIDev_InstallCwp(this); if(SUCCEEDED(hres)) { this->fAcquired = 1; this->fOnceAcquired = 1; /* * From now on, if we lose acquisition, * it's not the app's fault. */ this->hresNotAcquired = DIERR_INPUTLOST; hres = S_OK; } else { goto unacquire; } } else { unacquire:; CDIDev_RealUnacquire(this); } } } } } else { hres = E_INVALIDARG; } CDIDev_LeaveCrit(this); } ExitOleProcR(); return hres; } #ifdef XDEBUG CSET_STUBS(Acquire, (PV pdd), (pdd THAT_)) #else #define CDIDev_AcquireA CDIDev_Acquire #define CDIDev_AcquireW CDIDev_Acquire #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | InternalUnacquire | * * This does the real work of unacquiring. The internal * version bypasses the "the app requested this" flag, * so when the app goes to request something, it gets * instead of . * * If the application error code is , then * we will also signal the associated event so that it knows * that the state changed. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * *****************************************************************************/ STDMETHODIMP CDIDev_InternalUnacquire(PDD this) { HRESULT hres; EnterProcR(IDirectInputDevice8::InternalUnacquire, (_ "p", this)); /* * Must protect with the critical section to prevent confusing other * methods which change their behavior depending on whether the device * is acquired. */ CDIDev_EnterCrit(this); if(this->fAcquired) { AssertF(this->pdcb != c_pdcbNil); this->fAcquired = 0; Excl_Unacquire(&this->guid, this->hwnd, this->discl); if(this->discl & DISCL_FOREGROUND) { AssertF(this->hwnd); CDIDev_DelForegroundDevice(this); #ifdef WINNT if( IsIconic(this->hwnd) ) { this->fUnacquiredWhenIconic = 1; } #endif /* * Prefix notices (mb:34651) that the above could release the * interface, causing the following to party on garbage but only * if we have a refcounting bug so "By design". */ } /* * ISSUE-2001/03/29-timgill multithreading means we cannot rely on Excl_Unaquire() return values * We cannot trust the return value (if we made one) * of Excl_Unacquire, because another instance may have * snuck in and acquired the device after we Excl_Unacquire'd * it and started doing force feedback on it. * * We need to fix this with the joystick mutex. */ if(this->pes && (this->discl & DISCL_EXCLUSIVE)) { this->pes->lpVtbl->SendForceFeedbackCommand( this->pes, &this->sh, DISFFC_RESET); this->sh.dwTag = 0; } hres = CDIDev_RealUnacquire(this); if(this->hresNotAcquired == DIERR_INPUTLOST) { CDIDev_SetNotifyEvent(this); } } else { hres = S_FALSE; } CDIDev_LeaveCrit(this); ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | Unacquire | * * Release access to the device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The object is not currently acquired. * This may have been caused by a prior loss of input. * Note that this is a success code. * *****************************************************************************/ STDMETHODIMP CDIDev_Unacquire(PV pdd _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::Unacquire, (_ "p", pdd)); if(SUCCEEDED(hres = hresPvT(pdd))) { PDD this = _thisPv(pdd); /* * The app explicitly messed with acquisition. Any problems * retrieving data are now the apps' fault. */ this->hresNotAcquired = DIERR_NOTACQUIRED; hres = CDIDev_InternalUnacquire(this); } ExitOleProcR(); return hres; } #ifdef XDEBUG CSET_STUBS(Unacquire, (PV pdd), (pdd THAT_)) #else #define CDIDev_UnacquireA CDIDev_Unacquire #define CDIDev_UnacquireW CDIDev_Unacquire #endif /***************************************************************************** * * @doc INTERNAL * * @method PDIPROPVALIDINFO | IDirectInputDevice | ppviFind | * * Locate the DIPROPVALIDINFO structure that describes * the predefined property. * * @parm const GUID * | pguid | * * Property guid, or predefined property. * * @returns * * Pointer to a const that describes * what is and is not valid for this property. * * Returns 0 if the property is not one of the predefined * properties. * *****************************************************************************/ #pragma BEGIN_CONST_DATA typedef struct DIPROPVALIDINFO { PCGUID pguid; /* Property name */ DWORD dwSize; /* expected size */ DWORD fl; /* flags */ } DIPROPVALIDINFO, *PDIPROPVALIDINFO; /* * Note that the flags are negative in sense. * This makes validation easier. */ #define DIPVIFL_NOTDEVICE 0x00000001 /* Cannot be device */ #define DIPVIFL_NOTOBJECT 0x00000002 /* Cannot be object */ #define DIPVIFL_READONLY 0x00000004 /* Cannot be set */ #define DIPVIFL_NOTPRIVATE 0x00000008 /* Cannot handle private pvi */ #define DIPVIFL_NOTACQUIRED 0x00000010 /* Cannot modify while acquired */ DIPROPVALIDINFO c_rgpvi[] = { { DIPROP_BUFFERSIZE, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_NOTPRIVATE | DIPVIFL_NOTACQUIRED, }, { DIPROP_AXISMODE, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_NOTPRIVATE | DIPVIFL_NOTACQUIRED, }, { DIPROP_GRANULARITY, cbX(DIPROPDWORD), DIPVIFL_NOTDEVICE | DIPVIFL_READONLY | DIPVIFL_NOTACQUIRED, }, { DIPROP_RANGE, cbX(DIPROPRANGE), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, /* * Note that you can set the dead zone on the entire device. * This is the same as applying it to each axis individually. */ { DIPROP_DEADZONE, cbX(DIPROPDWORD), DIPVIFL_NOTACQUIRED, }, /* * Note that you can set the saturation on the entire device. * This is the same as applying it to each axis individually. */ { DIPROP_SATURATION, cbX(DIPROPDWORD), DIPVIFL_NOTACQUIRED, }, /* * Note that you can change the gain either while acquired * or not. Your choice. */ { DIPROP_FFGAIN, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT, }, /* * Note that the FF load is meaningful only when acquired, * so we'd better not complain if they access it while acquired! */ { DIPROP_FFLOAD, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, { DIPROP_AUTOCENTER, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_NOTACQUIRED, }, { DIPROP_CALIBRATIONMODE, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_NOTACQUIRED, }, { DIPROP_CALIBRATION, cbX(DIPROPCAL), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_GUIDANDPATH, cbX(DIPROPGUIDANDPATH), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, { DIPROP_INSTANCENAME, cbX(DIPROPSTRING), DIPVIFL_NOTOBJECT, }, { DIPROP_PRODUCTNAME, cbX(DIPROPSTRING), DIPVIFL_NOTOBJECT, }, { DIPROP_MAXBUFFERSIZE, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_NOTPRIVATE | DIPVIFL_NOTACQUIRED, }, { DIPROP_JOYSTICKID, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_NOTACQUIRED, }, { DIPROP_GETPORTDISPLAYNAME, cbX(DIPROPSTRING), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, /* * Note that you can change the report ID while acquired * or not. Your choice. */ { DIPROP_ENABLEREPORTID, cbX(DIPROPDWORD), 0x0, }, #if 0 { DIPROP_SPECIFICCALIBRATION, cbX(DIPROPCAL), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, #endif { DIPROP_PHYSICALRANGE, cbX(DIPROPRANGE), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_LOGICALRANGE, cbX(DIPROPRANGE), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_KEYNAME, cbX(DIPROPSTRING), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_SCANCODE, cbX(DIPROPDWORD), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_APPDATA, cbX(DIPROPPOINTER), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_CPOINTS, cbX(DIPROPCPOINTS), DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED, }, { DIPROP_VIDPID, cbX(DIPROPDWORD), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, { DIPROP_USERNAME, cbX(DIPROPSTRING), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, { DIPROP_TYPENAME, cbX(DIPROPSTRING), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, { DIPROP_MAPFILE, cbX(DIPROPSTRING), DIPVIFL_NOTOBJECT | DIPVIFL_READONLY, }, }; #pragma END_CONST_DATA STDMETHODIMP_(PDIPROPVALIDINFO) CDIDev_ppviFind(PCGUID pguid) { PDIPROPVALIDINFO ppvi; UINT ipvi; for(ipvi = 0, ppvi = c_rgpvi; ipvi < cA(c_rgpvi); ipvi++, ppvi++) { if(ppvi->pguid == pguid) { goto found; } } ppvi = 0; found: return ppvi; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | hresValidProp | * * Check that the property structure makes sense. * Returns the object index for further processing. * * @parm const GUID * | pguid | * * Property guid, or predefined property. * * @parm LPCDIPROPHEADER | pdiph | * * Propery header structure. * * @parm BOOL | fWrite | * * Whether property should be validate for writing. * * @parm OUT LPDIPROPINFO | ppropi | * * Receives object index. * *****************************************************************************/ typedef BOOL (WINAPI *PFNBAD)(PCV pv, UINT cb); STDMETHODIMP CDIDev_hresValidProp(PDD this, const GUID *pguid, LPCDIPROPHEADER pdiph, BOOL fWrite, LPDIPROPINFO ppropi) { HRESULT hres; PFNBAD pfnBad; EnterProcR(IDirectInputDevice8::Get/SetProperty, (_ "pxpx", this, pguid, pdiph, fWrite)); AssertF(CDIDev_InCrit(this)); if(fWrite) { pfnBad = (PFNBAD)IsBadWritePtr; } else { pfnBad = (PFNBAD)IsBadReadPtr; } if(!pfnBad(pdiph, cbX(DIPROPHEADER)) && pdiph->dwHeaderSize == cbX(DIPROPHEADER) && pdiph->dwSize % 4 == 0 && pdiph->dwSize >= pdiph->dwHeaderSize && !pfnBad(pdiph, pdiph->dwSize)) { /* * Now convert the item descriptor into an index. */ hres = CDIDev_hresMapHow(this, pdiph->dwObj, pdiph->dwHow, ppropi); if(SUCCEEDED(hres)) { /* * Now validate the property id or guid. */ if(HIWORD((UINT_PTR)pguid) == 0) { PDIPROPVALIDINFO ppvi; ppvi = CDIDev_ppviFind(pguid); /* * Note that if we don't find the GUID in our list, * we fail it straight away. This prevents ISVs * from trying to create properties in the Microsoft * Reserved range. */ if(ppvi) { if( ppvi->pguid == DIPROP_CALIBRATION ) { if( pdiph->dwSize == ppvi->dwSize || pdiph->dwSize == cbX(DIPROPCALPOV) ) { hres = S_OK; } else { RPF("%s: Arg 2: Invalid dwSize for property", s_szProc); hres = E_INVALIDARG; } } else if( pdiph->dwSize == ppvi->dwSize ) { if( pguid == DIPROP_KEYNAME || pguid == DIPROP_SCANCODE ) { // Fix Manbug 28888. // DIPROP_KEYNAME and DIPROP_SCANCODE are not a property for device. if( pdiph->dwHow == DIPH_DEVICE ) { hres = E_INVALIDARG; } } else { if(fWrite) { ScrambleBuf((PV)(pdiph+1), pdiph->dwSize - cbX(DIPROPHEADER) ); } hres = S_OK; } } else { RPF("%s: Arg 2: Invalid dwSize for property", s_szProc); hres = E_INVALIDARG; } } else { RPF("%s: Arg 1: Unknown property", s_szProc); hres = E_NOTIMPL; } } else { hres = hresFullValidGuid(pguid, 1); } } } else { RPF("%s: Arg 2: Invalid pointer", s_szProc); hres = E_INVALIDARG; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | hresValidDefProp | * * Determine whether the property is something we can handle * in the default property handler. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the property being retrieved. * * @parm DWORD | dwFlags | * * Flags for forbidden things. * if being validated for writing. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : Passes validation. * * : Not something we handle. * * *****************************************************************************/ HRESULT INTERNAL CDIDev_hresValidDefProp(PDD this, LPCDIPROPINFO ppropi, DWORD dwFlags) { HRESULT hres; PDIPROPVALIDINFO ppvi; EnterProc(CDIDev_hresValidDefProp, (_ "pGxx", this, ppropi->pguid, ppropi->dwDevType, dwFlags)); /* * Note that it's okay if the device is acquired. We want to * allow GetProperty to succeed on an acquired device. */ AssertF(CDIDev_InCrit(this)); ppvi = CDIDev_ppviFind(ppropi->pguid); if(ppvi) { if(ppropi->iobj == 0xFFFFFFFF) { dwFlags |= DIPVIFL_NOTDEVICE; /* Fail if devices forbidden */ } else { dwFlags |= DIPVIFL_NOTOBJECT; /* Fail if objects forbidden */ } if(this->pvi == 0) { dwFlags |= DIPVIFL_NOTPRIVATE; /* Fail if privates forbidden */ } /* * If attempting to modify property and we are acquired, * then also set the "but not while acquired" filter. */ if((dwFlags & DIPVIFL_READONLY) && this->fAcquired) { dwFlags |= DIPVIFL_NOTACQUIRED; /* Fail if r/o while acq'd */ } if((ppvi->fl & dwFlags) == 0) { hres = S_OK; /* Seems reasonable */ } else { if(ppvi->fl & dwFlags & DIPVIFL_READONLY) { RPF("SetProperty: Property is read-only"); hres = DIERR_READONLY; } else if(ppvi->fl & dwFlags & DIPVIFL_NOTACQUIRED) { RPF("SetProperty: Cannot change property while acquired"); hres = DIERR_ACQUIRED; } else { RPF("Get/SetProperty: Property does not exist for that object"); hres = E_NOTIMPL; /* Cannot do that */ } } } else { RPF("Get/SetProperty: Property does not exist"); hres = E_NOTIMPL; /* Definitely way out */ } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | DefGetProperty | * * Default implementation of * to handle properties which the device decides not to implement. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the property being retrieved. * * @parm OUT LPDIPROPHEADER | pdiph | * * Where to put the property value. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * * *****************************************************************************/ STDMETHODIMP CDIDev_DefGetProperty(PDD this, LPCDIPROPINFO ppropi, LPDIPROPHEADER pdiph) { HRESULT hres; EnterProc(CDIDev_DefGetProperty, (_ "pGx", this, ppropi->pguid, ppropi->dwDevType)); AssertF(CDIDev_InCrit(this)); hres = CDIDev_hresValidDefProp(this, ppropi, 0); if(SUCCEEDED(hres)) { LPDIPROPDWORD pdipdw = (PV)pdiph; LPDIPROPRANGE pdiprg = (PV)pdiph; LPDIPROPPOINTER pdipptr = (PV)pdiph; LPDIPROPSTRING pdipwsz = (PV)pdiph; switch((DWORD)(UINT_PTR)ppropi->pguid) { case (DWORD)(UINT_PTR)DIPROP_BUFFERSIZE: AssertF(this->pvi); /* Validation should've caught this */ pdipdw->dwData = this->celtBuf; hres = S_OK; break; case (DWORD)(UINT_PTR)DIPROP_AXISMODE: AssertF(this->pvi); /* Validation should've caught this */ if(this->pvi->fl & VIFL_RELATIVE) { pdipdw->dwData = DIPROPAXISMODE_REL; } else { pdipdw->dwData = DIPROPAXISMODE_ABS; } hres = S_OK; break; case (DWORD)(UINT_PTR)DIPROP_GRANULARITY: if(DIDFT_GETTYPE(ppropi->dwDevType) & DIDFT_AXIS) { /* Default axis granularity is 1 */ pdipdw->dwData = 1; hres = S_OK; } else { /* * Buttons don't have granularity. * POVs must be handled by device driver. */ RPF("GetProperty: Object doesn't have a granularity"); hres = E_NOTIMPL; } break; case (DWORD)(UINT_PTR)DIPROP_RANGE: if(DIDFT_GETTYPE(ppropi->dwDevType) & DIDFT_RELAXIS) { /* Default rel-axis range is infinite */ pdiprg->lMin = DIPROPRANGE_NOMIN; pdiprg->lMax = DIPROPRANGE_NOMAX; hres = S_OK; } else { /* * Device driver must handle abs axis range. * Buttons and POVs don't have range. */ RPF("GetProperty: Object doesn't have a range"); hres = E_NOTIMPL; } break; case (DWORD)(UINT_PTR)DIPROP_MAXBUFFERSIZE: pdipdw->dwData = this->celtBufMax; hres = S_OK; break; case (DWORD)(UINT_PTR)DIPROP_APPDATA: if( ( this->df.rgodf[ppropi->iobj].dwType & DIDFT_CONTROLOBJS ) &&!( this->df.rgodf[ppropi->iobj].dwType & DIDFT_NODATA ) ) { if( this->pdix ) { if( this->pdix[ppropi->iobj].dwOfs != 0xFFFFFFFF ) { pdipptr->uData = this->pdix[ppropi->iobj].uAppData; } else { hres = DIERR_OBJECTNOTFOUND; } } else { RPF("GetProperty: Need data format/semantic map applied to have app data"); hres = DIERR_NOTINITIALIZED; } } else { RPF("SetProperty: app data only valid for input controls"); hres = E_NOTIMPL; } break; case (DWORD)(UINT_PTR)DIPROP_USERNAME: hres = CMap_GetDeviceUserName( &this->guid, pdipwsz->wsz ); break; case (DWORD)(UINT_PTR)DIPROP_FFGAIN: pdipdw->dwData = this->dwGain; hres = S_OK; break; case (DWORD)(UINT_PTR)DIPROP_FFLOAD: hres = CDIDev_GetLoad(this, &pdipdw->dwData); break; case (DWORD)(UINT_PTR)DIPROP_AUTOCENTER: if(this->didcFF & DIDC_FORCEFEEDBACK) { pdipdw->dwData = this->dwAutoCenter; hres = S_OK; } else { hres = E_NOTIMPL; } break; default: /* * The user is asking for some property that simply * makes no sense here. E.g., asking for the dead * zone on a keyboard. */ SquirtSqflPtszV(sqfl | sqflBenign, TEXT("GetProperty: Property 0x%08x not supported on device"), (DWORD)(UINT_PTR)ppropi->pguid ); hres = E_NOTIMPL; break; } } ExitOleProc(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | GetProperty | * * Obtain information about a device or object in a device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN REFGUID | rguidProp | * * The identity of the property to be obtained. This can be * one of the predefined values, or it may * be a private GUID. * * @parm IN LPDIPROPHEADER | pdiph | * * Points to the portion of a structure * which dependson the property. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer, or the *

field is invalid, or the

field * is not zero when

is set to . * * : The specified object does not * exist. * * = : The property * is not supported by the device or object. * * @ex * * The following "C" code fragment illustrates how to obtain * the value of the property. * * | * * DIPROPDWORD dipdw; * HRESULT hres; * dipdw.diph.dwSize = sizeof(DIPROPDWORD); * dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); * dipdw.diph.dwObj = 0; // device property * hres = IDirectInputDevice_GetProperty(pdid, DIPROP_BUFFERSIZE, &dipdw.diph); * if (SUCCEEDED(hres)) { * // dipdw.dwData contains the value of the property * } * *****************************************************************************/ STDMETHODIMP CDIDev_GetProperty(PV pdd, REFGUID rguid, LPDIPROPHEADER pdiph _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetProperty, (_ "pxp", pdd, rguid, pdiph)); if(SUCCEEDED(hres = hresPvT(pdd))) { PDD this = _thisPv(pdd); DIPROPINFO propi; /* * Must protect with the critical section to prevent somebody * acquiring or changing the property we are reading. We need * to do this before validating, to prevent an acquisition. */ CDIDev_EnterCrit(this); propi.pguid = rguid; if(SUCCEEDED(hres = CDIDev_hresValidProp(this, rguid, pdiph, 1, &propi))) { /* * Prefix picks up that ppropi->iobj is uninitialized (mb:34682) * in the case of a BY_USAGE dwHow. However, the value is only * an output for MapUsage which will always either set it or * fail so there is no error. */ hres = this->pdcb->lpVtbl->GetProperty(this->pdcb, &propi, pdiph); if(hres == E_NOTIMPL) { hres = CDIDev_DefGetProperty(this, &propi, pdiph); } } CDIDev_LeaveCrit(this); } ExitBenignOleProcR(); return hres; } #ifdef XDEBUG CSET_STUBS(GetProperty, (PV pdm, REFGUID rguid, LPDIPROPHEADER pdiph), (pdm, rguid, pdiph THAT_)) #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | SetAxisMode | * * Default handler for clients trying to set the axis mode. * If the device doesn't handle axis modes natively, then * we'll fake it ourselves. * * @parm DWORD | dwMode | * * Desired new mode. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * *****************************************************************************/ STDMETHODIMP CDIDev_SetAxisMode(PDD this, DWORD dwMode) { HRESULT hres; EnterProcR(IDirectInputDevice8::SetProperty(AXISMODE), (_ "px", this, dwMode)); AssertF(this->pvi); /* Validation should've caught this */ hres = hresFullValidFl(dwMode, DIPROPAXISMODE_VALID, 2); if(SUCCEEDED(hres)) { if(dwMode & DIPROPAXISMODE_REL) { this->GetDeviceState = CDIDev_GetRelDeviceState; this->pvi->fl |= VIFL_RELATIVE; } else { this->GetDeviceState = CDIDev_GetAbsDeviceState; this->pvi->fl &= ~VIFL_RELATIVE; } if(this->cAxes) { hres = S_OK; } else { hres = DI_PROPNOEFFECT; } } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | SetAutoCenter | * * Default handler for clients trying to set the * auto-center property. * * If the device doesn't have control over the * auto-center spring, then we fail. * * @parm DWORD | dwMode | * * Desired new mode. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * *****************************************************************************/ STDMETHODIMP CDIDev_SetAutoCenter(PDD this, DWORD dwMode) { HRESULT hres; EnterProcR(IDirectInputDevice8::SetProperty(AUTOCENTER), (_ "px", this, dwMode)); hres = hresFullValidFl(dwMode, DIPROPAUTOCENTER_VALID, 2); if(SUCCEEDED(hres)) { if(this->didcFF & DIDC_FORCEFEEDBACK) { /* * We need to create the effect driver if disabling * autocenter so that CDIDev_FFAcquire will set the feedback * mode properly. */ if(fLimpFF(dwMode == DIPROPAUTOCENTER_OFF, SUCCEEDED(hres = CDIDev_CreateEffectDriver(this)))) { this->dwAutoCenter = dwMode; hres = S_OK; } } else { hres = E_NOTIMPL; } } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | SetGlobalAxisProp | * * Default implementation of * to handle properties which can be applied globally to all * absolute axes. * * @parm IN LPDIPROPINFO | ppropi | * * Information describing the property being set. * We edit it to avoid reallocating memory all the time. * * @parm IN LPCDIPROPHEADER | pdiph | * * The property itself. * * @returns * * We consider the property-set a success if all candidates * succeeded. counts as success, on the assumption * that the property is not meaningful on the candidate. * *****************************************************************************/ STDMETHODIMP CDIDev_SetGlobalAxisProp(PDD this, LPDIPROPINFO ppropi, LPCDIPROPHEADER pdiph) { HRESULT hres; for(ppropi->iobj = 0; ppropi->iobj < this->df.dwNumObjs; ppropi->iobj++) { DWORD dwType = this->df.rgodf[ppropi->iobj].dwType; if(dwType & DIDFT_ABSAXIS) { ppropi->dwDevType = this->df.rgodf[ppropi->iobj].dwType; hres = this->pdcb->lpVtbl->SetProperty(this->pdcb, ppropi, pdiph); if(FAILED(hres) && hres != E_NOTIMPL) { goto done; } } } hres = S_OK; done:; return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | DefSetProperty | * * Default implementation of * to handle properties which the device decides not to implement. * * @parm IN LPDIPROPINFO | ppropi | * * Information describing the property being set. * We edit it to avoid reallocating memory all the time. * * @parm OUT LPCDIPROPHEADER | pdiph | * * Where to put the property value. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The device is polled, so the result * might not be meaningful. (This return code is used when * you attempt to set the buffer size property.) * * = : The *

parameter is not a valid pointer. * * *****************************************************************************/ STDMETHODIMP CDIDev_DefSetProperty(PDD this, LPDIPROPINFO ppropi, LPCDIPROPHEADER pdiph) { HRESULT hres; EnterProc(CDIDev_DefSetProperty, (_ "pGx", this, ppropi->pguid, ppropi->dwDevType)); AssertF(CDIDev_InCrit(this)); /* * Note: The indentation here is historical; I left it this way * to keep the diff size down. */ hres = CDIDev_hresValidDefProp(this, ppropi, DIPVIFL_READONLY); if(SUCCEEDED(hres)) { LPDIPROPDWORD pdipdw = (PV)pdiph; LPDIPROPRANGE pdiprg = (PV)pdiph; LPDIPROPPOINTER pdipptr = (PV)pdiph; VXDDWORDDATA vdd; switch((DWORD)(UINT_PTR)ppropi->pguid) { case (DWORD)(UINT_PTR)DIPROP_BUFFERSIZE: AssertF(this->pvi); /* Validation should've caught this */ vdd.pvi = this->pvi; if( pdipdw->dwData > this->celtBufMax ) { RPF( "DIPROP_BUFFERSIZE: requested size %d is larger than maximum %d, using %d", pdipdw->dwData, this->celtBufMax, this->celtBufMax ); vdd.dw = this->celtBufMax; } else { vdd.dw = pdipdw->dwData; } hres = Hel_SetBufferSize(&vdd); #ifdef DEBUG_STICKY { TCHAR tszDbg[80]; wsprintf( tszDbg, TEXT("SetBufferSize(0x%08x) returned 0x%08x\r\n"), vdd.dw, hres ); OutputDebugString( tszDbg ); } #endif /* DEBUG_STICKY */ if(SUCCEEDED(hres)) { this->celtBuf = pdipdw->dwData; hres = this->hresPolled; } break; case (DWORD)(UINT_PTR)DIPROP_AXISMODE: hres = CDIDev_SetAxisMode(this, pdipdw->dwData); break; /* * We will handle these global properties * if the callback doesn't want to. */ case (DWORD)(UINT_PTR)DIPROP_RANGE: case (DWORD)(UINT_PTR)DIPROP_DEADZONE: case (DWORD)(UINT_PTR)DIPROP_SATURATION: case (DWORD)(UINT_PTR)DIPROP_CALIBRATIONMODE: case (DWORD)(UINT_PTR)DIPROP_CALIBRATION: if(ppropi->dwDevType == 0) { /* For device */ hres = CDIDev_SetGlobalAxisProp(this, ppropi, pdiph); } else { goto _default; } break; case (DWORD)(UINT_PTR)DIPROP_MAXBUFFERSIZE: this->celtBufMax = pdipdw->dwData; hres = S_OK; break; case (DWORD)(UINT_PTR)DIPROP_APPDATA: if( ( this->df.rgodf[ppropi->iobj].dwType & DIDFT_CONTROLOBJS ) &&!( this->df.rgodf[ppropi->iobj].dwType & DIDFT_NODATA ) ) { if( this->pdix ) { if( this->pdix[ppropi->iobj].dwOfs != 0xFFFFFFFF ) { this->pdix[ppropi->iobj].uAppData = pdipptr->uData; } else { hres = DIERR_OBJECTNOTFOUND; } } else { RPF("SetProperty: Need data format/semantic map applied to change app data"); hres = DIERR_NOTINITIALIZED; } } else { RPF("SetProperty: app data only valid for input controls"); hres = E_NOTIMPL; } break; case (DWORD)(UINT_PTR)DIPROP_FFGAIN: if(ISVALIDGAIN(pdipdw->dwData)) { this->dwGain = pdipdw->dwData; CDIDev_RefreshGain(this); hres = S_OK; } else { RPF("ERROR: SetProperty(DIPROP_FFGAIN): Gain out of range"); hres = E_INVALIDARG; } break; case (DWORD)(UINT_PTR)DIPROP_AUTOCENTER: hres = CDIDev_SetAutoCenter(this, pdipdw->dwData); break; _default:; default: /* * The validation filter already failed invalid properties. * So what's left is that the property is valid but cannot * be set, because it doesn't exist on the device (e.g., * dead zone) or because it is read-only. */ SquirtSqflPtszV(sqfl | sqflBenign, TEXT("SetProperty: Property 0x%08x not supported on device"), (DWORD)(UINT_PTR)ppropi->pguid ); hres = E_NOTIMPL; break; } } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | RealSetProperty | * * The function that does the real work. * * will internally * set the axis mode property, so it needs this backdoor * entry point. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN REFGUID | rguidProp | * * The identity of the property to be set. * * @parm IN LPDIPROPHEADER | pdiph | * * Points to the portion of a structure * which depends on the property. * *****************************************************************************/ STDMETHODIMP CDIDev_RealSetProperty(PDD this, REFGUID rguid, LPCDIPROPHEADER pdiph) { HRESULT hres; DIPROPINFO propi; EnterProcR(IDirectInputDevice8::SetProperty, (_ "pxp", this, rguid, pdiph)); /* * Must protect with the critical section to prevent somebody * acquiring or changing the property we are reading. We need * to do this before validating, to prevent an acquisition. */ CDIDev_EnterCrit(this); propi.pguid = rguid; if(SUCCEEDED(hres = CDIDev_hresValidProp(this, rguid, pdiph, 0, &propi))) { hres = this->pdcb->lpVtbl->SetProperty(this->pdcb, &propi, pdiph); if(hres == E_NOTIMPL) { hres = CDIDev_DefSetProperty(this, &propi, pdiph); } } CDIDev_LeaveCrit(this); ExitOleProc(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | SetProperty | * * Set information about a device or object in a device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN REFGUID | rguidProp | * * The identity of the property to be set. This can be * one of the predefined values, or it may * be a pointer to a private GUID. * * @parm IN LPDIPROPHEADER | pdiph | * * Points to the portion of a structure * which depends on the property. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The operation completed * successfully but * had no effect. For example, changing the axis mode * on a device with no axes will return this value. * * = : The *

parameter is not a valid pointer, or the *

field is invalid, or the

field * is not zero when

is set to . * * : The specified object does not * exist. * * = : The property * is not supported by the device or object. * *****************************************************************************/ STDMETHODIMP CDIDev_SetProperty(PV pdd, REFGUID rguid, LPCDIPROPHEADER pdiph _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::SetProperty, (_ "pxp", pdd, rguid, pdiph)); if(SUCCEEDED(hres = hresPvT(pdd))) { PDD this = _thisPv(pdd); hres = CDIDev_RealSetProperty(this, rguid, pdiph); } ExitOleProcR(); return hres; } #ifdef XDEBUG CSET_STUBS(SetProperty, (PV pdm, REFGUID rguid, LPCDIPROPHEADER pdiph), (pdm, rguid, pdiph THAT_)) #else #define CDIDev_SetPropertyA CDIDev_SetProperty #define CDIDev_SetPropertyW CDIDev_SetProperty #endif /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | SetCooperativeLevel | * * Establish the cooperativity level for the instance of * the device. * * The cooperativity level determines how the instance of * the device interacts with other instances of the device * and the rest of the system. * * Note that if the system mouse is acquired in exclusive * mode, then the mouse cursor will be removed from the screen * until the device is unacquired. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm HWND | hwnd | * * The window associated with the device. * The window must be a top-level window. * * It is an error to destroy the window while it is still * active in a DirectInput device. * * @parm DWORD | dwFlags | * * Flags which describe the cooperativity level associated * with the device. * * It consists of flags, documented separately. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * *****************************************************************************/ HRESULT INLINE CDIDev_SetCooperativeLevel_IsValidFl(DWORD dwFlags) { HRESULT hres; RD(static char s_szProc[] = "IDirectInputDevice::SetCooperativeLevel"); if(!(dwFlags & ~DISCL_VALID)) { if((dwFlags & DISCL_EXCLMASK) == DISCL_EXCLUSIVE || (dwFlags & DISCL_EXCLMASK) == DISCL_NONEXCLUSIVE) { if((dwFlags & DISCL_GROUNDMASK) == DISCL_FOREGROUND || (dwFlags & DISCL_GROUNDMASK) == DISCL_BACKGROUND) { hres = S_OK; } else { RPF("ERROR %s: arg %d: Must set exactly one of " "DISCL_FOREGROUND or DISCL_BACKGROUND", s_szProc, 2); hres = E_INVALIDARG; } } else { RPF("ERROR %s: arg %d: Must set exactly one of " "DISCL_EXCLUSIVE or DISCL_NONEXCLUSIVE", s_szProc, 2); hres = E_INVALIDARG; } } else { RPF("ERROR %s: arg %d: invalid flags", s_szProc, 2); hres = E_INVALIDARG; } return hres; } HRESULT INLINE CDIDev_SetCooperativeLevel_IsValidHwnd(HWND hwnd, DWORD dwFlags) { HRESULT hres; RD(static char s_szProc[] = "IDirectInputDevice::SetCooperativeLevel"); /* * If a window handle is passed, it must be valid. * * The window must be a top-level window to be activated. * * The window must belong to the calling process so we can * hook it. */ if(hwnd) { hres = hresFullValidHwnd(hwnd, 1); if(SUCCEEDED(hres)) { if(!(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)) { if(GetWindowPid(hwnd) == GetCurrentProcessId()) { } else { RPF("ERROR %s: window must belong to current process", s_szProc); hres = E_HANDLE; } } else { RPF("ERROR %s: window may not be a child window", s_szProc); hres = E_HANDLE; goto done; } } else { goto done; } } /* * Foreground mode or exclusive mode both require a window handle. */ if(dwFlags & (DISCL_FOREGROUND | DISCL_EXCLUSIVE)) { if(hwnd) { } else { RPF("ERROR %s: window handle required " "if DISCL_EXCLUSIVE or DISCL_FOREGROUND", s_szProc); hres = E_HANDLE; goto done; } } hres = S_OK; done:; return hres; } STDMETHODIMP CDIDev_SetCooperativeLevel(PV pdd, HWND hwnd, DWORD dwFlags _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::SetCooperativeLevel, (_ "pxx", pdd, hwnd, dwFlags)); if(SUCCEEDED(hres = hresPvT(pdd))) { PDD this = _thisPv(pdd); /* * Must protect with the critical section to prevent somebody * acquiring or Reset()ing behind our back. */ CDIDev_EnterCrit(this); if(SUCCEEDED(hres = IDirectInputDevice_NotAcquired(this)) && SUCCEEDED(hres = CDIDev_SetCooperativeLevel_IsValidFl(dwFlags)) && SUCCEEDED(hres = CDIDev_SetCooperativeLevel_IsValidHwnd(hwnd, dwFlags))) { AssertF(CDIDev_IsConsistent(this)); if( SUCCEEDED( hres ) ) { hres = this->pdcb->lpVtbl->SetCooperativeLevel( this->pdcb, hwnd, dwFlags); if(SUCCEEDED(hres)) { this->discl = dwFlags; this->hwnd = hwnd; if(this->pvi) { this->pvi->hwnd = hwnd; } } } else { AssertF( hres == E_INVALIDARG ); RPF("ERROR %s: arg %d: invalid flags", s_szProc, 2); } AssertF(CDIDev_IsConsistent(this)); } CDIDev_LeaveCrit(this); } ExitOleProcR(); return hres; } #ifdef XDEBUG CSET_STUBS(SetCooperativeLevel, (PV pdm, HWND hwnd, DWORD fl), (pdm, hwnd, fl THAT_)) #else #define CDIDev_SetCooperativeLevelA CDIDev_SetCooperativeLevel #define CDIDev_SetCooperativeLevelW CDIDev_SetCooperativeLevel #endif /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | RunControlPanel | * * Run the DirectInput control panel for the device. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN HWND | hwndOwner | * * Identifies the window handle that will be used as the * parent window for subsequent UI. NULL is a valid parameter, * indicating that there is no parent window. * * @parm DWORD | dwFlags | * * No flags are currently defined. This parameter "must" be * zero. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The device is attached. * * @devnote * * The

is eventually going to allow * to request a modal control panel. * *****************************************************************************/ STDMETHODIMP CDIDev_RunControlPanel(PV pdd, HWND hwndOwner, DWORD fl _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::RunControlPanel, (_ "pxx", pdd, hwndOwner, fl)); if(SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = hresFullValidHwnd0(hwndOwner, 1)) && SUCCEEDED(hres = hresFullValidFl(fl, DIRCP_VALID, 2))) { PDD this = _thisPv(pdd); IDirectInputDeviceCallback *pdcb; /* * Must protect with the critical section to prevent somebody * Reset()ing behind our back. However, we cannot hold the * critical section during the control panel callback, because * that will yield. * * So we copy/addref the pdcb inside the critical section, * then run the control panel outside the critical section, * then release the pdcb when we're finally done. */ CDIDev_EnterCrit(this); pdcb = this->pdcb; OLE_AddRef(pdcb); CDIDev_LeaveCrit(this); hres = pdcb->lpVtbl->RunControlPanel(pdcb, hwndOwner, fl); OLE_Release(pdcb); } ExitOleProc(); return hres; } #ifdef XDEBUG CSET_STUBS(RunControlPanel, (PV pdd, HWND hwndOwner, DWORD fl), (pdd, hwndOwner, fl THAT_)) #else #define CDIDev_RunControlPanelA CDIDev_RunControlPanel #define CDIDev_RunControlPanelW CDIDev_RunControlPanel #endif /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | Initialize | * * Initialize a DirectInputDevice object. * * Note that if this method fails, the underlying object should * be considered to be an an indeterminate state and needs to * be reinitialized before it can be subsequently used. * * The method automatically * initializes the device after creating it. Applications * normally do not need to call this function. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN HINSTANCE | hinst | * * Instance handle of the application or DLL that is creating * the DirectInput object. * * See the section titled "Initialization and Versions" * for more information. * * @parm DWORD | dwVersion | * * Version number of the dinput.h header file that was used. * * See the section titled "Initialization and Versions" * for more information. * * @parm IN REFGUID | rguid | * * Identifies the instance of the device for which the interface * should be associated. * The method * can be used to determine which instance GUIDs are supported by * the system. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The device had already been initialized with * the instance GUID passed in

. * * : The device cannot be initialized while * it is acquired. * * : The instance GUID does not exist * on the current machine. * * : * The device cannot be reinitialized because there are * still effects attached to it. * *****************************************************************************/ STDMETHODIMP CDIDev_Initialize(PV pdd, HINSTANCE hinst, DWORD dwVersion, REFGUID rguid _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::Initialize, (_ "pxxG", pdd, hinst, dwVersion, rguid)); if(SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = hresFullValidGuid(rguid, 1))) { PDD this = _thisPv(pdd); CREATEDCB CreateDcb; IDirectInputDeviceCallback *pdcb; /* * Must take the critical section to avoid Reset()ing * the device (or generally speaking, modifying the * internal state variables) while somebody else is * messing with it. */ CDIDev_EnterCrit(this); if(SUCCEEDED(hres = hresValidInstanceVer(hinst, dwVersion)) && SUCCEEDED(hres = hresFindInstanceGUID(rguid, &CreateDcb, 1)) && SUCCEEDED(hres = CDIDev_Reset(this))) { hres = CreateDcb(0, rguid, &IID_IDirectInputDeviceCallback, (PPV)&pdcb); if(SUCCEEDED(hres)) { this->pdcb = pdcb; AssertF(this->pvi == 0); if(SUCCEEDED(hres = CDIDev_GetDataFormat(this)) && SUCCEEDED(hres = CDIDev_GetPolled(this)) && SUCCEEDED(hres = this->pdcb->lpVtbl->GetInstance( this->pdcb, &this->pvi)) && SUCCEEDED(hres = CMapShep_New(NULL, &IID_IDirectInputMapShepherd, &this->pMS)) && SUCCEEDED(hres = CMap_InitializeCRCTable()) && SUCCEEDED(hres = CDIDev_GetCapabilitiesHelper(this)) ) { this->dwVersion = dwVersion; /* * Take a copy of the global app hacks here rather than * using the globals everywhere to make it easier to get * rid of the globals some fine day. */ this->diHacks = g_AppHacks; this->pdcb->lpVtbl->SetDIData(this->pdcb, dwVersion, &this->diHacks); this->guid = *rguid; if(this->pvi && (this->pvi->fl & VIFL_EMULATED)) { this->pvi->pdd = this; } hres = this->pdcb->lpVtbl->CookDeviceData(this->pdcb, 0, 0 ); if(SUCCEEDED(hres)) { this->fCook = 1; } CDIDev_InitFF(this); hres = S_OK; } else { RPF("Device driver didn't provide a data format"); } } else { #ifdef NOISY RPF("Cannot create device"); #endif } } CDIDev_LeaveCrit(this); } ExitOleProc(); return hres; } #ifdef XDEBUG CSET_STUBS(Initialize, (PV pdd, HINSTANCE hinst, DWORD dwVersion, REFGUID rguid), (pdd, hinst, dwVersion, rguid THAT_)) #else #define CDIDev_InitializeA CDIDev_Initialize #define CDIDev_InitializeW CDIDev_Initialize #endif /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputDevice | Init | * * Initialize the internal parts of the DirectInputDevice object. * *****************************************************************************/ void INLINE CDIDev_Init(PDD this) { /* * The critical section must be the very first thing we do, * because only Finalize checks for its existence. * * (We might be finalized without being initialized if the user * passed a bogus interface to CDIDev_New.) */ this->fCritInited = fInitializeCriticalSection(&this->crst); if( this->fCritInited ) { this->celtBufMax = DEVICE_MAXBUFFERSIZE; /* Default maximum buffer size */ this->pdcb = c_pdcbNil; GPA_InitFromZero(&this->gpaEff); } } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputDevice | New | * * Create a new DirectInputDevice object, uninitialized. * * @parm IN PUNK | punkOuter | * * Controlling unknown for aggregation. * * @parm IN RIID | riid | * * Desired interface to new object. * * @parm OUT PPV | ppvObj | * * Output pointer for new object. * *****************************************************************************/ STDMETHODIMP CDIDev_New(PUNK punkOuter, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcR(IDirectInputDevice8::, (_ "Gp", riid, punkOuter)); if (SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 3))) { hres = Excl_Init(); if(SUCCEEDED(hres)) { LPVOID pvTry = NULL; hres = Common_NewRiid(CDIDev, punkOuter, riid, &pvTry); if(SUCCEEDED(hres)) { PDD this = _thisPv(pvTry); CDIDev_Init(this); if( this->fCritInited ) { *ppvObj = pvTry; } else { Common_Unhold(this); *ppvObj = NULL; hres = E_OUTOFMEMORY; } } } } ExitOleProcPpvR(ppvObj); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIDev | CDIDev_ModifyEffectParams | * * Modifies parameters of DIEFFECT structure to fit the current FF device * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * * @parm IN OUT LPDIEFFECT | peff | * * Pointer to the effect structure * * @parm IN GUID | effGUID | * * GUID for the effect * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The effect can't be supported by the current device * (e.g. the number of FF axes on the device is 0) * * : Can't create the effect even with the modified parameters * *****************************************************************************/ HRESULT CDIDev_ModifyEffectParams ( PV pdd, LPDIEFFECT peff, GUID effGUID ) { HRESULT hres = S_OK; HRESULT hresCreate = S_OK; LPDIRECTINPUTEFFECT pdeff; PDD this = _thisPv(pdd); EnterProcR(CDIDev_ModifyEffectParams, (_ "p", pdd)); /* * To make sure that effects we enumerate will actually get * created on the device, try creating the effect. * * PREFIX warns (Win:171786) that pdeff is uninitialized when * CDIDev_CreateEffect tests that the pointer to it is writable. */ #ifdef XDEBUG hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL, ((LPUNKNOWN)this)->lpVtbl); #else hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL); #endif if(SUCCEEDED(hresCreate)) { Invoke_Release(&pdeff); } else { if (hresCreate == DIERR_INVALIDPARAM) { //two things can give DIERR_INVALIDPARAM: //invalid axes and invalid trigger button //check the axes first, then the trigger buttons //try to eliminate all DIERR_INVALIDPARAMS LPDIOBJECTDATAFORMAT lpObjDat = this->df.rgodf; DWORD dwNum = this->df.dwNumObjs; DWORD nCount; LPDWORD lpAxes; LPDWORD lpThisAxis; LPDWORD lpEffAxis; DWORD nAxes = 0; DWORD dwTrigger = DIJOFS_BUTTON(0); BOOL bTriggerCorrect = FALSE; AllocCbPpv(sizeof(DWORD)*dwNum, &lpAxes); lpThisAxis = lpAxes; for (nCount = 0; nCount < dwNum; nCount ++) { AssertF(lpObjDat != NULL); //check the axes if ((lpObjDat->dwType & (DIDFT_AXIS | DIDFT_FFACTUATOR) & DIDFT_TYPEMASK) && (fHasAllBitsFlFl(lpObjDat->dwType, (DIDFT_AXIS | DIDFT_FFACTUATOR) & DIDFT_ATTRMASK))) { *lpAxes = lpObjDat->dwOfs; nAxes++; lpAxes++; } else { //check the trigger button, if there's one if ((peff->dwTriggerButton != DIEB_NOTRIGGER) && (lpObjDat->dwType & DIDFT_FFEFFECTTRIGGER & DIDFT_TYPEMASK) && (fHasAllBitsFlFl(lpObjDat->dwType, DIDFT_FFEFFECTTRIGGER & DIDFT_ATTRMASK))) { if (lpObjDat->dwOfs == peff->dwTriggerButton) { //the trigger is valid bTriggerCorrect = TRUE; } else { //remember the trigger offset for the future dwTrigger = lpObjDat->dwOfs; } } } lpObjDat++; } //first, chack if there are any FF axes if (nAxes == 0) { //return an error if no FF axes on device hres = DIERR_UNSUPPORTED; } else { //trigger buttons are checked for validity before axes, //so set the trigger button, if needed, //because if it is invalid, this is what caused the error if ((peff->dwTriggerButton != DIEB_NOTRIGGER) && (bTriggerCorrect == FALSE)) { peff->dwTriggerButton = dwTrigger; // and try creating again #ifdef XDEBUG hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL, ((LPUNKNOWN)this)->lpVtbl); #else hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL); #endif if(SUCCEEDED(hresCreate)) { Invoke_Release(&pdeff); } } if (hresCreate == DIERR_INVALIDPARAM) { HRESULT hresInfo = S_OK; EFFECTMAPINFO emi; //this time, set the axes if (peff->cAxes > nAxes) { //change the number of axes peff->cAxes = nAxes; //change the flags if ((nAxes < 3) && (peff->dwFlags & DIEFF_SPHERICAL)) { peff->dwFlags &= ~DIEFF_SPHERICAL; peff->dwFlags |= DIEFF_POLAR; } else { if ((nAxes < 2) && (peff->dwFlags & DIEFF_POLAR)) { peff->dwFlags &= ~DIEFF_POLAR; peff->dwFlags |= DIEFF_CARTESIAN; } } } //check if size of type-specific param structures is not bigger then number of axes, //since this can also give us invalid params in type-specific . //need to do this only for conditions if (SUCCEEDED(hresInfo = CDIDev_FindEffectGUID(this, &effGUID, &emi, 2))) { //do the conditions if (emi.attr.dwEffType & DIEFT_CONDITION) { if (peff->cbTypeSpecificParams/(sizeof(DICONDITION)) > peff->cAxes) { peff->cbTypeSpecificParams = peff->cAxes*(sizeof(DICONDITION)); } } //don't need to do anything for custom forces, //since DInput doesn't check number of channels against number of axes anyway } //write over the axes lpEffAxis = peff->rgdwAxes; for (nCount = 0; nCount < nAxes, nCount < peff->cAxes; nCount ++) { *(lpEffAxis) = *(lpThisAxis); lpThisAxis ++; lpEffAxis++; } // and try creating again #ifdef XDEBUG hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL, ((LPUNKNOWN)this)->lpVtbl); #else hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL); #endif if(SUCCEEDED(hresCreate)) { Invoke_Release(&pdeff); } } } //free the axes array FreePpv(&lpAxes); } } if ((SUCCEEDED(hres)) && (hresCreate == DIERR_INVALIDPARAM)) { hres = hresCreate; } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method BOOL | CDIDev | CDIDev_IsStandardEffect | * * Checks if the effect GUID belongs to a standard DI effect * * @parm IN GUID | effGUID | * * GUID for the effect * * @returns BOOL * * TRUE if it is a standard DI effect; * FALSE otherwise. * * *****************************************************************************/ BOOL CDIDev_IsStandardEffect (GUID effGUID) { BOOL bStandard = TRUE; //check all the standard DX7 GUIDs if ((IsEqualGUID(&effGUID, &GUID_Sine)) || (IsEqualGUID(&effGUID, &GUID_Triangle)) || (IsEqualGUID(&effGUID, &GUID_ConstantForce)) || (IsEqualGUID(&effGUID, &GUID_RampForce)) || (IsEqualGUID(&effGUID, &GUID_Square)) || (IsEqualGUID(&effGUID, &GUID_SawtoothUp)) || (IsEqualGUID(&effGUID, &GUID_SawtoothDown)) || (IsEqualGUID(&effGUID, &GUID_Spring)) || (IsEqualGUID(&effGUID, &GUID_Damper)) || (IsEqualGUID(&effGUID, &GUID_Inertia)) || (IsEqualGUID(&effGUID, &GUID_Friction)) || (IsEqualGUID(&effGUID, &GUID_CustomForce))) { bStandard = TRUE; } else { bStandard = FALSE; } return bStandard; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | EnumEffectsInFile | * * Enumerates DIEFFECT struct(s) and effect GUID from file. * An application can use this in order to create pre-authored * force effects. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm LPCSTR | lpszFileName | * * Name of the RIFF file that contains collection of effects. * * @parm IN OUT LPENUMEFFECTSCALLBACK | pec | * * The callback function. * * @parm IN OUT LPVOID | pvRef | * Specifies the application-defined value given in the * function. * * @parm IN DWORD | dwFlags | * * Flags which control the enumeration. * * It consists of flags, documented separately. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

or *

parameter is invalid. * * @cb BOOL CALLBACK | DIEnumEffectsCallback | * * An application-defined callback function that receives * effect GUID, DIEFFECT and repeat count as a result of a call to the * method. * * @parm OUT LPCDIFILEEFFECT | lpDiFileEf | * * Pointer to a DIFILEEFFECT structure. * * * @parm IN OUT LPVOID | pvRef | * Specifies the application-defined value given in the * function. * * @returns * * Returns to continue the enumeration * or to stop the enumeration. * *****************************************************************************/ HRESULT CDIDev_EnumEffectsInFileA ( PV pddA, LPCSTR lpszFileName, LPDIENUMEFFECTSINFILECALLBACK pec, LPVOID pvRef, DWORD dwFlags ) { HRESULT hres = E_FAIL; EnterProcR(IDirectInputDevice8::EnumEffectsInFile, (_ "s", lpszFileName)); /* Validate incoming parameters */ if(SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidReadStrA(lpszFileName, MAX_JOYSTRING,1)) && SUCCEEDED(hres = hresFullValidPfn(pec, 2)) && SUCCEEDED(hres = hresFullValidFl(dwFlags, DIFEF_ENUMVALID, 3)) ) { PDD this = _thisPvNm(pddA, ddA); HMMIO hmmio; MMCKINFO mmck; DWORD dwEffectSz; hres = RIFF_Open(lpszFileName, MMIO_READ | MMIO_ALLOCBUF , &hmmio, &mmck, &dwEffectSz); if(SUCCEEDED(hres)) { HRESULT hresRead; DIEFFECT effect; DIFILEEFFECT DiFileEf; DIENVELOPE diEnvelope; DWORD rgdwAxes[DIEFFECT_MAXAXES]; LONG rglDirection[DIEFFECT_MAXAXES]; effect.rgdwAxes = rgdwAxes; effect.rglDirection = rglDirection; effect.lpEnvelope = &diEnvelope; DiFileEf.dwSize = cbX(DiFileEf); DiFileEf.lpDiEffect = &effect; while ((SUCCEEDED(hres)) && (SUCCEEDED(hresRead = RIFF_ReadEffect(hmmio, &DiFileEf)))) { BOOL fRc = DIENUM_CONTINUE; BOOL bInclude = TRUE; HRESULT hresModify = DI_OK; //modify if needed if (dwFlags & DIFEF_MODIFYIFNEEDED) { hresModify = CDIDev_ModifyEffectParams(this, &effect, DiFileEf.GuidEffect); } //if necessary, check whether effect is standard if (!(dwFlags & DIFEF_INCLUDENONSTANDARD)) { bInclude = CDIDev_IsStandardEffect(DiFileEf.GuidEffect); } //call back only if all the conditions posed by the flags are satisfied if ((SUCCEEDED(hresModify)) && (bInclude == TRUE)) { fRc = Callback(pec, &DiFileEf, pvRef); } //free type-specific only if allocated if(effect.cbTypeSpecificParams > 0) { FreePv(effect.lpvTypeSpecificParams); effect.cbTypeSpecificParams = 0x0; } if(fRc == DIENUM_STOP) { break; } else if(fRc == DIENUM_CONTINUE) { continue; } else { RPF("IDirectInputDevice::EnumEffectsInFile: Invalid return value from enumeration callback"); ValidationException(); break; } } RIFF_Close(hmmio, 0); //if hresRead failed because couldn't descend into the chunk, it means the end of file, //so everything is OK; //else return this error if (SUCCEEDED(hres)) { if (hresRead == hresLe(ERROR_SECTOR_NOT_FOUND)) { hres = S_OK; } else { hres = hresRead; } } } } ExitOleProc(); return hres; } HRESULT CDIDev_EnumEffectsInFileW ( PV pddW, LPCWSTR lpszFileName, LPDIENUMEFFECTSINFILECALLBACK pec, LPVOID pvRef, DWORD dwFlags ) { HRESULT hres = E_FAIL; EnterProcR(IDirectInputDevice8::EnumEffectsInFileW, (_ "s", lpszFileName)); /* Validate incoming parameters */ if(SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidReadStrW(lpszFileName, MAX_JOYSTRING,1)) && SUCCEEDED(hres = hresFullValidPfn(pec, 2)) && SUCCEEDED(hres = hresFullValidFl(dwFlags, DIFEF_ENUMVALID, 3)) ) { CHAR szFileName[MAX_PATH]; PDD this = _thisPvNm(pddW, ddW); UToA(szFileName, MAX_PATH, lpszFileName); hres = CDIDev_EnumEffectsInFileA(&this->ddA, szFileName, pec, pvRef, dwFlags); } return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice | WriteEffectToFile | * * Writes DIEFFECT struct(s) and effect GUID to a file. * An application can use this in order to create pre-authored * force effects. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm LPCSTR | lpszFileName | * * Name of the RIFF file that contains collection of effects. * * @parm IN DWORD | dwEntries | * * Number of structures in the array. * * @parm IN LPCDIFILEEFFECT | rgDiFileEft | * * Array of structure. * * * @parm IN DWORD | dwFlags | * * Flags which control how the effect should be written. * * It consists of flags, documented separately. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

or *

parameter is invalid. * *****************************************************************************/ HRESULT CDIDev_WriteEffectToFileA ( PV pddA, LPCSTR lpszFileName, DWORD dwEntries, LPDIFILEEFFECT rgDiFileEffect, DWORD dwFlags ) { HRESULT hres = E_NOTIMPL; EnterProcR(IDirectInputDevice8::WriteEffectToFileA, (_ "s", lpszFileName)); /* Validate incoming parameters */ if(SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidReadStrA(lpszFileName, MAX_JOYSTRING,1))&& SUCCEEDED(hres = hresFullValidFl(dwFlags, DIFEF_ENUMVALID, 3)) && SUCCEEDED(hres = (IsBadReadPtr(rgDiFileEffect, cbX(*rgDiFileEffect))) ? E_POINTER : S_OK)) { PDD this = _thisPvNm(pddA, ddA); HMMIO hmmio; MMCKINFO mmck; DWORD dwEffectSz; hres = RIFF_Open(lpszFileName, MMIO_CREATE | MMIO_WRITE | MMIO_ALLOCBUF , &hmmio, &mmck, &dwEffectSz); if(SUCCEEDED(hres)) { UINT nCount; LPDIFILEEFFECT lpDiFileEf = rgDiFileEffect; //write out the effects for(nCount = 0; nCount < dwEntries; nCount++) { BOOL bInclude = TRUE; hres = (IsBadReadPtr(lpDiFileEf, cbX(*lpDiFileEf))) ? E_POINTER : S_OK; if (FAILED(hres)) { break; } //if necessary, check whether the effect is standard if (!(dwFlags & DIFEF_INCLUDENONSTANDARD)) { bInclude = CDIDev_IsStandardEffect(lpDiFileEf->GuidEffect); } if ((SUCCEEDED(hres)) && (bInclude == TRUE)) { hres = RIFF_WriteEffect(hmmio, lpDiFileEf); } if(FAILED(hres)) { break; } lpDiFileEf++; } RIFF_Close(hmmio, 0); } } ExitOleProc(); return hres; } HRESULT CDIDev_WriteEffectToFileW ( PV pddW, LPCWSTR lpszFileName, DWORD dwEntries, LPDIFILEEFFECT lpDiFileEffect, DWORD dwFlags ) { HRESULT hres = E_FAIL; EnterProcR(IDirectInputDevice8::WriteEffectToFile, (_ "s", lpszFileName)); /* Validate incoming parameters */ if(SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidReadStrW(lpszFileName, MAX_JOYSTRING,1))) { CHAR szFileName[MAX_PATH]; PDD this = _thisPvNm(pddW, ddW); UToA(szFileName, MAX_PATH, lpszFileName); hres = CDIDev_WriteEffectToFileA(&this->ddA, szFileName, dwEntries, lpDiFileEffect, dwFlags); } return hres; } /***************************************************************************** * * The long-awaited vtbls and templates * *****************************************************************************/ #pragma BEGIN_CONST_DATA #define CDIDev_Signature 0x20564544 /* "DEV " */ Primary_Interface_Begin(CDIDev, TFORM(ThisInterfaceT)) TFORM(CDIDev_GetCapabilities), TFORM(CDIDev_EnumObjects), TFORM(CDIDev_GetProperty), TFORM(CDIDev_SetProperty), TFORM(CDIDev_Acquire), TFORM(CDIDev_Unacquire), TFORM(CDIDev_GetDeviceState), TFORM(CDIDev_GetDeviceData), TFORM(CDIDev_SetDataFormat), TFORM(CDIDev_SetEventNotification), TFORM(CDIDev_SetCooperativeLevel), TFORM(CDIDev_GetObjectInfo), TFORM(CDIDev_GetDeviceInfo), TFORM(CDIDev_RunControlPanel), TFORM(CDIDev_Initialize), TFORM(CDIDev_CreateEffect), TFORM(CDIDev_EnumEffects), TFORM(CDIDev_GetEffectInfo), TFORM(CDIDev_GetForceFeedbackState), TFORM(CDIDev_SendForceFeedbackCommand), TFORM(CDIDev_EnumCreatedEffectObjects), TFORM(CDIDev_Escape), TFORM(CDIDev_Poll), TFORM(CDIDev_SendDeviceData), TFORM(CDIDev_EnumEffectsInFile), TFORM(CDIDev_WriteEffectToFile), TFORM(CDIDev_BuildActionMap), TFORM(CDIDev_SetActionMap), TFORM(CDIDev_GetImageInfo), Primary_Interface_End(CDIDev, TFORM(ThisInterfaceT)) Secondary_Interface_Begin(CDIDev, SFORM(ThisInterfaceT), SFORM(dd)) SFORM(CDIDev_GetCapabilities), SFORM(CDIDev_EnumObjects), SFORM(CDIDev_GetProperty), SFORM(CDIDev_SetProperty), SFORM(CDIDev_Acquire), SFORM(CDIDev_Unacquire), SFORM(CDIDev_GetDeviceState), SFORM(CDIDev_GetDeviceData), SFORM(CDIDev_SetDataFormat), SFORM(CDIDev_SetEventNotification), SFORM(CDIDev_SetCooperativeLevel), SFORM(CDIDev_GetObjectInfo), SFORM(CDIDev_GetDeviceInfo), SFORM(CDIDev_RunControlPanel), SFORM(CDIDev_Initialize), SFORM(CDIDev_CreateEffect), SFORM(CDIDev_EnumEffects), SFORM(CDIDev_GetEffectInfo), SFORM(CDIDev_GetForceFeedbackState), SFORM(CDIDev_SendForceFeedbackCommand), SFORM(CDIDev_EnumCreatedEffectObjects), TFORM(CDIDev_Escape), SFORM(CDIDev_Poll), SFORM(CDIDev_SendDeviceData), SFORM(CDIDev_EnumEffectsInFile), SFORM(CDIDev_WriteEffectToFile), SFORM(CDIDev_BuildActionMap), SFORM(CDIDev_SetActionMap), SFORM(CDIDev_GetImageInfo), Secondary_Interface_End(CDIDev, SFORM(ThisInterfaceT), SFORM(dd))