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
111 KiB
3490 lines
111 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))
|