windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/dx8/dll/didenum.c
2020-09-26 16:20:57 +08:00

747 lines
20 KiB
C

/*****************************************************************************
*
* DIDEnum.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* The IDirectInput device enumerator.
*
* We don't bother making this an honest OLE enumerator because
* there's no point. There's no way to access it from the outside.
*
* Contents:
*
* CDIDEnum_New
* CDIDEnum_Next
* CDIDEnum_Release
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflDEnum
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct _CDIDEnum |
*
* Records the state of a device enumeration. Note that this
* is not free-threaded.
*
* @field PDIW | pdiW |
*
* The <i IDirectInputW> object that owns the enumeration.
*
* @field DWORD | dwDevType |
*
* Device type filter.
*
* @field DWORD | edfl |
*
* Enumeration flags.
*
* @field int | idosdStatic |
*
* The next static device to enumerate. Static devices live
* in <c c_rgdosdStatic>.
*
* @field DWORD | dwVer |
*
* Version of DirectX we are emulating.
*
* If we are emulating DirectX 3.0 or less, then don't
* reveal joysticks.
*
* @field int | idosdDynamic |
*
* The next dynamic device to enumerate. Dyanmic devices
* are kept in the <e CDIDEnum.rgdosdDynamic> array. They
* are snapshotted into the enumeration structure to avoid
* race conditions if a device comes or goes while we are
* in the middle of an enumeration.
*
* @field PHIDDEVICELIST | phdl |
*
* List of HID devices to be returned by the enumeration.
*
*****************************************************************************/
typedef struct _CDIDEnum
{
D(DWORD dwSig;)
PDIW pdiW;
DWORD dwDevType;
DWORD edfl;
int idosdStatic;
DWORD dwVer;
int idosdDynamic;
PHIDDEVICELIST phdl;
PDIDW pdidW;
} DENUM, *PDENUM, **PPDENUM;
#define CDIDENUM_SIGNATURE 0x4D554E45 /* "ENUM" */
#define AssertPde(pde) AssertF((pde)->dwSig == CDIDENUM_SIGNATURE)
/*****************************************************************************
*
* @doc INTERNAL
*
* @global DIOBJECTSTATICDATA | c_rgdosdStatic[] |
*
* Right now, the list of device is static and hard-coded.
* Eventually, we'll
* use plug and play to enumerate devices of class "input" and
* get information from their config/software keys.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
/*
* Our array of static joystick instance guids.
*
*/
GUID rgGUID_Joystick[cJoyMax] = {
{ 0x6F1D2B70,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B71,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B72,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B73,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B74,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B75,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B76,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B77,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B78,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B79,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B7A,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B7B,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B7C,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B7D,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B7E,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
{ 0x6F1D2B7F,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00},
};
#if cJoyMax != 16
#error rgGUID_Joystick supports only 16 joysticks.
#endif
/*
* Note that we recycle the SysMouse GUID as the instance GUID too,
* since there will never be more than one system mouse installed in
* the system. Similarly for SysKeyboard.
*/
DIOBJECTSTATICDATA c_rgdosdStatic[] = {
{ &GUID_SysMouse, DI8DEVTYPE_MOUSE, CMouse_New,},
{ &GUID_SysMouseEm, DI8DEVTYPE_MOUSE, CMouse_New,},
{ &GUID_SysMouseEm2, DI8DEVTYPE_MOUSE, CMouse_New,},
{ &GUID_SysKeyboard, DI8DEVTYPE_KEYBOARD, CKbd_New,},
{ &GUID_SysKeyboardEm, DI8DEVTYPE_KEYBOARD, CKbd_New,},
{ &GUID_SysKeyboardEm2, DI8DEVTYPE_KEYBOARD, CKbd_New,},
#ifndef WINNT
/*
* On WINNT all joysticks are HID devices.
* So it is pointless to include predefined
* Joystick GUIDs
*/
#define MAKEJOY(n) \
{ &rgGUID_Joystick[n],DI8DEVCLASS_GAMECTRL, CJoy_New, }
MAKEJOY( 0),
MAKEJOY( 1),
MAKEJOY( 2),
MAKEJOY( 3),
MAKEJOY( 4),
MAKEJOY( 5),
MAKEJOY( 6),
MAKEJOY( 7),
MAKEJOY( 8),
MAKEJOY( 9),
MAKEJOY(10),
MAKEJOY(11),
MAKEJOY(12),
MAKEJOY(13),
MAKEJOY(14),
MAKEJOY(15),
#undef MAKEJOY
#endif
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | hresFindInstanceGUID |
*
* Locates information given an instance GUID.
*
* @parm IN PCGUID | pguid |
*
* The instance GUID to be located.
*
* @parm OUT CREATEDCB * | pcdcb |
*
* Receives pointer to the <f CreateDcb> function for the object.
*
*****************************************************************************/
HRESULT EXTERNAL
hresFindInstanceGUID_(PCGUID pguid, CREATEDCB *pcdcb,
LPCSTR s_szProc, int iarg)
{
HRESULT hres;
EnterProcS(hresFindInstance, (_ "G", pguid));
if(SUCCEEDED(hres = hresFullValidGuid(pguid, iarg)))
{
int idosd;
/*
* First try the list of static devices. Since this
* list never changes, we don't need to protect it
* with a critical section.
*/
for(idosd = 0; idosd < cA(c_rgdosdStatic); idosd++)
{
if(IsEqualGUID(pguid, c_rgdosdStatic[idosd].rguidInstance))
{
*pcdcb = c_rgdosdStatic[idosd].CreateDcb;
goto done;
}
}
/*
* So it wasn't one of the static devices. See if it's
* one of the dynamic HID devices we've already found.
*/
hres = hresFindHIDInstanceGUID(pguid, pcdcb);
if(FAILED(hres))
{
/*
* Not on our list of dynamic HID devices.
* Re-enumerate them and try again. Maybe it was
* for a device that we recently added.
*/
DIHid_BuildHidList(TRUE);
hres = hresFindHIDInstanceGUID(pguid, pcdcb);
}
if(FAILED(hres))
{
#ifdef WINNT
/*
* NT Bug#351951.
* If they are directly asking for one of the predefined joystick
* IDs then see if we have a device mapped to that ID. If so,
* pretend they asked for that GUID instead.
*/
/*
* Weakly Assert the range of predefined static joystick instance GUIDs
*/
AssertF( ( rgGUID_Joystick[0].Data1 & 0x0f ) == 0 );
AssertF( ( rgGUID_Joystick[0x0f].Data1 & 0x0f ) == 0x0f );
/*
* Check the GUID is the same as the first static one ignoring LS 4 bits
*/
if( ( (pguid->Data1 & 0xf0) == (rgGUID_Joystick[0].Data1 & 0xf0) )
&& !memcmp( ((PBYTE)&rgGUID_Joystick)+1, ((PBYTE)pguid)+1, sizeof(*pguid) - 1 ) )
{
RPF("%s: Using predefined instance GUIDs is bad and should not work!", s_szProc);
if( phdiFindJoyId( pguid->Data1 & 0x0f ) )
{
*pcdcb = CHid_New;
hres = S_OK;
}
else
{
*pcdcb = 0;
hres = DIERR_DEVICENOTREG;
}
}
else
{
RPF("%s: Warning: GUID is not installed in this system", s_szProc);
*pcdcb = 0;
hres = DIERR_DEVICENOTREG;
}
#else
RPF("%s: Warning: GUID is not installed in this system", s_szProc);
*pcdcb = 0;
hres = DIERR_DEVICENOTREG;
#endif
}
}
done:;
ExitOleProcPpv(pcdcb);
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CDIDEnum_Release |
*
* Free the enumeration object and its associated resources.
*
* @parm CDIDEnum * | pde |
*
* The enumeration state to be released.
*
*****************************************************************************/
void EXTERNAL
CDIDEnum_Release(PDENUM pde)
{
EnterProcI(CDIDEnum_Release, (_ "p", pde));
AssertPde(pde);
/*
* First, release any last enumerated device
*/
if( pde->pdidW )
{
OLE_Release(pde->pdidW);
}
OLE_Release(pde->pdiW);
FreePpv(&pde->phdl);
FreePv(pde);
}
#define S_SKIP hresUs(2)
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | hresIsDeviceTypeMatch |
*
* Test if a device type matches the filter.
*
* @parm BYTE | bFilter |
*
* The device type or class to filter by.
*
* @parm BYTE | bDevType |
*
* The device type to be tested.
*
* @returns
*
* Returns <c S_OK> if the device matches the filter
* or <c S_SKIP> if it does not.
*
*****************************************************************************/
HRESULT hresIsDeviceTypeMatch
(
BYTE bFilter,
BYTE bDevType
)
{
HRESULT hres;
AssertF( bDevType >= DI8DEVTYPE_MIN );
AssertF( bDevType < DI8DEVTYPE_MAX );
if( ( bFilter == 0 )
|| ( bFilter == bDevType ) )
{
hres = S_OK;
}
else
{
hres = S_SKIP;
switch( bFilter )
{
case DI8DEVCLASS_DEVICE:
if( bDevType == DI8DEVTYPE_DEVICE )
{
hres = S_OK;
}
break;
case DI8DEVCLASS_POINTER:
if( ( bDevType == DI8DEVTYPE_MOUSE )
|| ( bDevType == DI8DEVTYPE_SCREENPOINTER ) )
{
hres = S_OK;
}
break;
case DI8DEVCLASS_KEYBOARD:
if( bDevType == DI8DEVTYPE_KEYBOARD )
{
hres = S_OK;
}
break;
case DI8DEVCLASS_GAMECTRL:
if( (( bDevType >= DI8DEVTYPE_GAMEMIN )
&& ( bDevType < DI8DEVTYPE_GAMEMAX )) ||
( bDevType == DI8DEVTYPE_SCREENPOINTER ) || /* Windows bug 385284 (jacklin) - DI8DEVCLASS_GAMECTRL should */
( bDevType == DI8DEVTYPE_SUPPLEMENTAL ) ) /* include DI8DEVTYPE_SCREENPOINTER and DI8DEVTYPE_SUPPLEMENTAL */
{
hres = S_OK;
}
break;
}
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDEnum_Next |
*
* Return the next device.
*
* Note that this is not the same as the OLE <mf IEnumXxx::Next>
* function. Not that it'd be hard to convert over; it's just
* not needed yet.
*
* @parm CDIDEnum * | pde |
*
* Maintains enumeration state.
*
* @parm LPGUID | pguid |
*
* Receives the enumerated GUID.
*
* @parm LPDIDEVICEINSTANCEW | pddiW |
*
* Receives device attributes.
*
* @returns
*
* Returns <c S_OK> if the object was successfully obtained,
* or <c S_FALSE> if there aren't any more objects.
*
* Warning! <f CDIObj_EnumDevicesW> assumes that this function
* cannot fail.
*
*****************************************************************************/
STDMETHODIMP
CDIDEnum_Next(PDENUM pde, LPDIDEVICEINSTANCEW pddiW)
{
HRESULT hres;
EnterProcI(CDIDEnum_Next, (_ "p", pde));
AssertPde(pde);
AssertF(pddiW->dwSize == cbX(*pddiW));
/*
* Keep going until something works.
*/
do
{
PDIOBJECTSTATICDATA pdosd;
/*
* Release any previously enumerated or looked at device
*/
if( pde->pdidW )
{
OLE_Release(pde->pdidW);
pde->pdidW = NULL;
}
/*
* Pull one from the static list first.
* If that is empty, then pull from the dynamic list.
* If that is empty, then we're done.
*/
if(pde->idosdStatic < cA(c_rgdosdStatic))
{
pdosd = &c_rgdosdStatic[pde->idosdStatic++];
} else if(pde->phdl && pde->idosdDynamic < pde->phdl->chdi)
{
pdosd = &pde->phdl->rghdi[pde->idosdDynamic].osd;
pdosd->rguidInstance = &pde->phdl->rghdi[pde->idosdDynamic].guid;
pde->idosdDynamic++;
} else
{
hres = S_FALSE;
goto done;
}
/*
* ISSUE-2001/03/03-MarcAnd Filtered device enumerations are slow
* Since DI8DEVTYPEs can be generated on the fly and can be
* overridden we can no longer filter before creating the device.
* This is badness. Ideally, we need to add the WinMM and system
* devices to our dynamic device list, tidy that info up and add
* all the info we need to that list.
*/
if( 1 )
{
hres = IDirectInput_CreateDevice(pde->pdiW, pdosd->rguidInstance,
(PV)&pde->pdidW, 0);
if(SUCCEEDED(hres))
{
if(CDIObj_TestDeviceFlags(pde->pdidW, pde->edfl) == S_OK)
{
pddiW->dwSize = cbX(*pddiW);
hres = IDirectInputDevice_GetDeviceInfo(pde->pdidW, pddiW);
if( SUCCEEDED( hres ) )
{
AssertF( IsEqualGUID(pdosd->rguidInstance, &pddiW->guidInstance) );
hres = hresIsDeviceTypeMatch( GET_DIDEVICE_TYPE(pde->dwDevType), GET_DIDEVICE_TYPE(pddiW->dwDevType) );
}
} else
{
hres = S_SKIP;
}
} else
{
hres = S_SKIP;
}
} else
{
hres = S_SKIP;
}
} while(hres == S_SKIP);
done:;
AssertF(hres == S_OK || hres == S_FALSE);
ScrambleBit(&pddiW->dwDevType, DIDEVTYPE_RANDOM);
return hres;
}
STDMETHODIMP
CDIDEnum_InternalNext(PDENUM pde, LPDIDEVICEINSTANCEW pddiW, LPDIRECTINPUTDEVICE8W *ppdid8W)
{
HRESULT hres;
EnterProcI(CDIDEnum_Next, (_ "p", pde));
AssertPde(pde);
AssertF(pddiW->dwSize == cbX(*pddiW));
/*
* Keep going until something works.
*/
do
{
PDIOBJECTSTATICDATA pdosd;
/*
* Release any previously enumerated or looked at device
*/
if( pde->pdidW )
{
OLE_Release(pde->pdidW);
pde->pdidW = NULL;
}
/*
* Pull one from the static list first.
* If that is empty, then pull from the dynamic list.
* If that is empty, then we're done.
*/
if(pde->idosdStatic < cA(c_rgdosdStatic))
{
pdosd = &c_rgdosdStatic[pde->idosdStatic++];
} else if(pde->phdl && pde->idosdDynamic < pde->phdl->chdi)
{
pdosd = &pde->phdl->rghdi[pde->idosdDynamic].osd;
pdosd->rguidInstance = &pde->phdl->rghdi[pde->idosdDynamic].guid;
pde->idosdDynamic++;
} else
{
hres = S_FALSE;
goto done;
}
hres = IDirectInput_CreateDevice(pde->pdiW, pdosd->rguidInstance,
(PV)&pde->pdidW, 0);
if(SUCCEEDED(hres))
{
if(CDIObj_TestDeviceFlags(pde->pdidW, pde->edfl) == S_OK)
{
pddiW->dwSize = cbX(*pddiW);
hres = IDirectInputDevice_GetDeviceInfo(pde->pdidW, pddiW);
*ppdid8W = (LPDIRECTINPUTDEVICE8W)pde->pdidW;
AssertF(fLimpFF(SUCCEEDED(hres),
IsEqualGUID(pdosd->rguidInstance,
&pddiW->guidInstance)));
/*
* Do filtering here (see ISSUE in CDIDEnum_Next for why)
*/
hres = hresIsDeviceTypeMatch( GET_DIDEVICE_TYPE(pde->dwDevType), GET_DIDEVICE_TYPE(pddiW->dwDevType) );
} else
{
hres = S_SKIP;
}
} else
{
hres = S_SKIP;
}
} while(hres == S_SKIP);
done:;
AssertF(hres == S_OK || hres == S_FALSE);
ScrambleBit(&pddiW->dwDevType, DIDEVTYPE_RANDOM);
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDEnum_New |
*
* Create an enumeration object.
*
* The enumeration object snapshots the system device state
* and farms them out one at a time.
*
* @parm PDIW | pdiW |
*
* Parent <i IDirectInputW> we piggyback off of for device
* creation.
*
* @field DWORD | dwDevType |
*
* Device type filter.
*
* @field DWORD | edfl |
*
* Enumeration flags.
*
* @field DWORD | dwVer |
*
* Version of DirectX we are emulating.
*
* This should always be DirectX 8.0.
*
* @parm CDIDEnum ** | ppde |
*
* Receives the enumeration object.
*
* @returns
*
* Returns <c S_OK> on success or an error code on failure.
*
*****************************************************************************/
STDMETHODIMP
CDIDEnum_New(PDIW pdiW, DWORD dwDevType, DWORD edfl, DWORD dwVer, PPDENUM ppde)
{
HRESULT hres;
EnterProcI(CDIDEnum_New, (_ "pxx", pdiW, dwDevType, edfl));
/*
* Refresh the HID device list so the enumeration is fresh.
*/
DIHid_BuildHidList(TRUE);
hres = AllocCbPpv(cbX(CDIDEnum), ppde);
if(SUCCEEDED(hres))
{
PDENUM pde = *ppde;
D(pde->dwSig = CDIDENUM_SIGNATURE);
pde->pdiW = pdiW;
pde->dwDevType = dwDevType;
pde->edfl = edfl;
pde->dwVer = dwVer;
/*
* Make sure last enumerated device pointer is cleared
*/
pde->pdidW = NULL;
AssertF(pde->idosdStatic == 0);
/*
* If enumerating only HID devices, then skip over all
* the static (non-HID) devices. This is important so
* we don't go into infinite recursion death with WINMM.DLL,
* which does an enumeration to find HID joysticks
* in the first place.
*/
if(pde->dwDevType & DIDEVTYPE_HID)
{
pde->idosdStatic = cA(c_rgdosdStatic);
}
AssertF(pde->idosdDynamic == 0);
/*
* Clone the device list. This must be done under the
* critical section to avoid races.
*/
DllEnterCrit();
if(g_phdl)
{
hres = AllocCbPpv(cbHdlChdi(g_phdl->chdi), &pde->phdl);
if(SUCCEEDED(hres))
{
CopyMemory(pde->phdl, g_phdl, cbHdlChdi(g_phdl->chdi));
SquirtSqflPtszV(sqfl, TEXT("%S: Have %d HID devices"),
s_szProc, pde->phdl->chdi);
hres = S_OK;
}
} else
{
hres = S_OK;
}
DllLeaveCrit();
if(SUCCEEDED(hres))
{
OLE_AddRef(pde->pdiW);
hres = S_OK;
}
}
ExitOleProcPpv(ppde);
return hres;
}