Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3490 lines
108 KiB

/*****************************************************************************
*
* DIObj.c
*
* Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* The IDirectInput main interface.
*
* Contents:
*
* CDIObj_New
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflDi
#define DIDEVTYPE_DEVICE_ORDER 1
#define DIDEVTYPE_HID_MOUSE_ORDER 2
#define DIDEVTYPE_HID_KEYBOARD_ORDER 3
#define DIDEVTYPE_MOUSE_ORDER 4
#define DIDEVTYPE_KEYBOARD_ORDER 5
#define DIDEVTYPE_SUPPLEMENTAL_ORDER 6
#define MAX_ORDER (DI8DEVTYPE_MAX - DI8DEVTYPE_MIN + DIDEVTYPE_SUPPLEMENTAL_ORDER + 2)
#define INVALID_ORDER (MAX_ORDER + 1)
#define MAX_DEVICENUM 32
DIORDERDEV g_DiDevices[MAX_DEVICENUM]; //all devices that are attached
int g_nCurDev;
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct CDIObj |
*
* The <i IDirectInput> object, from which other things come.
*
* The A and W versions are simply alternate interfaces on the same
* underlying object.
*
* There really isn't anything interesting in the structure
* itself.
*
*
* @field IDirectInputA | diA |
*
* ANSI DirectInput object (containing vtbl).
*
* @field IDirectInputW | diW |
*
* UNICODE DirectInput object (containing vtbl).
*
* @field IDirectInputJoyConfig *| pdjc |
*
* Aggregated joystick configuration interface (if created).
*
* @field BOOL | fCritInited:1 |
*
* Set if the critical section has been initialized.
*
* @field CRITICAL_SECTION | crst |
*
* Critical section that guards thread-sensitive data.
*****************************************************************************/
typedef struct CDIObj
{
/* Supported interfaces */
TFORM(IDirectInput8) TFORM(di);
SFORM(IDirectInput8) SFORM(di);
DWORD dwVersion;
IDirectInputJoyConfig *pdjc;
BOOL fCritInited:1;
CRITICAL_SECTION crst;
} CDIObj, DDI, *PDDI;
#define ThisClass CDIObj
#define ThisInterface TFORM(IDirectInput8)
#define ThisInterfaceA IDirectInput8A
#define ThisInterfaceW IDirectInput8W
#define ThisInterfaceT IDirectInput8
/*****************************************************************************
*
* Declare the interfaces we will be providing.
*
*****************************************************************************/
Primary_Interface(CDIObj, TFORM(ThisInterfaceT));
Secondary_Interface(CDIObj, SFORM(ThisInterfaceT));
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | QueryInterface |
*
* Gives a client access to other interfaces on an object.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @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 <mf IUnknown::QueryInterface>.
*
*//**************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | AddRef |
*
* Increments the reference count for the interface.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::AddRef>.
*
*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | 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 LPDIRECTINPUT | lpDirectInput
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::Release>.
*
*****************************************************************************/
#ifdef DEBUG
Default_QueryInterface(CDIObj)
Default_AddRef(CDIObj)
Default_Release(CDIObj)
#else
#define CDIObj_QueryInterface Common_QueryInterface
#define CDIObj_AddRef Common_AddRef
#define CDIObj_Release Common_Release
#endif
#define CDIObj_AppFinalize Common_AppFinalize
/*****************************************************************************
*
* @doc INTERNAL
*
* @mfunc void | CDIObj | EnterCrit |
*
* Enter the object critical section.
*
* @doc INTERNAL
*
* @mfunc void | CDIObj | LeaveCrit |
*
* Leave the object critical section.
*
*****************************************************************************/
void INLINE
CDIObj_EnterCrit(PDDI this)
{
EnterCriticalSection(&this->crst);
}
void INLINE
CDIObj_LeaveCrit(PDDI this)
{
LeaveCriticalSection(&this->crst);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @mfunc HRESULT | IDirectInput | QIHelper |
*
* We will dynamically create <i IDirectInputJoyConfig>
* and aggregate it with us.
*
* @parm IN REFIID | riid |
*
* The requested interface's IID.
*
* @parm OUT LPVOID * | ppvObj |
*
* Receives a pointer to the obtained interfacethis->pdix[iobj].dwOfs.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_QIHelper(PDDI this, RIID riid, PPV ppvObj)
{
HRESULT hres;
EnterProcI(CDIObj_QIHelper, (_ "pG", this, riid));
if ( IsEqualIID(riid, &IID_IDirectInputJoyConfig8) )
{
*ppvObj = 0; /* In case the New fails */
CDIObj_EnterCrit(this);
if ( this->pdjc == 0 )
{
hres = CJoyCfg_New((PUNK)this, &IID_IUnknown, (PPV)&this->pdjc);
} else
{
hres = S_OK;
}
CDIObj_LeaveCrit(this);
if ( SUCCEEDED(hres) )
{
/*
* This QI will addref us if it succeeds.
*/
hres = OLE_QueryInterface(this->pdjc, riid, ppvObj);
} else
{
this->pdjc = 0;
}
} else
{
hres = Common_QIHelper(this, riid, ppvObj);
}
ExitOleProcPpv(ppvObj);
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CDIObj_Finalize |
*
* Clean up our instance data.
*
* @parm PV | pvObj |
*
* Object being released. Note that it may not have been
* completely initialized, so everything should be done
* carefully.
*
*****************************************************************************/
void INTERNAL
CDIObj_Finalize(PV pvObj)
{
PDDI this = pvObj;
Invoke_Release(&this->pdjc);
if ( this->fCritInited )
{
DeleteCriticalSection(&this->crst);
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInput | CreateDeviceHelper |
*
* Creates and initializes an instance of a device which is
* specified by the GUID and IID.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm IN PCGUID | pguid |
*
* See <mf IDirectInput::CreateDevice>.
*
* @parm OUT PPV | ppvObj |
*
* See <mf IDirectInput::CreateDevice>.
*
* @parm IN LPUNKNOWN | punkOuter |
*
* See <mf IDirectInput::CreateDevice>.
*
* @parm IN RIID | riid |
*
* The interface the application wants to create. This will
* be either <i IDirectInputDeviceA> or <i IDirectInputDeviceW>.
* If the object is aggregated, then this parameter is ignored.
*
* @returns
*
* Returns a COM error code.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_CreateDeviceHelper(PDDI this, PCGUID pguid, PPV ppvObj,
PUNK punkOuter, RIID riid)
{
HRESULT hres;
EnterProc(CDIObj_CreateDeviceHelper,
(_ "pGxG", this, pguid, punkOuter, riid));
/*
* CDIDev_New will validate the punkOuter and ppvObj.
*
* IDirectInputDevice_Initialize will validate the pguid.
*
* riid is known good (since it came from CDIObj_CreateDeviceW
* or CDIObj_CreateDeviceA).
*/
hres = CDIDev_New(punkOuter, punkOuter ? &IID_IUnknown : riid, ppvObj);
if ( SUCCEEDED(hres) && punkOuter == 0 )
{
PDID pdid = *ppvObj;
hres = IDirectInputDevice_Initialize(pdid, g_hinst,
this->dwVersion, pguid);
if ( SUCCEEDED(hres) )
{
} else
{
Invoke_Release(ppvObj);
}
}
ExitOleProcPpv(ppvObj);
return(hres);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | CreateDevice |
*
* Creates and initializes an instance of a device which is
* specified by the GUID and IID.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm REFGUID | rguid |
* Identifies the instance of the
* device for which the indicated interface
* is requested. The <mf IDirectInput::EnumDevices> method
* can be used to determine which instance GUIDs are supported by
* the system.
*
* @parm OUT LPDIRECTINPUTDEVICE * | lplpDirectInputDevice |
* Points to where to return
* the pointer to the <i IDirectInputDevice> interface, if successful.
*
* @parm IN LPUNKNOWN | punkOuter | Pointer to controlling unknown
* for OLE aggregation, or 0 if the interface is not aggregated.
* Most callers will pass 0.
*
* @comm Calling this function with <p punkOuter> = NULL
* is equivalent to creating the object via
* <f CoCreateInstance>(&CLSID_DirectInputDevice, NULL,
* CLSCTX_INPROC_SERVER, <p riid>, <p lplpDirectInputDevice>);
* then initializing it with <f Initialize>.
*
* Calling this function with <p punkOuter> != NULL
* is equivalent to creating the object via
* <f CoCreateInstance>(&CLSID_DirectInputDevice, <p punkOuter>,
* CLSCTX_INPROC_SERVER, &IID_IUnknown, <p lplpDirectInputDevice>).
* The aggregated object must be initialized manually.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p ppvOut> parameter is not a valid pointer.
*
* <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>:
* Out of memory.
*
* <c DIERR_NOINTERFACE> = <c E_NOINTERFACE>
* The specified interface is not supported by the object.
*
* <c DIERR_DEVICENOTREG> = The device instance does not
* correspond to a device that is registered with DirectInput.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_CreateDeviceW(PV pdiW, REFGUID rguid, PPDIDW ppdidW, PUNK punkOuter)
{
HRESULT hres;
EnterProcR(IDirectInput8::CreateDevice,
(_ "pGp", pdiW, rguid, punkOuter));
if ( SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW)) )
{
PDDI this = _thisPvNm(pdiW, diW);
hres = CDIObj_CreateDeviceHelper(this, rguid, (PPV)ppdidW,
punkOuter, &IID_IDirectInputDevice8W);
}
ExitOleProcPpv(ppdidW);
return(hres);
}
STDMETHODIMP
CDIObj_CreateDeviceA(PV pdiA, REFGUID rguid, PPDIDA ppdidA, PUNK punkOuter)
{
HRESULT hres;
EnterProcR(IDirectInput8::CreateDevice,
(_ "pGp", pdiA, rguid, punkOuter));
if ( SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA)) )
{
PDDI this = _thisPvNm(pdiA, diA);
hres = CDIObj_CreateDeviceHelper(this, rguid, (PPV)ppdidA,
punkOuter, &IID_IDirectInputDevice8A);
}
ExitOleProcPpv(ppdidA);
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIObj_TestDeviceFlags |
*
* Determines whether the device matches the specified flags.
* Phantom devices are treated as not really there.
*
* @parm PDIDW | pdidW |
*
* Device to be queried.
*
* @parm DWORD | edfl |
*
* Enumeration flags. It is one or more <c DIEDFL_*> values.
*
* The bits in the enumeration flags are in two categories.*
*
* Normal flags are the ones whose presence requires that
* the corresponding bit in the device flags also be set.
*
* Inverted flags (<c DIEDFL_INCLUDEMASK>) are the ones whose
* absence requires that the corresponding bit in the device
* flags also be absent.
*
* By inverting the inclusion flags in both the enumeration
* flags and the actual device flags, and then treating the
* whole thing as a bunch of normal flags, we get the desired
* behavior for the inclusion flags.
*
* @returns
*
* <c S_OK> if the device meets the criteria.
*
* <c S_FALSE> if the device does not meet the criteria.
* Note that <mf DirectInput::GetDeviceStatus> relies on
* this specific return value.
*
* Other error code as appropriate.
*
*****************************************************************************/
HRESULT EXTERNAL
CDIObj_TestDeviceFlags(PDIDW pdidW, DWORD edfl)
{
HRESULT hres;
DIDEVCAPS_DX3 dc;
EnterProcI(CDIObj_TestDeviceFlags, (_ "px", pdidW, edfl));
/*
* 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.
*/
dc.dwSize = cbX(dc);
hres = IDirectInputDevice_GetCapabilities(pdidW, (PV)&dc);
AssertF(dc.dwSize == cbX(dc));
CAssertF(DIEDFL_ATTACHEDONLY == DIDC_ATTACHED);
CAssertF(DIEDFL_FORCEFEEDBACK == DIDC_FORCEFEEDBACK);
CAssertF(DIEDFL_INCLUDEALIASES == DIDC_ALIAS);
CAssertF(DIEDFL_INCLUDEPHANTOMS == DIDC_PHANTOM);
CAssertF(DIEDFL_INCLUDEHIDDEN == DIDC_HIDDEN);
if ( SUCCEEDED(hres) )
{
if ( fHasAllBitsFlFl(dc.dwFlags ^ DIEDFL_INCLUDEMASK,
edfl ^ DIEDFL_INCLUDEMASK) )
{
hres = S_OK;
} else
{
/*
* Note: DX3 and DX5 returned E_DEVICENOTREG for
* phantom devices. Now we return S_FALSE. Let's
* hope nobody gets upset.
*/
hres = S_FALSE;
}
}
ExitOleProc();
return(hres);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | EnumDevices |
*
* Enumerates the DirectInput devices that are attached to
* or could be attached to the computer.
*
* For example, an external game port may support a joystick
* or a steering wheel, but only one can be plugged in at a
* time. <mf IDirectInput::EnumDevices> will enumerate both
* devices.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm DWORD | dwDevType |
*
* Device type filter. If 0, then all device types are
* enumerated. Otherwise, it is either a <c DIDEVCLASS_*> value,
* indicating the device class that should be enumerated or a
* <c DIDEVTYPE_*> value, indicating the device type that should be
* enumerated.
*
* @parm LPDIENUMDEVICESCALLBACK | lpCallback |
* Points to an application-defined callback function.
* For more information, see the description of the
* <f DIEnumDevicesProc> callback function.
*
* @parm IN LPVOID | pvRef |
* Specifies a 32-bit application-defined
* value to be passed to the callback function. This value
* may be any 32-bit value; it is prototyped as an <t LPVOID>
* for convenience.
*
* @parm DWORD | fl |
* Optional flags which control the enumeration. The
* following flags are defined and may be combined.
*
* <c DIEDFL_ATTACHEDONLY>: Enumerate only attached devices.
*
* <c DIEDFL_FORCEFEEDBACK>: Enumerate only devices which
* support force feedback. This flag is new for DirectX 5.0.
*
* <c DIEDFL_INCLUDEALIASES>: Include alias devices in the
* enumeration. If this flag is not specified, then devices
* which are aliases of other devices (indicated by the
* <c DIDC_ALIAS> flag in the <e DIDEVCAPS.dwFlags> field
* of the <t DIDEVCAPS> structure) will be excluded from
* the enumeration. This flag is new for DirectX 5.0a.
*
* <c DIEDFL_INCLUDEPHANTOMS>: Include phantom devices in the
* enumeration. If this flag is not specified, then devices
* which are phantoms (indicated by the
* <c DIDC_PHANTOM> flag in the <e DIDEVCAPS.dwFlags> field
* of the <t DIDEVCAPS> structure) will be excluded from
* the enumeration. This flag is new for DirectX 5.0a.
*
* The default is
* <c DIEDFL_ALLDEVICES>: Enumerate all installed devices.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
* Note that if the callback stops the enumeration prematurely,
* the enumeration is considered to have succeeded.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p fl> parameter contains invalid flags, or the callback
* procedure returned an invalid status code.
*
* @cb BOOL CALLBACK | DIEnumDevicesProc |
*
* An application-defined callback function that receives
* DirectInput devices as a result of a call to the
* <om IDirectInput::EnumDevices> method.
*
* @parm IN LPDIDEVICEINSTANCE | lpddi |
*
* Structure that describes the device instance.
*
*
* @parm IN OUT LPVOID | pvRef |
* Specifies the application-defined value given in the
* <mf IDirectInput::EnumDevices> function.
*
* @returns
*
* Returns <c DIENUM_CONTINUE> to continue the enumeration
* or <c DIENUM_STOP> to stop the enumeration.
*
*//**************************************************************************
*
* In DEBUG/RDEBUG, if the callback returns a bogus value, raise
* a validation exception.
*
*****************************************************************************/
HRESULT INLINE
CDIObj_EnumDevices_IsValidTypeFilter(DWORD dwDevType)
{
HRESULT hres;
/*
* First make sure the type mask is okay.
*/
if( ( GET_DIDEVICE_TYPE( dwDevType ) < DI8DEVCLASS_MAX )
|| ( ( GET_DIDEVICE_TYPE( dwDevType ) >= DI8DEVTYPE_MIN )
&& ( GET_DIDEVICE_TYPE( dwDevType ) < DI8DEVTYPE_MAX ) ) )
{
/*
* Now make sure attribute masks are okay.
*/
if ( dwDevType & DIDEVTYPE_ENUMMASK & ~DIDEVTYPE_ENUMVALID )
{
RPF("IDirectInput::EnumDevices: Invalid dwDevType");
hres = E_INVALIDARG;
} else
{
hres = S_OK;
}
} else
{
RPF("IDirectInput::EnumDevices: Invalid dwDevType");
hres = E_INVALIDARG;
}
return(hres);
}
STDMETHODIMP
CDIObj_EnumDevicesW(PV pdiW, DWORD dwDevType,
LPDIENUMDEVICESCALLBACKW pec, LPVOID pvRef, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInput8::EnumDevices,
(_ "pxppx", pdiW, dwDevType, pec, pvRef, fl));
if ( SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW)) &&
SUCCEEDED(hres = hresFullValidPfn(pec, 2)) &&
SUCCEEDED(hres = CDIObj_EnumDevices_IsValidTypeFilter(dwDevType)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIEDFL_VALID, 4)) )
{
PDDI this = _thisPvNm(pdiW, diW);
if ( SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion)) )
{
CDIDEnum *pde;
hres = CDIDEnum_New(&this->diW, dwDevType, fl, this->dwVersion, &pde);
if ( SUCCEEDED(hres) )
{
DIDEVICEINSTANCEW ddiW;
ddiW.dwSize = cbX(ddiW);
while ( (hres = CDIDEnum_Next(pde, &ddiW)) == S_OK )
{
BOOL fRc;
/*
* WARNING! "goto" here! Make sure that nothing
* is held while we call the callback.
*/
fRc = Callback(pec, &ddiW, pvRef);
switch ( fRc )
{
case DIENUM_STOP: goto enumdoneok;
case DIENUM_CONTINUE: break;
default:
RPF("%s: Invalid return value from callback", s_szProc);
ValidationException();
break;
}
}
AssertF(hres == S_FALSE);
enumdoneok:;
CDIDEnum_Release(pde);
hres = S_OK;
}
}
}
ExitOleProcR();
return(hres);
}
BOOL INTERNAL CDIObj_InternalDeviceEnumProcW(LPDIDEVICEINSTANCEW pddiW, LPDIRECTINPUTDEVICE8W pdid8W, LPVOID pv);
STDMETHODIMP
CDIObj_InternalEnumDevicesW(PV pdiW, DWORD dwDevType, LPVOID pvRef, DWORD fl)
{
HRESULT hres;
CDIDEnum *pde;
PDDI this = _thisPvNm(pdiW, diW);
hres = CDIDEnum_New(&this->diW, dwDevType, fl, this->dwVersion, &pde);
if ( SUCCEEDED(hres) )
{
DIDEVICEINSTANCEW ddiW;
LPDIRECTINPUTDEVICE8W pdid8W;
ddiW.dwSize = cbX(ddiW);
while ( (hres = CDIDEnum_InternalNext(pde, &ddiW, &pdid8W)) == S_OK )
{
BOOL fRc;
fRc = CDIObj_InternalDeviceEnumProcW(&ddiW, pdid8W, pvRef);
switch ( fRc )
{
case DIENUM_STOP: goto enumdoneok;
case DIENUM_CONTINUE: break;
default:
ValidationException();
break;
}
}
AssertF(hres == S_FALSE);
enumdoneok:;
CDIDEnum_Release(pde);
hres = S_OK;
}
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIObj_EnumDevicesCallbackA |
*
* Wrapper function for <mf IDirectInput::EnumDevices>
* which translates the UNICODE parameters to ANSI.
*
* @parm IN LPCDIDECICEINSTANCEW | pdiW |
*
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm IN OUT PV | pvRef |
*
* Pointer to <t struct ENUMDEVICESINFO> which describes
* the original callback.
*
* @returns
*
* Returns whatever the original callback returned.
*
*****************************************************************************/
typedef struct ENUMDEVICESINFO
{
LPDIENUMDEVICESCALLBACKA pecA;
PV pvRef;
} ENUMDEVICESINFO, *PENUMDEVICESINFO;
BOOL CALLBACK
CDIObj_EnumDevicesCallback(LPCDIDEVICEINSTANCEW pdiW, PV pvRef)
{
PENUMDEVICESINFO pedi = pvRef;
BOOL fRc;
DIDEVICEINSTANCEA diA;
EnterProc(CDIObj_EnumDevicesCallback,
(_ "GGxWWp", &pdiW->guidInstance, &pdiW->guidProduct,
&pdiW->dwDevType,
pdiW->tszProductName, pdiW->tszInstanceName,
pvRef));
diA.dwSize = cbX(diA);
DeviceInfoWToA(&diA, pdiW);
fRc = pedi->pecA(&diA, pedi->pvRef);
ExitProcX(fRc);
return(fRc);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputA | EnumDevices |
*
* ANSI version of <mf IDirectInput::EnumDevices>.
* We wrap the operation.
*
* @parm IN LPGUID | lpGUIDDeviceType |
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm LPDIENUMDEVICESCALLBACKA | lpCallbackA |
* Same as <mf IDirectInput::EnumDevices>, except ANSI.
*
* @parm IN LPVOID | pvRef |
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm DWORD | fl |
* Same as <mf IDirectInput::EnumDevices>.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_EnumDevicesA(PV pdiA, DWORD dwDevType,
LPDIENUMDEVICESCALLBACKA pec, LPVOID pvRef, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInput8::EnumDevices,
(_ "pxppx", pdiA, dwDevType, pec, pvRef, fl));
/*
* EnumDevicesW will validate the rest.
*/
if ( SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA)) &&
SUCCEEDED(hres = hresFullValidPfn(pec, 1)) )
{
ENUMDEVICESINFO edi = { pec, pvRef};
PDDI this = _thisPvNm(pdiA, diA);
hres = CDIObj_EnumDevicesW(&this->diW, dwDevType,
CDIObj_EnumDevicesCallback, &edi, fl);
}
ExitOleProcR();
return(hres);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | GetDeviceStatus |
*
* Determine whether a device is currently attached.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm REFGUID | rguid |
*
* Identifies the instance of the
* device whose status is being checked.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The device is attached.
*
* <c DI_NOTATTACHED> = <c S_FALSE>: The device is not
* attached.
*
* <c E_FAIL>: DirectInput could not determine
* whether the device is attached.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* device does not exist.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_GetDeviceStatus(PV pdi, REFGUID rguid _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput8::GetDeviceStatus, (_ "pG", pdi, rguid));
if ( SUCCEEDED(hres = hresPvT(pdi)) )
{
PDDI this = _thisPv(pdi);
PDIDW pdidW;
hres = IDirectInput_CreateDevice(&this->diW, rguid, (PV)&pdidW, 0);
if ( SUCCEEDED(hres) )
{
hres = CDIObj_TestDeviceFlags(pdidW, DIEDFL_ATTACHEDONLY);
OLE_Release(pdidW);
}
}
ExitOleProc();
return(hres);
}
#ifdef XDEBUG
CSET_STUBS(GetDeviceStatus, (PV pdi, REFGUID rguid), (pdi, rguid THAT_))
#else
#define CDIObj_GetDeviceStatusA CDIObj_GetDeviceStatus
#define CDIObj_GetDeviceStatusW CDIObj_GetDeviceStatus
#endif
#ifdef DO_THE_IMPOSSIBLE
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | SetAttachedDevice |
*
* Informs DirectInput that a new device has been attached
* to the system by the user. This is useful when an application
* asks the user to attach a currently installed device but does
* not want to launch the DirectInput control panel.
*
* DirectInput needs to be informed that the device has
* been attached for internal bookkeeping purposes.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm IN LPDIRECTINPUTDEVICE | lpDIDevice |
*
* Identifies the device which has been attached.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The device is attached.
*
* @devnote
*
* This method is not implemented in the current release
* of DirectInput.
*
* This won't work. We need to receive a port, too.
* And how can the app create a <p lpDIDevice> in the
* first place for a device that does not exist?
* I guess I just don't understand.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_SetAttachedDevice(PV pdi, PV pdid _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput8::SetAttachedDevice, (_ "pp", pdi, pdid));
if ( SUCCEEDED(hres = hresPvT(pdi)) )
{
PDDI this = _thisPv(pdi);
hres = E_NOTIMPL;
}
ExitOleProc();
return(hres);
}
#ifdef XDEBUG
CSET_STUBS(SetAttachedDevice, (PV pdi, PV pdid), (pdi, pdid THAT_))
#else
#define CDIObj_SetAttachedDeviceA CDIObj_SetAttachedDevice
#define CDIObj_SetAttachedDeviceW CDIObj_SetAttachedDevice
#endif
#endif
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | RunControlPanel |
*
* Run the DirectInput control panel so that the user can
* install a new input device or modify the setup.
*
* This function will not run third-party control panels.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @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.
*
* <c DI_OK> = <c S_OK>: The device is attached.
*
* @devnote
*
* The <p dwFlags> is eventually going to allow
* <c DIRCP_MODAL> to request a modal control panel.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
STDMETHODIMP
CDIObj_RunControlPanel(PV pdi, HWND hwndOwner, DWORD fl _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput8::RunControlPanel, (_ "pxx", pdi, hwndOwner, fl));
if ( SUCCEEDED(hres = hresPvT(pdi)) &&
SUCCEEDED(hres = hresFullValidHwnd0(hwndOwner, 1)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIRCP_VALID, 2)) )
{
PDDI this = _thisPv(pdi);
if ( SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion)) )
{
/*
* We used to run "directx.cpl,@0,3" but directx.cpl is not
* redistributable; it comes only with the SDK. So we just
* run the system control panel.
*/
hres = hresRunControlPanel(TEXT(""));
}
}
ExitOleProc();
return(hres);
}
#ifdef XDEBUG
CSET_STUBS(RunControlPanel, (PV pdi, HWND hwndOwner, DWORD fl),
(pdi, hwndOwner, fl THAT_))
#else
#define CDIObj_RunControlPanelA CDIObj_RunControlPanel
#define CDIObj_RunControlPanelW CDIObj_RunControlPanel
#endif
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | Initialize |
*
* Initialize a DirectInput object.
*
* The <f DirectInputCreate> method automatically
* initializes the DirectInput object device after creating it.
* Applications normally do not need to call this function.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @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.
*
* @returns
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The device is attached.
*
* <c DIERR_DIERR_OLDDIRECTINPUTVERSION>: The application
* requires a newer version of DirectInput.
*
* <c DIERR_DIERR_BETADIRECTINPUTVERSION>: The application
* was written for an unsupported prerelease version
* of DirectInput.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_Initialize(PV pdi, HINSTANCE hinst, DWORD dwVersion _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput8::Initialize, (_ "pxx", pdi, hinst, dwVersion));
if ( SUCCEEDED(hres = hresPvT(pdi)) )
{
PDDI this = _thisPv(pdi);
if ( SUCCEEDED(hres = hresValidInstanceVer(hinst, dwVersion)) )
{
this->dwVersion = dwVersion;
}
}
#ifndef DX_FINAL_RELEASE
{
#pragma message("BETA EXPIRATION TIME BOMB! Remove for final build!")
SYSTEMTIME st;
GetSystemTime(&st);
if ( st.wYear > DX_EXPIRE_YEAR ||
((st.wYear == DX_EXPIRE_YEAR) && (MAKELONG(st.wDay, st.wMonth) > MAKELONG(DX_EXPIRE_DAY, DX_EXPIRE_MONTH)))
) {
MessageBox(0, DX_EXPIRE_TEXT,
TEXT("Microsoft DirectInput"), MB_OK);
}
}
#endif
ExitOleProc();
return(hres);
}
#ifdef XDEBUG
CSET_STUBS(Initialize, (PV pdi, HINSTANCE hinst, DWORD dwVersion),
(pdi, hinst, dwVersion THAT_))
#else
#define CDIObj_InitializeA CDIObj_Initialize
#define CDIObj_InitializeW CDIObj_Initialize
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIObj_FindDeviceInternal |
*
* The worker function for
* <mf IDirectInput2::FindDevice> which works only for HID devices.
*
* For more details, see <mf IDirectInput2::FindDevice>.
*
* @parm LPCTSTR | ptszName |
*
* The name of the device relative to the class <t GUID>.
*
* @parm OUT LPGUID | pguidOut |
*
* Pointer to a <t GUID> which receives the instance
* <t GUID> for the device, if the device is found.
*
*****************************************************************************/
HRESULT EXTERNAL
CDIObj_FindDeviceInternal(LPCTSTR ptszName, LPGUID pguidOut)
{
HRESULT hres;
/*
* Look twice. If it's not found the first time,
* then refresh the cache and try again in case
* it was for a device that was recently added.
* (In fact, it will likely be a device that was
* recently added, because FindDevice is usually
* called in response to a Plug and Play event.)
*/
hres = hresFindHIDDeviceInterface(ptszName, pguidOut);
if ( FAILED(hres) )
{
DIHid_BuildHidList(TRUE);
hres = hresFindHIDDeviceInterface(ptszName, pguidOut);
}
return(hres);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput2 | FindDevice |
*
* Obtain the instance <t GUID> for a device given
* its class <t GUID> and an opaque name.
*
* This method can be used by applications which register
* for Plug and Play notifications and are notified by
* Plug and Play that a new device has been added
* to the system. The Plug and Play notification will
* be in the form of a class <t GUID> and a device name.
* The application can pass the <t GUID> and name to
* this method to obtain the instance <t GUID> for
* the device, which can then be passed to
* <mf IDirectInput::CreateDevice> or
* <mf IDirectInput::GetDeviceStatus>.
*
* @cwrap LPDIRECTINPUT2 | lpDirectInput2
*
* @parm REFGUID | rguidClass |
*
* Class <t GUID> identifying the device class
* for the device the application wishes to locate.
*
* The application obtains the class <t GUID> from the
* Plug and Play device arrival notification.
*
* @parm LPCTSTR | ptszName |
*
* The name of the device relative to the class <t GUID>.
*
* The application obtains the class name from the
* Plug and Play device arrival notification.
*
* @parm OUT LPGUID | pguidInstance |
*
* Pointer to a <t GUID> which receives the instance
* <t GUID> for the device, if the device is found.
*
* @returns
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The device was found, and its
* instance <t GUID> has been stored in <p pguidInstance>.
*
* <c DIERR_DEVICENOTREG> = The <t GUID> and name do not
* correspond to a device that is registered with DirectInput.
* For example, they may refer to a storage device rather
* than an input device.
*
*****************************************************************************/
#define cchNameMax MAX_PATH
STDMETHODIMP
TFORM(CDIObj_FindDevice)(PV pdiT, REFGUID rguid,
LPCTSTR ptszName, LPGUID pguidOut)
{
HRESULT hres;
EnterProcR(IDirectInput8::FindDevice,
(_ "pGs", pdiT, rguid, ptszName));
if ( SUCCEEDED(hres = TFORM(hresPv)(pdiT)) &&
SUCCEEDED(hres = hresFullValidGuid(rguid, 1)) &&
SUCCEEDED(hres = TFORM(hresFullValidReadStr)(ptszName,
cchNameMax, 2)) &&
SUCCEEDED(hres = hresFullValidWritePvCb(pguidOut, cbX(GUID), 3)) )
{
if ( IsEqualIID(rguid, &GUID_HIDClass) )
{
hres = CDIObj_FindDeviceInternal(ptszName, pguidOut);
} else
{
hres = DIERR_DEVICENOTREG;
}
}
ExitOleProc();
return(hres);
}
STDMETHODIMP
SFORM(CDIObj_FindDevice)(PV pdiS, REFGUID rguid,
LPCSSTR psszName, LPGUID pguidOut)
{
HRESULT hres;
TCHAR tsz[cchNameMax];
EnterProcR(IDirectInput8::FindDevice,
(_ "pGS", pdiS, rguid, psszName));
/*
* TFORM(CDIObj_FindDevice) will validate the rguid and pguidOut.
*/
if ( SUCCEEDED(hres = SFORM(hresPv)(pdiS)) &&
SUCCEEDED(hres = SFORM(hresFullValidReadStr)(psszName, cA(tsz), 2)) )
{
PDDI this = _thisPvNm(pdiS, SFORM(di));
SToT(tsz, cA(tsz), psszName);
hres = TFORM(CDIObj_FindDevice)(&this->TFORM(di), rguid, tsz, pguidOut);
}
ExitOleProc();
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @mfunc HRESULT | IDirectInput | New |
*
* Create a new instance of an IDirectInput object.
*
* @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.
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_New(PUNK punkOuter, RIID riid, PPV ppvObj)
{
HRESULT hres;
EnterProcR(IDirectInput8::CreateInstance, (_ "Gp", riid, ppvObj));
hres = Excl_Init();
if ( SUCCEEDED(hres) )
{
/*
* Note that we cannot use Common_NewRiid for an object
* that aggregates other interfaces!
*
* The reason is that Common_NewRiid will perform
* a QI as part of the initialization, but we cannot handle
* the QI until after we've been initialized and are
* ready to mess with aggregated goo.
*/
if ( SUCCEEDED(hres = hresFullValidRiid(riid, 2)) )
{
if ( fLimpFF(punkOuter, IsEqualIID(riid, &IID_IUnknown)) )
{
hres = Common_New(CDIObj, punkOuter, ppvObj);
if ( SUCCEEDED(hres) )
{
PDDI this = _thisPv(*ppvObj);
this->fCritInited = fInitializeCriticalSection(&this->crst);
if ( this->fCritInited )
{
/*
* Only after the object is ready do we QI for the
* requested interface. And the reason is that the
* QI might cause us to create an aggregated buddy,
* which we can't do until we've been initialized.
*
* Don't do this extra QI if we are ourselves aggregated,
* or we will end up giving the wrong punk to the caller!
*/
if ( punkOuter == 0 )
{
hres = OLE_QueryInterface(this, riid, ppvObj);
OLE_Release(this);
}
if ( FAILED(hres) )
{
Invoke_Release(ppvObj);
}
} else
{
Common_Unhold(this);
*ppvObj = NULL;
hres = E_OUTOFMEMORY;
}
}
} else
{
RPF("CreateDevice: IID must be IID_IUnknown if created for aggregation");
*ppvObj = 0;
hres = CLASS_E_NOAGGREGATION;
}
}
}
ExitOleProcPpvR(ppvObj);
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIObj_IsDeviceUsedByUser |
*
* To Test whether the device is used by the user as specified by pdm->lpszUserName.
*
* @parm IN LPDIDEVICEINSTANCEW | pddiW |
*
* Device Instance.
*
* @parm IN LPDIMAPPER | pdm |
*
* pointer to DIMAPPER structure
*
* @returns
*
* TRUE: device is used by the user
* FALSE: otherwise
*
*****************************************************************************/
BOOL INTERNAL
CDIObj_IsDeviceUsedByUser( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm )
{
HRESULT hres;
BOOL fRtn = FALSE;
WCHAR wszUserName[UNLEN+1];
hres = CMap_GetDeviceUserName( &pddiW->guidInstance, wszUserName );
if( hres == S_OK ) {
DWORD dwLen = 0;
dwLen = lstrlenW(pdm->lpszUserName);
if(memcmp(pdm->lpszUserName, wszUserName, dwLen*2) == 0)
{
fRtn = TRUE;
}
}
return(fRtn);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIObj_IsDeviceAvailable |
*
* To Test whether the device is still available.
*
* @parm IN LPDIDEVICEINSTANCEW | pddiW |
*
* Device Instance.
*
* @parm IN LPDIMAPPER | pdm |
*
* pointer to DIMAPPER structure
*
* @returns
*
* TRUE: device is available
* FALSE: not available
*
*****************************************************************************/
BOOL INTERNAL
CDIObj_IsDeviceAvailable( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm )
{
HRESULT hres;
BOOL fAvailable = FALSE;
WCHAR wszUserName[UNLEN+1];
hres = CMap_GetDeviceUserName( &pddiW->guidInstance, wszUserName );
if( hres != S_OK ) {
fAvailable = TRUE;
}
return(fAvailable);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIObj_IsUserConfigured |
*
* To Test whether the device has been configured by user.
*
* @parm IN LPDIDEVICEINSTANCEW | pddiW |
*
* Device Instance.
*
* @parm IN LPDIMAPPER | pdm |
*
* pointer to DIMAPPER structure
*
* @returns
*
* TRUE: user configured
* FALSE: not configured
*
*****************************************************************************/
BOOL INTERNAL
CDIObj_IsUserConfigured( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm )
{
LPDIACTIONFORMATW pdiaf = pdm->pDiActionFormat;
BOOL fConfigured = FALSE;
DWORD i;
for ( i=0; i<pdiaf->dwNumActions; i++ )
{
if ( IsEqualGUID(&pdiaf->rgoAction[i].guidInstance, &pddiW->guidInstance) )
{
fConfigured = (pdiaf->rgoAction[i].dwHow & DIAH_USERCONFIG) ? TRUE: FALSE;
break;
}
}
return(fConfigured);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func int | CDIObj_GetMappedActionNum |
*
* Get the number of the actions which have been mapped to controls.
*
* @parm IN LPDIDEVICEINSTANCEW | pddiW |
*
* Device Instance.
*
* @parm IN LPDIMAPPER | pdm |
*
* Pointer to DIMAPPER structure
*
* @returns
*
* Number of mapped actions
*
*****************************************************************************/
int INTERNAL
CDIObj_GetMappedActionNum( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm )
{
LPDIACTIONFORMATW pdiaf = pdm->pDiActionFormat;
DWORD i;
int num = 0;
for ( i=0; i<pdiaf->dwNumActions; i++ )
{
if ( IsEqualGUID(&pdiaf->rgoAction[i].guidInstance, &pddiW->guidInstance) )
{
if ( pdiaf->rgoAction[i].dwHow & DIAH_MAPMASK )
{
num++;
}
}
}
return(num);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func DWORD | CDIObj_GetMappedPriorities |
*
* Get the priorities of the mapeed actions.
*
* @parm IN LPDIDEVICEINSTANCEW | pddiW |
*
* Device Instance.
*
* @parm IN LPDIMAPPER | pdm |
*
* Pointer to DIMAPPER structure
*
* @returns
*
* The priorities of mapped actions. Can be DIEDBS_MAPPEDPRI1,
* DIEDBS_MAPPEDPRI2, or the OR of both.
*
*****************************************************************************/
DWORD INTERNAL
CDIObj_GetMappedPriorities( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm )
{
LPDIACTIONFORMATW pdiaf = pdm->pDiActionFormat;
DWORD i;
DWORD pri = 0;
for ( i=0; i<pdiaf->dwNumActions; i++ )
{
if( pri == ( DIEDBS_MAPPEDPRI1 | DIEDBS_MAPPEDPRI2 ) ) {
break;
}
if ( IsEqualGUID(&pdiaf->rgoAction[i].guidInstance, &pddiW->guidInstance) &&
pdiaf->rgoAction[i].dwHow & DIAH_MAPMASK
)
{
if( DISEM_PRI_GET(pdiaf->rgoAction[i].dwSemantic) == 0 )
{
pri |= DIEDBS_MAPPEDPRI1;
} else if( DISEM_PRI_GET(pdiaf->rgoAction[i].dwSemantic) == 1 ) {
pri |= DIEDBS_MAPPEDPRI2;
}
}
}
return(pri);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func int | CDIObj_GetDeviceOrder |
*
* Get the number of the actions which have been mapped to controls.
*
* @parm IN LPDIDEVICEINSTANCEW | pddiW |
*
* Device Instance.
*
* @parm IN LPDIMAPPER | pdm |
*
* pointer to DIMAPPER structure
*
* @returns
*
* The order of the device
*
* @comm
* The order (DWORD) is consisted of three parts:
* HIWORD: HIBYTE: the order defined in the genre
* LOWBYTE: none, can be used later
* LOWORD: HIBYTE - mapped action number
* LOWBYTE - Force Feedback (1) or not (0)
*
*****************************************************************************/
#define GET_MAPPED_ACTION_NUM(x) ((x & 0x0000ff00) >> 8)
DWORD INTERNAL
CDIObj_GetDeviceOrder( LPDIDEVICEINSTANCEW pddiW, LPDIMAPPER pdm )
{
WORD wHighWord, wLowWord;
BYTE byDevOrder, byFF, byMappedActions;
DWORD dwGenre, dwJoyType;
BYTE byOrder;
AssertF(pddiW);
AssertF(pdm->pDiActionFormat);
if ( CDIObj_IsUserConfigured( pddiW, pdm ) )
{
byDevOrder = MAX_ORDER;
} else
{
switch( GET_DIDEVICE_TYPE(pddiW->dwDevType) )
{
case DI8DEVTYPE_DEVICE:
if( !(pdm->dwFlags & DIEDBSFL_NONGAMINGDEVICES) )
{
byDevOrder = INVALID_ORDER;
} else {
byDevOrder = DIDEVTYPE_DEVICE_ORDER;
}
break;
case DI8DEVTYPE_MOUSE:
if( pddiW->dwDevType & DIDEVTYPE_HID )
{
if( !(pdm->dwFlags & DIEDBSFL_MULTIMICEKEYBOARDS) ) {
byDevOrder = INVALID_ORDER;
} else {
byDevOrder = DIDEVTYPE_HID_MOUSE_ORDER;
}
} else {
byDevOrder = DIDEVTYPE_MOUSE_ORDER;
}
break;
case DI8DEVTYPE_KEYBOARD:
if( pddiW->dwDevType & DIDEVTYPE_HID )
{
if( !(pdm->dwFlags & DIEDBSFL_MULTIMICEKEYBOARDS) ) {
byDevOrder = INVALID_ORDER;
} else {
byDevOrder = DIDEVTYPE_HID_KEYBOARD_ORDER;
}
} else {
byDevOrder = DIDEVTYPE_KEYBOARD_ORDER;
}
break;
case DI8DEVTYPE_JOYSTICK:
case DI8DEVTYPE_GAMEPAD:
case DI8DEVTYPE_DRIVING:
case DI8DEVTYPE_FLIGHT:
case DI8DEVTYPE_1STPERSON:
case DI8DEVTYPE_SCREENPOINTER:
case DI8DEVTYPE_REMOTE:
case DI8DEVTYPE_DEVICECTRL:
dwJoyType = GET_DIDEVICE_TYPE(pddiW->dwDevType);
dwGenre = DISEM_VIRTUAL_GET(pdm->pDiActionFormat->dwGenre);
AssertF(dwGenre <= DISEM_MAX_GENRE);
AssertF(dwJoyType < DI8DEVTYPE_MAX);
AssertF(dwJoyType != 0);
for ( byOrder=DI8DEVTYPE_MIN; byOrder<DI8DEVTYPE_MAX; byOrder++ )
{
if ( DiGenreDeviceOrder[dwGenre][byOrder-DI8DEVTYPE_MIN] == dwJoyType )
{
break;
}
}
/*
* If the device is not on the default list, set its order as
* DIDEVTYPE_NOTDEFAULTDEVICE_ORDER + 1
*/
byDevOrder = DI8DEVTYPE_MAX - byOrder + DIDEVTYPE_SUPPLEMENTAL_ORDER + 1;
break;
case DI8DEVTYPE_SUPPLEMENTAL:
byDevOrder = DIDEVTYPE_SUPPLEMENTAL_ORDER;
break;
}
}
if( byDevOrder != INVALID_ORDER ) {
byFF = IsEqualGUID(&pddiW->guidFFDriver, &GUID_Null) ? 0 : 1;
byMappedActions = (UCHAR) CDIObj_GetMappedActionNum( pddiW, pdm );
wLowWord = MAKEWORD( byFF, byMappedActions );
wHighWord = MAKEWORD( 0, byDevOrder );
return(MAKELONG( wLowWord, wHighWord ));
} else {
return 0;
}
}
/*****************************************************************************
*
* CDIObj_DeviceEnumProc
*
* Device enumeration procedure which is called one for each device.
*
*****************************************************************************/
BOOL INTERNAL
CDIObj_InternalDeviceEnumProcW(LPDIDEVICEINSTANCEW pddiW, LPDIRECTINPUTDEVICE8W pdid8W, LPVOID pv)
{
LPDIMAPPER pdm = pv;
HRESULT hres = S_OK;
BOOL fRc = DIENUM_CONTINUE;
BOOL fContinue = FALSE;
DWORD dwDevOrder;
if ( g_nCurDev >= MAX_DEVICENUM )
{
fRc = DIENUM_STOP;
goto _done;
}
AssertF(pdid8W);
AssertF(pdm->pDiActionFormat);
hres = pdid8W->lpVtbl->BuildActionMap(pdid8W, pdm->pDiActionFormat, pdm->lpszUserName,
pdm->lpszUserName ? DIDBAM_DEFAULT : DIDBAM_HWDEFAULTS);
if ( SUCCEEDED(hres) )
{
if ( (pdm->dwFlags & DIEDBSFL_AVAILABLEDEVICES) ||
(pdm->dwFlags & DIEDBSFL_THISUSER) )
{
if( ((pdm->dwFlags & DIEDBSFL_AVAILABLEDEVICES) && CDIObj_IsDeviceAvailable(pddiW,pdm)) ||
((pdm->dwFlags & DIEDBSFL_THISUSER) && CDIObj_IsDeviceUsedByUser(pddiW,pdm))
) {
fContinue = TRUE;
}
} else {
fContinue = TRUE;
}
if( fContinue &&
((dwDevOrder = CDIObj_GetDeviceOrder(pddiW, pdm)) != 0) )
{
#ifdef DEBUG
DWORD dbgRef;
#endif
#ifdef DEBUG
dbgRef =
#endif
pdid8W->lpVtbl->AddRef(pdid8W);
g_DiDevices[g_nCurDev].dwOrder = dwDevOrder;
g_DiDevices[g_nCurDev].dwFlags = CDIObj_GetMappedPriorities( pddiW, pdm );
g_DiDevices[g_nCurDev].pdid8W = pdid8W;
memcpy( &g_DiDevices[g_nCurDev].ftTimeStamp, &pdm->pDiActionFormat->ftTimeStamp, sizeof(FILETIME) );
memcpy( &g_DiDevices[g_nCurDev].ddiW, pddiW, sizeof(*pddiW) );
g_nCurDev ++;
fRc = DIENUM_CONTINUE;
}
}
_done:
return(fRc);
}
/*****************************************************************************
*
* compare
*
* Compare func used in shortsort
*
*****************************************************************************/
int __cdecl compare( const void *arg1, const void *arg2 )
{
DWORD dw1 = ((LPDIORDERDEV)arg1)->dwOrder;
DWORD dw2 = ((LPDIORDERDEV)arg2)->dwOrder;
/*
* Compare the device order
* If necessary, we can compare seperately: devtype, mapped action number, FF device
*/
if ( dw1 < dw2 )
{
return(1);
} else if ( dw1 > dw2 )
{
return(-1);
} else
{
return(0);
}
}
void FreeDiActionFormatW(LPDIACTIONFORMATW* lplpDiAfW )
{
FreePpv(lplpDiAfW);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | IsValidMapObjectA |
*
* Validates a LPDIACTIONFORMATW including strings
*
* @parm const LPDIACTIONFORMATW | lpDiAfW |
*
* Original.
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
HRESULT
IsValidMapObjectA
(
LPDIACTIONFORMATA paf
#ifdef XDEBUG
comma LPCSTR pszProc
comma UINT argnum
#endif
)
{
HRESULT hres;
hres = CDIDev_ActionMap_IsValidMapObject
( (LPDIACTIONFORMATW)paf
#ifdef XDEBUG
comma pszProc
comma argnum
#endif
);
if( SUCCEEDED( hres ) )
{
if( paf->dwSize != cbX(DIACTIONFORMATA) )
{
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATA.dwSize 0x%08x",
pszProc, paf->dwSize ); )
hres = E_INVALIDARG;
}
}
if(SUCCEEDED(hres))
{
DWORD i;
LPDIACTIONA lpDiAA;
// Compute the size for each of the text strings in array of DIACTIONs
for ( i = 0x0, lpDiAA = paf->rgoAction;
i < paf->dwNumActions && SUCCEEDED(hres) ;
i++, lpDiAA++ )
{
// Handle the NULL ptr case
if ( NULL != lpDiAA->lptszActionName )
{
hres = hresFullValidReadStrA_(lpDiAA->lptszActionName, MAX_JOYSTRING, pszProc, argnum);
}
}
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | IsValidMapObjectW |
*
* Validates a LPDIACTIONFORMATW including strings
*
* @parm const LPDIACTIONFORMATW | lpDiAfW |
*
* Original.
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
HRESULT
IsValidMapObjectW
(
LPDIACTIONFORMATW paf
#ifdef XDEBUG
comma LPCSTR pszProc
comma UINT argnum
#endif
)
{
HRESULT hres;
hres = CDIDev_ActionMap_IsValidMapObject
( paf
#ifdef XDEBUG
comma pszProc
comma argnum
#endif
);
if( SUCCEEDED( hres ) )
{
if( paf->dwSize != cbX(DIACTIONFORMATW) )
{
D( RPF("IDirectInputDevice::%s: Invalid DIACTIONFORMATW.dwSize 0x%08x",
pszProc, paf->dwSize ); )
hres = E_INVALIDARG;
}
}
if(SUCCEEDED(hres))
{
DWORD i;
LPDIACTIONW lpDiAW;
// Compute the size for each of the text strings in array of DIACTIONs
for ( i = 0x0, lpDiAW = paf->rgoAction;
i < paf->dwNumActions && SUCCEEDED(hres) ;
i++, lpDiAW++ )
{
// Handle the NULL ptr case
if ( NULL != lpDiAW->lptszActionName )
{
hres = hresFullValidReadStrW_(lpDiAW->lptszActionName, MAX_JOYSTRING, pszProc, argnum);
}
}
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | DiActionFormatWtoW |
*
* Copies LPDIACTIONFORMATW to LPDIACTIONFORMATW
*
* @parm const LPDIACTIONFORMATW | lpDiAfW |
*
* Original.
*
* @parm LPDIACTIONFORMATW* | lplpDiAfW |
*
* Address of a pointer to a <t DIACTIONFORMATW> that receives the converted
* ACTIONFORMAT.
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
HRESULT EXTERNAL DiActionFormatWtoW
(
const LPDIACTIONFORMATW lpDiAfW0,
LPDIACTIONFORMATW* lplpDiAfW
)
{
DWORD cbAlloc;
PDWORD pdwStrLen0, pdwStrLen;
LPDIACTIONFORMATW lpDiAfW;
LPDIACTIONW lpDiAW0;
LPDIACTIONW lpDiAW;
DWORD i;
HRESULT hres;
EnterProcI(DiActionFormatWtoW, (_ "xx", lpDiAfW0, lplpDiAfW));
// Internal function, no validation
*lplpDiAfW = NULL;
/*
* PREFIX complains (mb:37926 - items 3 & 4) that we could be requesting
* a zero byte allocation which would not allocate anything. This is
* never the case because CDIDev_ActionMap_IsValidMapObject tests that
* dwNumActions is less than 2^24. Assert in debug for extra safety.
*/
AssertF( (lpDiAfW0->dwNumActions +1) * cbX(*pdwStrLen0) );
hres = AllocCbPpv( (lpDiAfW0->dwNumActions +1) * cbX(*pdwStrLen0) , &pdwStrLen0);
if ( SUCCEEDED(hres) )
{
pdwStrLen = pdwStrLen0;
// Compute the amount of memory required to clone the DIACTIONFORMATA
cbAlloc =
/* 1: The Action Format array */
lpDiAfW0->dwSize
/* 2: Each of the DIACTION arrays */
+ lpDiAfW0->dwActionSize * lpDiAfW0->dwNumActions;
// Compute the size for each of the text strings in array of DIACTIONs
for ( i = 0x0, lpDiAW0 = lpDiAfW0->rgoAction;
i < lpDiAfW0->dwNumActions ;
i++, lpDiAW0++ )
{
// Handle the NULL ptr case
if ( !lpDiAW0->lptszActionName )
{
*pdwStrLen++ = 0;
}
else
{
if ( (UINT_PTR)lpDiAW0->lptszActionName > (UINT_PTR)0xFFFF )
{
/* 3: Text string in each DIACTION array*/
// Conversion from A to U, need multiplier
*pdwStrLen = cbX(lpDiAW0->lptszActionName[0]) * ( lstrlenW(lpDiAW0->lptszActionName) + 1 );
cbAlloc += *pdwStrLen++;
}
else
{
// Use resource strings
WCHAR wsz[MAX_PATH];
if (lpDiAfW0->hInstString > 0)
{
//find out the length of the string
*pdwStrLen = LoadStringW(lpDiAfW0->hInstString, lpDiAW0->uResIdString, (LPWSTR) &wsz, MAX_PATH);
}
else
{
*pdwStrLen = 0;
}
cbAlloc += *pdwStrLen++;
}
}
}
if ( SUCCEEDED( hres = AllocCbPpv(cbAlloc, &lpDiAfW) ) )
{
DWORD dwLen;
DWORD cb;
pdwStrLen = pdwStrLen0;
// 1: Copy the DIACTIONFORMAT
*lpDiAfW = *lpDiAfW0;
cb = lpDiAfW0->dwSize;
// 2: Block copy the DIACTION array
lpDiAfW->rgoAction = (LPDIACTIONW)( (char*)lpDiAfW + cb);
dwLen = lpDiAfW0->dwActionSize * lpDiAfW0->dwNumActions;
memcpy(lpDiAfW->rgoAction, lpDiAfW0->rgoAction, dwLen);
cb += dwLen;
// 3: ActionName
for ( i = 0x0, lpDiAW0=lpDiAfW0->rgoAction, lpDiAW=lpDiAfW->rgoAction;
i < lpDiAfW0->dwNumActions ;
i++, lpDiAW0++, lpDiAW++ )
{
if ( (UINT_PTR)lpDiAW0->lptszActionName > (UINT_PTR)0xFFFF )
{
WCHAR* wsz = (WCHAR*) ((char*)lpDiAfW+cb);
lpDiAW->lptszActionName = wsz;
dwLen = *pdwStrLen++;
memcpy(wsz, lpDiAW0->lptszActionName, dwLen);
cb += dwLen ;
} else
{
// Handle resource strings
// OK for now, as long as UI always uses CloneDiActionFormatW
WCHAR* wsz = (WCHAR*) ((char*)lpDiAfW+cb);
dwLen = *pdwStrLen++;
if ((dwLen != 0) && (LoadStringW(lpDiAfW0->hInstString, lpDiAW0->uResIdString, wsz, dwLen)))
{
//If we found a length last time there must be a resource module
AssertF( lpDiAfW0->hInstString > 0 );
//Found and loaded the string
lpDiAW->lptszActionName = wsz;
}
else
{
//No hinstance or length 0 or didn't load the string
lpDiAW->lptszActionName = NULL;
}
cb += dwLen;
}
}
// If we have not done something goofy, the memory allocates should match
// the memory we used
AssertF(cbAlloc == cb );
*lplpDiAfW = lpDiAfW;
}
FreePpv(&pdwStrLen0);
}
ExitOleProc();
return(hres);
}
/*****************************************************************************
*
* CDIMap_EnumDevicesBySemantics
*
* Enum Suitable Devices.
*
*****************************************************************************/
STDMETHODIMP CDIObj_EnumDevicesBySemanticsW(
PV pDiW,
LPCWSTR lpszUserName,
LPDIACTIONFORMATW pDiActionFormat,
LPDIENUMDEVICESBYSEMANTICSCBW pecW,
LPVOID pvRef,
DWORD dwFlags
)
{
HRESULT hres;
EnterProcR(IDirectInput8::EnumDevicesBySemantics,
(_ "pppppx", pDiW, lpszUserName, pDiActionFormat, pecW, pvRef, dwFlags));
if ( SUCCEEDED(hres = hresPvI(pDiW, ThisInterfaceW)) &&
SUCCEEDED(hres = IsValidMapObjectW(pDiActionFormat D(comma s_szProc comma 2))) &&
(lpszUserName == NULL || SUCCEEDED(hres = hresFullValidReadStrW(lpszUserName, UNLEN+1, 1))) &&
SUCCEEDED(hres = hresFullValidPfn(pecW, 3)) &&
SUCCEEDED(hres = hresFullValidFl(dwFlags, DIEDBSFL_VALID, 5)) &&
SUCCEEDED(hres = CMap_ValidateActionMapSemantics(pDiActionFormat, DIDBAM_PRESERVE))
)
{
PDDI this = _thisPvNm(pDiW, diW);
if ( SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion)) )
{
DIMAPPER dm;
LPDIACTIONFORMATW lpDiAfW;
if ( SUCCEEDED(hres= DiActionFormatWtoW(pDiActionFormat, &lpDiAfW)) )
{
dm.lpszUserName = lpszUserName;
dm.pDiActionFormat = lpDiAfW;
dm.pecW = pecW;
dm.pvRef = pvRef;
dm.dwFlags = dwFlags;
if( dwFlags == 0 ) {
dwFlags |= DIEDFL_ATTACHEDONLY;
}
dwFlags &= ~DIEDBSFL_AVAILABLEDEVICES;
dwFlags &= ~DIEDBSFL_THISUSER;
dwFlags &= ~DIEDBSFL_MULTIMICEKEYBOARDS;
dwFlags &= ~DIEDBSFL_NONGAMINGDEVICES;
ZeroX(g_DiDevices);
g_nCurDev = 0;
hres = CDIObj_InternalEnumDevicesW( pDiW,
0, //enum all tyeps of devices
(LPVOID)&dm,
dwFlags //only enum attached devices
);
/*
* For short array sorting (size <= 8), shortsort is better than qsort.
*/
if ( SUCCEEDED(hres) && g_nCurDev )
{
int ndev;
int nNewDev = -1;
FILETIME ft = { 0, 0 };
FILETIME ftMostRecent;
shortsort( (char *)&g_DiDevices[0], (char *)&g_DiDevices[g_nCurDev-1], sizeof(DIORDERDEV), compare );
SquirtSqflPtszV(sqflDi | sqflVerbose,
TEXT("EnumDevicesBySemantics: %d devices enumed"), g_nCurDev );
for ( ndev=0; ndev<g_nCurDev; ndev++ )
{
if( (g_DiDevices[ndev].ftTimeStamp.dwHighDateTime != DIAFTS_NEWDEVICEHIGH) &&
(g_DiDevices[ndev].ftTimeStamp.dwLowDateTime != DIAFTS_NEWDEVICELOW) &&
(CompareFileTime(&g_DiDevices[ndev].ftTimeStamp, &ft) == 1) ) // first device is newer
{
nNewDev = ndev;
memcpy( &ft, &g_DiDevices[ndev].ftTimeStamp, sizeof(FILETIME) );
}
}
if( nNewDev != -1 ) {
g_DiDevices[nNewDev].dwFlags |= DIEDBS_RECENTDEVICE;
memcpy( &ftMostRecent, &g_DiDevices[nNewDev].ftTimeStamp, sizeof(FILETIME) );
}
for ( ndev=0; ndev<g_nCurDev; ndev++ )
{
// find RECENT devices and set flag.
if( (nNewDev != -1) && (ndev != nNewDev) ) {
if( (ftMostRecent.dwHighDateTime == g_DiDevices[ndev].ftTimeStamp.dwHighDateTime) &&
(ftMostRecent.dwLowDateTime - g_DiDevices[ndev].ftTimeStamp.dwLowDateTime < 100000000 ) )//10 seconds difference
{
g_DiDevices[ndev].dwFlags |= DIEDBS_RECENTDEVICE;
}
}
// find NEW devices and set flag
if( (g_DiDevices[ndev].ftTimeStamp.dwLowDateTime == DIAFTS_NEWDEVICELOW) &&
(g_DiDevices[ndev].ftTimeStamp.dwHighDateTime == DIAFTS_NEWDEVICEHIGH) )
{
g_DiDevices[ndev].dwFlags |= DIEDBS_NEWDEVICE;
}
}
if( !IsBadCodePtr((PV)pecW) ) {
for ( ndev=0; ndev<g_nCurDev; ndev++ )
{
LPDIDEVICEINSTANCEW pddiW = &g_DiDevices[ndev].ddiW;
SquirtSqflPtszV(sqflDi | sqflVerbose,
TEXT("EnumDevicesBySemantics: device %d - %s: %d action(s) mapped"),
ndev+1, pddiW->tszProductName, GET_MAPPED_ACTION_NUM(g_DiDevices[ndev].dwOrder) );
if ( pddiW )
{
#ifdef DEBUG
DWORD dbgRef;
#endif
DWORD dwDeviceRemaining = g_nCurDev-ndev-1;
AssertF(g_DiDevices[ndev].pdid8W);
AssertF(g_DiDevices[ndev].pdid8W->lpVtbl);
if( g_DiDevices[ndev].pdid8W && g_DiDevices[ndev].pdid8W->lpVtbl )
{
BOOL fRc;
fRc = pecW(pddiW, g_DiDevices[ndev].pdid8W, g_DiDevices[ndev].dwFlags, dwDeviceRemaining, pvRef);
if( fRc == DIENUM_STOP ) {
for ( ; ndev<g_nCurDev; ndev++ ) {
g_DiDevices[ndev].pdid8W->lpVtbl->Release(g_DiDevices[ndev].pdid8W);
}
break;
}
#ifdef DEBUG
dbgRef =
#endif
g_DiDevices[ndev].pdid8W->lpVtbl->Release(g_DiDevices[ndev].pdid8W);
}
}
}
}
}
FreeDiActionFormatW(&lpDiAfW);
}
}
}
ExitOleProcR();
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIObj_EnumDevicesBySemanticsCallbackA |
*
* Wrapper function for <mf IDirectInput::EnumDevicesBySemantics>
* which translates the UNICODE parameters to ANSI.
*
* @parm IN LPCDIDECICEINSTANCEW | pdiW |
*
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm IN OUT PV | pvRef |
*
* Pointer to <t struct ENUMDEVICESBYSEMANTICSINFO> which describes
* the original callback.
*
* @returns
*
* Returns whatever the original callback returned.
*
*****************************************************************************/
typedef struct ENUMDEVICESBYSEMANTICSINFO
{
LPDIENUMDEVICESBYSEMANTICSCBA pecA;
PV pvRef;
} ENUMDEVICESBYSEMANTICSINFO, *PENUMDEVICESBYSEMANTICSINFO;
BOOL CALLBACK
CDIObj_EnumDevicesBySemanticsCallback(LPCDIDEVICEINSTANCEW pdiW, LPDIRECTINPUTDEVICE8W pdid8W, DWORD dwFlags, DWORD dwDeviceRemaining, PV pvRef)
{
PENUMDEVICESBYSEMANTICSINFO pesdi = pvRef;
BOOL fRc;
DIDEVICEINSTANCEA diA;
LPDIRECTINPUTDEVICE8A pdid8A = NULL;
EnterProc(CDIObj_EnumDevicesBySemanticsCallback,
(_ "GGxWWp", &pdiW->guidInstance, &pdiW->guidProduct,
&pdiW->dwDevType,
pdiW->tszProductName, pdiW->tszInstanceName,
pvRef));
diA.dwSize = cbX(diA);
DeviceInfoWToA(&diA, pdiW);
Device8WTo8A(&pdid8A, pdid8W);
fRc = pesdi->pecA(&diA, pdid8A, dwFlags, dwDeviceRemaining, pesdi->pvRef);
ExitProcX(fRc);
return(fRc);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | EnumDevicesBySemantics |
*
* Enumerates devices suitable for the application specified
* <t DIACTIONFORMAT>.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* ISSUE-2001/03/29-timgill Need to fix auto docs
* @parm LPTSTR | lptszActionMap |
*
* Friendly name for the application.
*
* @parm REFGUID | rguid |
*
* Unique GUID to identify the application.
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
STDMETHODIMP CDIObj_EnumDevicesBySemanticsA
(
PV pDiA,
LPCSTR lpszUserName,
LPDIACTIONFORMATA pDiActionFormat,
LPDIENUMDEVICESBYSEMANTICSCBA pecA,
LPVOID pvRef,
DWORD dwFlags
)
{
HRESULT hres;
EnterProcR(IDirectInput8::EnumDevicesBySemantics,
(_ "pppppx", pDiA, lpszUserName, pDiActionFormat, pecA, pvRef, dwFlags));
/*
* EnumDevicesBySemanticsW will validate the rest.
*/
if ( SUCCEEDED(hres = hresPvI(pDiA, ThisInterfaceA)) &&
SUCCEEDED(hres = IsValidMapObjectA(pDiActionFormat D(comma s_szProc comma 2))) &&
(lpszUserName == NULL || SUCCEEDED(hres = hresFullValidReadStrA(lpszUserName, UNLEN+1, 1))) &&
SUCCEEDED(hres = hresFullValidPfn(pecA, 3)) &&
SUCCEEDED(hres = hresFullValidFl(dwFlags, DIEDBSFL_VALID, 5))
)
{
PDDI this = _thisPvNm(pDiA, diA);
ENUMDEVICESBYSEMANTICSINFO esdi = { pecA, pvRef};
WCHAR wszUserName[MAX_PATH];
DIACTIONFORMATW diafW;
LPDIACTIONW rgoActionW;
wszUserName[0] = L'\0';
if( lpszUserName ) {
AToU(wszUserName, MAX_PATH, lpszUserName);
}
/*
* Assert that the structure can be copied as:
* a) dwSize
* b) everything else
* c) the app name
*/
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) == 0 );
CAssertF( FIELD_OFFSET( DIACTIONFORMATA, dwSize ) == 0 );
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) + cbX( ((LPDIACTIONFORMATW)0)->dwSize )
== FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) );
#if defined(_WIN64)
CAssertF( ( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
- ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) )
< MAX_NATURAL_ALIGNMENT ) );
#else
CAssertF( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
== ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) );
#endif
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, tszActionMap )
== FIELD_OFFSET( DIACTIONFORMATA, tszActionMap ) );
CAssertF( cA( ((LPDIACTIONFORMATW)0)->tszActionMap ) == cA( ((LPDIACTIONFORMATA)0)->tszActionMap ) );
//Init diafW fields
diafW.dwSize = cbX(DIACTIONFORMATW);
memcpy( &diafW.dwActionSize, &pDiActionFormat->dwActionSize,
FIELD_OFFSET( DIACTIONFORMATW, tszActionMap ) - FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) );
AToU(diafW.tszActionMap, cbX(pDiActionFormat->tszActionMap), pDiActionFormat->tszActionMap);
if ( SUCCEEDED( hres = AllocCbPpv( cbCxX(pDiActionFormat->dwNumActions, DIACTIONW), &rgoActionW) ) )
{
memcpy( rgoActionW, pDiActionFormat->rgoAction, sizeof(DIACTIONA) * pDiActionFormat->dwNumActions );
diafW.rgoAction = rgoActionW;
hres = CDIObj_EnumDevicesBySemanticsW(&this->diW, wszUserName,
&diafW,
CDIObj_EnumDevicesBySemanticsCallback,
&esdi, dwFlags);
FreePpv(&rgoActionW);
}
}
ExitOleProcR();
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | hresValidSurface |
*
* Tests the interface pointer for a valid surface of sufficient
* dimensions to display the UI and with a supported pixel format.
*
* @parm IUnknown* | lpUnkDDSTarget |
*
* Pointer to an interface which must be validated as a COM object
* before calling this function.
*
* @returns
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p lpUnkDDSTarget> parameter is not valid.
* A DirectDraw or D3D error if one was returned.
* or a Standard OLE <t HRESULT>.
*
*****************************************************************************/
HRESULT INLINE hresValidSurface
(
IUnknown* lpUnkDDSTarget
)
{
HRESULT hres;
IUnknown* lpSurface = NULL;
/*
* Short on time, so take short-cuts in validation debug error messages
*/
#ifdef XDEBUG
CHAR s_szProc[] = "IDirectInput8::ConfigureDevices";
#endif
#define ArgIS 2
if( SUCCEEDED( hres = lpUnkDDSTarget->lpVtbl->QueryInterface(
lpUnkDDSTarget, &IID_IDirect3DSurface8, (LPVOID*)&lpSurface ) ) )
{
D3DSURFACE_DESC SurfDesc;
hres = ((IDirect3DSurface8*)lpSurface)->lpVtbl->GetDesc( ((IDirect3DSurface8*)lpSurface), &SurfDesc );
if( FAILED( hres ) )
{
RPF( "%s: Arg %d: Unable to GetDesc on surface, error 0x%08x", s_szProc, ArgIS, hres );
/*
* D3D returns real HRESULTs which can be returned to the caller
*/
}
else
{
if( ( SurfDesc.Width < 640 ) || ( SurfDesc.Height < 480 ) )
{
RPF( "%s: Arg %d: cannot use %d by %d surface",
s_szProc, ArgIS, SurfDesc.Width, SurfDesc.Height );
hres = E_INVALIDARG;
}
else
{
switch( SurfDesc.Format )
{
case D3DFMT_R8G8B8:
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("ConfigureDevices: validated %d by %d format %d surface"),
SurfDesc.Width, SurfDesc.Height, SurfDesc.Format );
break;
default:
RPF( "%s: Arg %d: cannot use surface format %d ", s_szProc, ArgIS, SurfDesc.Format );
hres = E_INVALIDARG;
break;
}
}
}
}
else
{
DDSURFACEDESC2 SurfDesc;
SurfDesc.dwSize = cbX( SurfDesc );
if( SUCCEEDED(hres = lpUnkDDSTarget->lpVtbl->QueryInterface(
lpUnkDDSTarget, &IID_IDirectDrawSurface7, (LPVOID*) &lpSurface)) )
{
hres = ((IDirectDrawSurface7*)lpSurface)->lpVtbl->GetSurfaceDesc( ((IDirectDrawSurface7*)lpSurface), &SurfDesc );
}
else if( SUCCEEDED(hres = lpUnkDDSTarget->lpVtbl->QueryInterface(
lpUnkDDSTarget, &IID_IDirectDrawSurface4, (LPVOID*) &lpSurface )) )
{
hres = ((IDirectDrawSurface4*)lpSurface)->lpVtbl->GetSurfaceDesc( ((IDirectDrawSurface4*)lpSurface), &SurfDesc );
}
if( FAILED( hres ) )
{
if( lpSurface )
{
RPF( "%s: Arg %d: failed GetSurfaceDesc, error 0x%08x", s_szProc, ArgIS, hres );
}
else
{
RPF( "%s: Arg %d: failed QI for supported surface interfaces from %p, error 0x%08x",
s_szProc, ArgIS, lpSurface, hres );
}
/*
* DDraw returns real HRESULTs which can be returned to the caller
*/
}
else if( ( SurfDesc.dwWidth < 640 ) || ( SurfDesc.dwHeight < 480 ) )
{
RPF( "%s: Arg %d: cannot use %d by %d surface",
s_szProc, ArgIS, SurfDesc.dwWidth, SurfDesc.dwHeight );
hres = E_INVALIDARG;
}
else
{
/*
* Check for the eqivalent of the DX8 surfaces:
* A8R8G8B8, X8R8G8B8, R8G8B8, A1R5G5B5, X1R5G5B5, R5G6B5
*/
if( SurfDesc.ddpfPixelFormat.dwFlags & DDPF_RGB )
{
if( SurfDesc.ddpfPixelFormat.dwRGBBitCount > 16 )
{
AssertF( ( SurfDesc.ddpfPixelFormat.dwRGBBitCount == 32 )
||( SurfDesc.ddpfPixelFormat.dwRGBBitCount == 24 ) );
/*
* All of these must be R8 G8 B8
*/
if( ( SurfDesc.ddpfPixelFormat.dwRBitMask == 0x00FF0000 )
&& ( SurfDesc.ddpfPixelFormat.dwGBitMask == 0x0000FF00 )
&& ( SurfDesc.ddpfPixelFormat.dwBBitMask == 0x000000FF ) )
{
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("ConfigureDevices: validated %d by %d format R8G8B8 %d bit surface"),
SurfDesc.dwWidth, SurfDesc.dwHeight, SurfDesc.ddpfPixelFormat.dwRGBBitCount );
}
else
{
RPF( "%s: Arg %d: cannot use surface pixel format", s_szProc, ArgIS );
hres = E_INVALIDARG;
}
}
else
{
if( SurfDesc.ddpfPixelFormat.dwRGBBitCount == 16 )
{
/*
* Allow R5 G5 B5 and R5 G6 B5
*/
if( ( SurfDesc.ddpfPixelFormat.dwBBitMask == 0x0000001F )
&& ( ( SurfDesc.ddpfPixelFormat.dwGBitMask == 0x000003E0 )
&& ( SurfDesc.ddpfPixelFormat.dwRBitMask == 0x00007C00 ) )
|| ( ( SurfDesc.ddpfPixelFormat.dwGBitMask == 0x000007E0 )
&& ( SurfDesc.ddpfPixelFormat.dwRBitMask == 0x0000F800 ) ) )
{
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("ConfigureDevices: validated %d by %d format 16 bit surface"),
SurfDesc.dwWidth, SurfDesc.dwHeight );
}
else
{
RPF( "%s: Arg %d: cannot use 16 bit surface pixel format", s_szProc, ArgIS );
hres = E_INVALIDARG;
}
}
else
{
RPF( "%s: Arg %d: cannot use %d bit surface pixel format",
s_szProc, ArgIS, SurfDesc.ddpfPixelFormat.dwRGBBitCount );
hres = E_INVALIDARG;
}
}
}
else
{
RPF( "%s: Arg %d: cannot use non RGB surface, Surface.dwFlags = 0x%08x",
s_szProc, ArgIS, SurfDesc.ddpfPixelFormat.dwFlags );
hres = E_INVALIDARG;
}
}
}
if( lpSurface != NULL )
{
lpSurface->lpVtbl->Release( lpSurface );
}
return hres;
#undef ArgIS
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | ConfigureDevices |
*
* Configures the devices by attaching mappings provided in
* <t DIACTIONFORMAT> to appropriate device controls.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm LPCTSTR | lpctszUserName |
*
* User Name.
*
* @parm LPDIACTIONFORMAT | lpDiActionFormat |
*
* Pointer to the <t DIACTIONFORMAT> structure containing the action map.
*
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
STDMETHODIMP CDIObj_ConfigureDevicesCore
(
PV pDiW,
LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
DWORD dwFlags,
LPVOID pvRefData
)
{
//the real ConfigureDevices()
//some stuff should have been validated already
HRESULT hres = S_OK;
EnterProcI(IDirectInput8::ConfigureDevicesCore,
(_ "pppup", pDiW, lpdiCallback,lpdiCDParams, dwFlags, pvRefData));
if( SUCCEEDED( hres = hresFullValidFl( dwFlags, DICD_VALID, 3 ) )
&& SUCCEEDED( hres = hresFullValidReadPvCb( &(lpdiCDParams->dics), sizeof(DICOLORSET), 2 ) ) )
{
/*
* lpUnkDDSTarget and lpdiCallback are "coupled", because the only
* function of the callback is to display the updates to the surface
* either they are both NULL, or they are both non-NULL and valid.
* otherwise, it is an error.
*/
if( lpdiCallback == NULL )
{
if( lpdiCDParams->lpUnkDDSTarget == NULL )
{
hres = S_OK;
}
else
{
RPF( "%s: Arg %d or %d: neither or both of callback and surface must be NULL",
s_szProc, 1, 2 );
hres = E_INVALIDARG;
}
}
else if( lpdiCDParams->lpUnkDDSTarget == NULL )
{
RPF( "%s: Arg %d or %d: neither or both of surface and callback must be NULL",
s_szProc, 2, 1 );
hres = E_INVALIDARG;
}
else if( SUCCEEDED( hres = hresFullValidPfn( lpdiCallback, 1 ) )
&& SUCCEEDED( hres = hresFullValidPitf( lpdiCDParams->lpUnkDDSTarget, 2 ) ) )
{
hres = hresValidSurface( lpdiCDParams->lpUnkDDSTarget );
}
if( SUCCEEDED( hres ) )
{
//load the framework
HINSTANCE hinst;
IDirectInputActionFramework* pDIAcFrame = NULL;
TCHAR tszName[ctchNameGuid];
TCHAR tszClsid[ctchGuid];
NameFromGUID(tszName, &CLSID_CDirectInputActionFramework);
memcpy(tszClsid, &tszName[ctchNamePrefix], cbX(tszClsid) );
hres = DICoCreateInstance(tszClsid, NULL, &IID_IDIActionFramework, (LPVOID*) & pDIAcFrame, &hinst);
if( SUCCEEDED(hres) )
{
//for getting default user name, if needed
LPWSTR pwszUserName = NULL;
//can't pass NULL user name down to the framework -- need to get the default user name
if( lpdiCDParams->lptszUserNames == NULL )
{
hres = GetWideUserName(NULL, NULL, &pwszUserName);
lpdiCDParams->lptszUserNames = pwszUserName;
lpdiCDParams->dwcUsers = 1;
}
if( SUCCEEDED(hres) )
{
//call the framework
hres = pDIAcFrame->lpVtbl->ConfigureDevices
(
pDIAcFrame,
lpdiCallback,
lpdiCDParams,
dwFlags,
pvRefData);
if( SUCCEEDED(hres) )
{
SquirtSqflPtszV( sqfl | sqflVerbose,
TEXT("Default Remapping UI returned 0x%08x"), hres );
}
else
{
SquirtSqflPtszV( sqfl | sqflError,
TEXT("Default Remapping UI returned error 0x%08x"), hres );
}
//release pwsUserName, if we have used it
FreePpv(&pwszUserName);
}
FreeLibrary( hinst );
}
}
}
ExitOleProc();
return hres;
}
STDMETHODIMP CDIObj_ConfigureDevicesW
(
PV pDiW,
LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
DWORD dwFlags,
LPVOID pvRefData
)
{
HRESULT hres = S_OK;
EnterProcR(IDirectInput8::ConfigureDevices,
(_ "pppxp", pDiW, lpdiCallback,lpdiCDParams, dwFlags, pvRefData));
//validate all the ptrs
if ( (SUCCEEDED(hres = hresPvI(pDiW, ThisInterfaceW)) &&
(SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams, sizeof(DICONFIGUREDEVICESPARAMSW), 2))) &&
((lpdiCDParams->lptszUserNames == NULL) || (SUCCEEDED(hres = hresFullValidReadStrW((LPWSTR)(lpdiCDParams->lptszUserNames), MAX_JOYSTRING * (lpdiCDParams->dwcUsers), 2)))) &&
(SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams->lprgFormats, lpdiCDParams->dwcFormats*sizeof(DIACTIONFORMATW), 2)))))
{
if( lpdiCDParams->dwSize != cbX(DICONFIGUREDEVICESPARAMSW) )
{
RPF("IDirectInput::%s: Invalid DICONFIGUREDEVICESPARAMSW.dwSize 0x%08x",
lpdiCDParams->dwSize );
hres = E_INVALIDARG;
}
if (SUCCEEDED(hres))
{
PDDI this = _thisPvNm(pDiW, diW);
//params structure
DICONFIGUREDEVICESPARAMSW diconfparamsW;
//to translate each DIACTIONFORMAT
LPDIACTIONFORMATW* lpDiAfW = NULL;
//the cloning array of DIACTIONFORMATs
LPDIACTIONFORMATW* lpDAFW = NULL;
//to traverse the old array
LPDIACTIONFORMATW lpDIFormat;
//to traverse the new array
LPDIACTIONFORMATW lpDIF;
//user names
LPWSTR lpUserNames = NULL;
//DIACTIONFORMATs cloned
DWORD clonedF = 0;
//length of user names
DWORD strLen = 0;
//zero out
ZeroMemory(&diconfparamsW, sizeof(DICONFIGUREDEVICESPARAMSW));
//set the size
diconfparamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW);
//1. Validate and translate each LPDIACTIONFORMAT in the array
lpDIFormat = (lpdiCDParams->lprgFormats);
//allocate the new array
hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &diconfparamsW.lprgFormats);
if (FAILED(hres))
{
goto cleanup;
}
lpDIF = diconfparamsW.lprgFormats;
//allocate the cloning array
hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &lpDAFW);
if (FAILED(hres))
{
goto cleanup;
}
lpDiAfW = lpDAFW;
//clone
for (clonedF = 0; clonedF < lpdiCDParams->dwcFormats; clonedF ++)
{
//validate
hres = IsValidMapObjectW(lpDIFormat D(comma s_szProc comma 2));
if (FAILED(hres))
{
goto cleanup;
}
//translate
hres = DiActionFormatWtoW(lpDIFormat, lpDiAfW);
if (FAILED(hres))
{
goto cleanup;
}
//save
*lpDIF = *(*lpDiAfW);
//move on
lpDIFormat++;
lpDiAfW++;
lpDIF++;
}
//if everything went fine, should have cloned all
AssertF(clonedF == lpdiCDParams->dwcFormats);
//2. Copy the user names
if (lpdiCDParams->lptszUserNames != NULL)
{
DWORD countN;
WCHAR* lpName = lpdiCDParams->lptszUserNames;
for (countN = 0; countN < lpdiCDParams->dwcUsers; countN ++)
{
DWORD Len;
hres = hresFullValidReadStrW(lpName, MAX_JOYSTRING, 2);
if (FAILED(hres))
{
goto cleanup;
}
Len = lstrlenW(lpName);
//if length is 0 -- and we haven't reached the correct user count yet -
//then it is an error
if (Len == 0)
{
hres = DIERR_INVALIDPARAM;
goto cleanup;
}
//move on to the next user name
strLen += Len + 1;
lpName += Len + 1;
}
//if everything went fine, should have traversed all the user names
AssertF(countN == lpdiCDParams->dwcUsers);
//allocate
hres = AllocCbPpv( (strLen + 1) * 2, &lpUserNames );
if (FAILED(hres))
{
goto cleanup;
}
//copy
memcpy(lpUserNames, lpdiCDParams->lptszUserNames, strLen*2);
diconfparamsW.lptszUserNames = lpUserNames;
}
//3. Populate the rest of the structure
diconfparamsW.dwcUsers = lpdiCDParams->dwcUsers;
diconfparamsW.dwcFormats = clonedF;
diconfparamsW.hwnd = lpdiCDParams->hwnd;
diconfparamsW.dics = lpdiCDParams->dics;
diconfparamsW.lpUnkDDSTarget = lpdiCDParams->lpUnkDDSTarget;
//4. Call the framework
hres = CDIObj_ConfigureDevicesCore
(
&this->diW,
lpdiCallback,
&diconfparamsW,
dwFlags,
pvRefData);
cleanup:;
//free the space for the new array
FreePpv(&diconfparamsW.lprgFormats);
//free as many DIACTIONFORMATs as were created
if (lpDAFW)
{
lpDiAfW = lpDAFW;
for (clonedF; clonedF > 0; clonedF--)
{
FreeDiActionFormatW(lpDiAfW);
lpDiAfW++;
}
//delete the entire block
FreePpv(&lpDAFW);
}
//delete the user names
FreePpv(&lpUserNames);
}
}
ExitOleProc();
return(hres);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | DiActionFormatAtoW |
*
* Copies LPDIACTIONFORMATA to LPDIACTIONFORMATW
*
* @parm const LPDIACTIONFORMATA | lpDiAfA |
*
* Original.
*
* @parm LPDIACTIONFORMATW* | lplpDiAfW |
*
* Address of a pointer to a <t DIACTIONFORMATW> that receives the converted
* ACTIONFORMAT.
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
HRESULT EXTERNAL DiActionFormatAtoW
(
const LPDIACTIONFORMATA lpDiAfA,
LPDIACTIONFORMATW* lplpDiAfW
)
{
DWORD cbAlloc;
PDWORD pdwStrLen, pdwStrLen0;
LPDIACTIONFORMATW lpDiAfW;
LPDIACTIONA lpDiAA;
LPDIACTIONW lpDiAW;
DWORD i;
HRESULT hres;
EnterProcI(DiActionFormatAtoW, (_ "xx", lpDiAfA, lplpDiAfW));
// Internal function, no validation
*lplpDiAfW = NULL;
/*
* PREFIX complains (mb:37926 - item 2) that we could be requesting a
* zero byte allocation which would not allocate anything. This is
* never the case because CDIDev_ActionMap_IsValidMapObject tests that
* dwNumActions is less than 2^24. Assert in debug for extra safety.
*/
AssertF( (lpDiAfA->dwNumActions +1)*cbX(*pdwStrLen0) );
hres = AllocCbPpv( (lpDiAfA->dwNumActions +1)*cbX(*pdwStrLen0) , &pdwStrLen0);
if ( SUCCEEDED(hres) )
{
// Compute the amount of memory required to clone the DIACTIONFORMATA
cbAlloc =
/* 1: The wide form of the Action Format array */
cbX(DIACTIONFORMATW)
/* 2: Each of the DIACTION arrays */
+ lpDiAfA->dwActionSize * lpDiAfA->dwNumActions;
pdwStrLen = pdwStrLen0;
// Compute the size for each of the text strings in array of DIACTIONs
for ( i = 0x0, lpDiAA = lpDiAfA->rgoAction;
i < lpDiAfA->dwNumActions ;
i++, lpDiAA++ )
{
// Handle the NULL ptr case
if ( NULL != lpDiAA->lptszActionName )
{
/* 3: Text string in each DIACTION array*/
// Conversion from A to U, need multiplier
// ISSUE-2001/03/29-timgill (MarcAnd), A to U conversions are not always 1 to 1.
*pdwStrLen = lstrlenA(lpDiAA->lptszActionName) + 1;
cbAlloc += cbX(lpDiAW->lptszActionName[0]) * ( *pdwStrLen++ );
}
}
if ( SUCCEEDED( hres = AllocCbPpv(cbAlloc, &lpDiAfW) ) )
{
DWORD dwLen;
DWORD cb;
pdwStrLen = pdwStrLen0;
// 1: Copy the DIACTIONFORMAT
/*
* Assert that the structure can be copied as:
* a) dwSize
* b) everything else
* c) the app name
*/
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) == 0 );
CAssertF( FIELD_OFFSET( DIACTIONFORMATA, dwSize ) == 0 );
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, dwSize ) + cbX( ((LPDIACTIONFORMATW)0)->dwSize )
== FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) );
#if defined(_WIN64)
CAssertF( ( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
- ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) )
< MAX_NATURAL_ALIGNMENT ) );
#else
CAssertF( ( cbX( DIACTIONFORMATW ) - cbX( ((LPDIACTIONFORMATW)0)->tszActionMap ) )
== ( cbX( DIACTIONFORMATA ) - cbX( ((LPDIACTIONFORMATA)0)->tszActionMap ) ) );
#endif
CAssertF( FIELD_OFFSET( DIACTIONFORMATW, tszActionMap )
== FIELD_OFFSET( DIACTIONFORMATA, tszActionMap ) );
CAssertF( cA( ((LPDIACTIONFORMATW)0)->tszActionMap ) == cA( ((LPDIACTIONFORMATA)0)->tszActionMap ) );
// Init counts and lpDiAfW fields
dwLen = lpDiAfA->dwActionSize * lpDiAfA->dwNumActions;
cb = lpDiAfW->dwSize = cbX(DIACTIONFORMATW);
memcpy( &lpDiAfW->dwActionSize, &lpDiAfA->dwActionSize,
FIELD_OFFSET( DIACTIONFORMATW, tszActionMap ) - FIELD_OFFSET( DIACTIONFORMATW, dwActionSize ) );
AToU(lpDiAfW->tszActionMap, cbX(lpDiAfA->tszActionMap), lpDiAfA->tszActionMap);
// 2: Block copy the DIACTION array
CAssertF(cbX(*lpDiAfW->rgoAction) == cbX(*lpDiAfA->rgoAction) )
lpDiAfW->rgoAction = (LPDIACTIONW)( (char*)lpDiAfW + cb);
dwLen = lpDiAfA->dwActionSize * lpDiAfA->dwNumActions;
memcpy(lpDiAfW->rgoAction, lpDiAfA->rgoAction, dwLen);
cb += dwLen;
// 3: ActionName
// Convert each of the strings in the ACTION array from A to W
for ( i = 0x0, lpDiAA=lpDiAfA->rgoAction, lpDiAW=lpDiAfW->rgoAction;
i < lpDiAfW->dwNumActions ;
i++, lpDiAA++, lpDiAW++ )
{
if ( lpDiAA->lptszActionName != NULL )
{
WCHAR* wsz = (WCHAR*) ((char*)lpDiAfW+cb);
lpDiAW->lptszActionName = wsz;
dwLen = (*pdwStrLen++);
AToU( wsz, dwLen, lpDiAA->lptszActionName);
cb += dwLen * cbX(lpDiAW->lptszActionName[0]) ;
} else
{
// Resource strings are handled in DiActionFormatWtoW
// OK for now, as long as UI always uses CloneDiActionFormatW
lpDiAW->lptszActionName = NULL;
}
}
// If we have not done something goofy, the memory allocates should match
// the memory we used
AssertF(cbAlloc == cb );
*lplpDiAfW = lpDiAfW;
}
FreePpv(&pdwStrLen0);
}
ExitOleProc();
return(hres);
}
STDMETHODIMP CDIObj_ConfigureDevicesA
(
PV pDiA,
LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
LPDICONFIGUREDEVICESPARAMSA lpdiCDParams,
DWORD dwFlags,
LPVOID pvRefData
)
{
HRESULT hres = S_OK;
EnterProcR(IDirectInput8::ConfigureDevices,
(_ "pppxp", pDiA, lpdiCallback, lpdiCDParams, dwFlags, pvRefData));
/*
* ConfigureDevicesCore will validate the rest.
*/
if ( (SUCCEEDED(hres = hresPvI(pDiA, ThisInterfaceA)) &&
(SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams, sizeof(DICONFIGUREDEVICESPARAMSA), 2)) &&
((lpdiCDParams->lptszUserNames == NULL) || (SUCCEEDED(hres = hresFullValidReadStrA((LPSTR)(lpdiCDParams->lptszUserNames), MAX_JOYSTRING * (lpdiCDParams->dwcUsers), 2)))) &&
(SUCCEEDED(hres = hresFullValidReadPvCb(lpdiCDParams->lprgFormats, lpdiCDParams->dwcFormats*sizeof(DIACTIONFORMATA), 2))))))
{
if( lpdiCDParams->dwSize != cbX(DICONFIGUREDEVICESPARAMSA) )
{
RPF("IDirectInput::%s: Invalid DICONFIGUREDEVICESPARAMSA.dwSize 0x%08x",
lpdiCDParams->dwSize );
hres = E_INVALIDARG;
}
if (SUCCEEDED(hres))
{
PDDI this = _thisPvNm(pDiA, diA);
//params structure
DICONFIGUREDEVICESPARAMSW diconfparamsW;
//to translate each DIACTIONFORMAT
LPDIACTIONFORMATW* lpDiAfW = NULL;
//the new array of DIACTIONFORMATs
LPDIACTIONFORMATW* lpDAFW = NULL;
//to traverse the old array
LPDIACTIONFORMATA lpDIFormat;
//to traverse the new array
LPDIACTIONFORMATW lpDIF;
//to keep the new user name
LPWSTR lpUserNames = NULL;
//to know how many DIACTIONFORMATS we have cloned successfully
DWORD clonedF = 0;
//kength of user names string
DWORD strLen = 0;
//zero out
ZeroMemory(&diconfparamsW, sizeof(DICONFIGUREDEVICESPARAMSW));
//set the size
diconfparamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW);
//1. Validate and translate each LPDIACTIONFORMAT in the array
lpDIFormat = (lpdiCDParams->lprgFormats);
//allocate the new array
hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &diconfparamsW.lprgFormats);
if (FAILED(hres))
{
goto cleanup;
}
lpDIF = diconfparamsW.lprgFormats;
//allocate the cloning array
hres = AllocCbPpv(lpdiCDParams->dwcFormats * sizeof(DIACTIONFORMATW), &lpDAFW);
if (FAILED(hres))
{
goto cleanup;
}
lpDiAfW = lpDAFW;
//clone
for (clonedF = 0; clonedF < lpdiCDParams->dwcFormats; clonedF ++)
{
//validate
hres = IsValidMapObjectA(lpDIFormat D(comma s_szProc comma 2));
if (FAILED(hres))
{
goto cleanup;
}
//translate
hres = DiActionFormatAtoW(lpDIFormat, lpDiAfW);
if (FAILED(hres))
{
goto cleanup;
}
//save
*lpDIF = *(*lpDiAfW);
//move on
lpDIFormat++;
lpDiAfW++;
lpDIF++;
}
//if everything went fine, should have cloned all
AssertF(clonedF == lpdiCDParams->dwcFormats);
//2. Copy the user names
if (lpdiCDParams->lptszUserNames != NULL)
{
DWORD countN;
DWORD Len;
//to traverse new user names
WCHAR* lpNameW;
CHAR* lpName = lpdiCDParams->lptszUserNames;
//go throught all the user names
for ( countN = 0; countN < lpdiCDParams->dwcUsers; countN ++)
{
hres = hresFullValidReadStrA(lpName, MAX_JOYSTRING, 2);
if (FAILED(hres))
{
goto cleanup;
}
Len = lstrlenA(lpName);
//if length is 0 -- and we haven't reached the correct user count yet -
//then it is an error
if (Len == 0)
{
hres = DIERR_INVALIDPARAM;
goto cleanup;
}
//move on to the next user name
strLen += Len + 1;
lpName += Len + 1;
}
//if everything went fine, should have traversed all the user names
AssertF(countN == lpdiCDParams->dwcUsers);
//allocate
hres = AllocCbPpv( (strLen + 1) * 2, &lpUserNames );
if (FAILED(hres))
{
goto cleanup;
}
//translate
//AToU stops at the first '\0', so we have to go in a loop
lpName = lpdiCDParams->lptszUserNames;
lpNameW = lpUserNames;
//go throught all the user names
for ( countN = 0; countN < lpdiCDParams->dwcUsers; countN ++)
{
Len = lstrlenA(lpName);
AToU(lpNameW, Len + 1, lpName);
lpName += Len + 1;
lpNameW += Len + 1;
}
//save
diconfparamsW.lptszUserNames = lpUserNames;
}
//3. Populate the rest of the structure
diconfparamsW.dwcUsers = lpdiCDParams->dwcUsers;
diconfparamsW.dwcFormats = clonedF;
diconfparamsW.hwnd = lpdiCDParams->hwnd;
diconfparamsW.dics = lpdiCDParams->dics;
diconfparamsW.lpUnkDDSTarget = lpdiCDParams->lpUnkDDSTarget;
//4.Call the framework
hres = CDIObj_ConfigureDevicesCore
(
&this->diW,
lpdiCallback,
&diconfparamsW,
dwFlags,
pvRefData);
cleanup:;
//free the sapce for the array
FreePpv(&diconfparamsW.lprgFormats);
//free as many DIACTIONFORMATs as were created
if (lpDAFW)
{
lpDiAfW = lpDAFW;
for (clonedF; clonedF > 0; clonedF--)
{
FreeDiActionFormatW(lpDiAfW);
lpDiAfW++;
}
//delete the entire block
FreePpv(&lpDAFW);
}
//free the user names, if allocated
FreePpv(&lpUserNames);
}
}
ExitOleProc();
return(hres);
}
/*****************************************************************************
*
* The long-awaited vtbls and templates
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
#define CDIObj_Signature 0x504E4944 /* "DINP" */
Interface_Template_Begin(CDIObj)
Primary_Interface_Template(CDIObj, TFORM(ThisInterfaceT))
Secondary_Interface_Template(CDIObj, SFORM(ThisInterfaceT))
Interface_Template_End(CDIObj)
Primary_Interface_Begin(CDIObj, TFORM(ThisInterfaceT))
TFORM(CDIObj_CreateDevice),
TFORM(CDIObj_EnumDevices),
TFORM(CDIObj_GetDeviceStatus),
TFORM(CDIObj_RunControlPanel),
TFORM(CDIObj_Initialize),
TFORM(CDIObj_FindDevice),
TFORM(CDIObj_EnumDevicesBySemantics),
TFORM(CDIObj_ConfigureDevices),
Primary_Interface_End(CDIObj, TFORM(ThisInterfaceT))
Secondary_Interface_Begin(CDIObj, SFORM(ThisInterfaceT), SFORM(di))
SFORM(CDIObj_CreateDevice),
SFORM(CDIObj_EnumDevices),
SFORM(CDIObj_GetDeviceStatus),
SFORM(CDIObj_RunControlPanel),
SFORM(CDIObj_Initialize),
SFORM(CDIObj_FindDevice),
SFORM(CDIObj_EnumDevicesBySemantics),
SFORM(CDIObj_ConfigureDevices),
Secondary_Interface_End(CDIObj, SFORM(ThisInterfaceT), SFORM(di))