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

678 lines
17 KiB
C

/*****************************************************************************
*
* DIHel.c
*
* Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* Hardware emulation layer for DirectInput.
*
* Contents:
*
* Hel_AcquireInstance
* Hel_UnacquireInstance
* Hel_SetBufferSize
* Hel_DestroyInstance
*
* Hel_SetDataFormat
* Hel_SetNotifyHandle
*
* Hel_Mouse_CreateInstance
* Hel_Kbd_CreateInstance
* Hel_Kbd_InitKeys
* Hel_Joy_CreateInstance
* Hel_Joy_Ping
* Hel_Joy_GetInitParms
*
* IoctlHw
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflHel
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | IoctlHw |
*
* Send the IOCtl to the hardware device.
*
* @parm DWORD | ioctl |
*
* I/O control code.
*
* @parm IN LPVOID | pvIn |
*
* Optional input parameter.
*
* @parm DWORD | cbIn |
*
* Size of input buffer in bytes.
*
* @parm IN LPVOID | pvOut |
*
* Optional output parameter.
*
* @parm DWORD | cbOut |
*
* Size of output buffer in bytes.
*
* @returns
*
* <c S_OK> if the ioctl succeeded and returned the correct
* number of bytes, else something based on the Win32 error code.
*
*****************************************************************************/
#ifndef WINNT
HRESULT EXTERNAL
IoctlHw(DWORD ioctl, LPVOID pvIn, DWORD cbIn, LPVOID pvOut, DWORD cbOut)
{
HRESULT hres;
DWORD cbRc;
if (g_hVxD != INVALID_HANDLE_VALUE) {
if (DeviceIoControl(g_hVxD, ioctl, pvIn, cbIn,
pvOut, cbOut, &cbRc, 0)) {
if (cbRc == cbOut) {
hres = S_OK;
} else {
SquirtSqflPtszV(sqfl, TEXT("Ioctl(%08x) returned wrong cbOut"),
ioctl);
hres = DIERR_BADDRIVERVER;
}
} else {
SquirtSqflPtszV(sqfl | sqflError,
TEXT("Ioctl(%08x) failed, error %d"),
ioctl, GetLastError());
hres = hresLe(GetLastError());
}
} else {
hres = DIERR_BADDRIVERVER;
}
return hres;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_IoctlChoose |
*
* Send the IOCtl to the hardware device if it is native,
* or perform the operation through emulation if it is emulated.
*
* @parm PVXDINSTANCE | pvi |
*
* The device in question.
*
* @parm PFNHANDLER | pfn |
*
* The emulation function to call to carry out the operation.
*
* @parm DWORD | ioctl |
*
* I/O control code.
*
* @parm IN LPVOID | pvIn |
*
* Optional input parameter.
*
* @parm DWORD | cbIn |
*
* Size of input buffer in bytes.
*
*****************************************************************************/
typedef HRESULT (EXTERNAL *PFNHANDLER)(PV pv);
HRESULT INTERNAL
Hel_IoctlChoose(PVXDINSTANCE pvi, PFNHANDLER pfn,
DWORD ioctl, LPVOID pvIn, DWORD cbIn)
{
HRESULT hres;
if (!(pvi->fl & VIFL_EMULATED)) {
hres = IoctlHw(ioctl, pvIn, cbIn, 0, 0);
} else {
hres = pfn(pvIn);
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_AcquireInstance |
*
* Attempt to acquire the device instance, using either the
* device driver or emulation, whichever is appropriate.
*
* @parm PVXDINSTANCE | pvi |
*
* The instance to acquire.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_AcquireInstance(PVXDINSTANCE pvi)
{
return Hel_IoctlChoose(pvi, CEm_AcquireInstance,
IOCTL_ACQUIREINSTANCE, &pvi, cbX(pvi));
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_UnacquireInstance |
*
* Attempt to unacquire the device instance.
*
* @parm PVXDINSTANCE | pvi |
*
* The instance to unacquire.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_UnacquireInstance(PVXDINSTANCE pvi)
{
return Hel_IoctlChoose(pvi, CEm_UnacquireInstance,
IOCTL_UNACQUIREINSTANCE, &pvi, cbX(pvi));
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_SetBufferSize |
*
* Set the buffer size.
*
* @parm PVXDDWORDDATA | pvdd |
*
* Information about the buffer size.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_SetBufferSize(PVXDDWORDDATA pvdd)
{
HRESULT hres;
EnterProc(Hel_SetBufferSize, (_ "pxx", pvdd->pvi, pvdd->dw, pvdd->pvi->fl));
hres = Hel_IoctlChoose(pvdd->pvi, CEm_SetBufferSize,
IOCTL_SETBUFFERSIZE, pvdd, cbX(*pvdd));
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_DestroyInstance |
*
* Destroy the device instance in the appropriate way.
*
* @parm PVXDINSTANCE | pvi |
*
* The instance.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_DestroyInstance(PVXDINSTANCE pvi)
{
return Hel_IoctlChoose(pvi, CEm_DestroyInstance,
IOCTL_DESTROYINSTANCE, &pvi, cbX(pvi));
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_SetDataFormat |
*
* Set the data format.
*
* @parm PVXDDATAFORMAT | pvdf |
*
* Information about the data format.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_SetDataFormat(PVXDDATAFORMAT pvdf)
{
return Hel_IoctlChoose(pvdf->pvi, CEm_SetDataFormat,
IOCTL_SETDATAFORMAT, pvdf, cbX(*pvdf));
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_SetNotifyHandle |
*
* Set the event handle for notification.
*
* @parm PVXDDWORDDATA | pvdd |
*
* 9x: dw = ring 0 handle. Dinput calls _OpenVxDHandle to get ring 0 handle.
* NT: dw = ring 3 handle. DINPUT.SYS translates the handle to pointer.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_SetNotifyHandle(PVXDDWORDDATA pvdd)
{
HRESULT hres;
if (!(pvdd->pvi->fl & VIFL_EMULATED)) {
#ifndef WINNT
AssertF(_OpenVxDHandle);
if (pvdd->dw) {
pvdd->dw = _OpenVxDHandle((HANDLE)pvdd->dw);
}
#endif
hres = IoctlHw(IOCTL_SETNOTIFYHANDLE, pvdd, cbX(*pvdd), 0, 0);
} else {
hres = S_OK;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct CREATEDEVICEINFO |
*
* Describes how to create the device either via the driver or
* via emulation.
*
* @parm DWORD | dwIoctl |
*
* IOCtl code to try.
*
* @parm DWORD | flEmulation |
*
* Flag in registry that forces emulation.
*
* @parm EMULATIONCREATEPROC | pfnCreate |
*
* Function that creates emulation object.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
typedef HRESULT (EXTERNAL *EMULATIONCREATEPROC)
(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut);
typedef struct CREATEDEVICEINFO {
DWORD dwIoctl;
DWORD flEmulation;
EMULATIONCREATEPROC pfnCreate;
} CREATEDEVICEINFO, *PCREATEDEVICEINFO;
CREATEDEVICEINFO c_cdiMouse = {
IOCTL_MOUSE_CREATEINSTANCE,
DIEMFL_MOUSE,
CEm_Mouse_CreateInstance,
};
CREATEDEVICEINFO c_cdiKbd = {
IOCTL_KBD_CREATEINSTANCE,
DIEMFL_KBD | DIEMFL_KBD2,
CEm_Kbd_CreateInstance,
};
CREATEDEVICEINFO c_cdiJoy = {
IOCTL_JOY_CREATEINSTANCE,
DIEMFL_JOYSTICK,
CEm_Joy_CreateInstance,
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_CreateInstance |
*
* Attempt to create the device instance through the driver
* with the specified IOCtl.
*
* If that is not possible, then use the emulation callback.
*
* @parm PCREATEDEVICEINFO | pcdi |
*
* Describes how to create the device.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* Describes the device being created.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* Receives created instance.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_CreateInstance(PCREATEDEVICEINFO pcdi,
PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
{
HRESULT hres;
pdevf->dwEmulation |= g_flEmulation;
pdevf->dwEmulation &= pcdi->flEmulation;
if (pdevf->dwEmulation ||
(FAILED(hres = IoctlHw(pcdi->dwIoctl, pdevf, cbX(*pdevf),
ppviOut, cbX(*ppviOut))))) {
hres = pcdi->pfnCreate(pdevf, ppviOut);
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Mouse_CreateInstance |
*
* Attempt to create the device instance through the driver.
* If that is not possible, then use the emulation layer.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* Describes the device being created.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* Receives created instance.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Mouse_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
{
return Hel_CreateInstance(&c_cdiMouse, pdevf, ppviOut);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Kbd_CreateInstance |
*
* Attempt to create the device instance through the driver.
* If that is not possible, then use the emulation layer.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* Describes the device being created.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* Receives created instance.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Kbd_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
{
return Hel_CreateInstance(&c_cdiKbd, pdevf, ppviOut);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Kbd_InitKeys |
*
* Tell the device driver (or emulation) about the key state.
*
* @parm PVXDDWORDDATA | pvdd |
*
* The instance and the key state.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Kbd_InitKeys(PVXDDWORDDATA pvdd)
{
return Hel_IoctlChoose(pvdd->pvi, CEm_Kbd_InitKeys,
IOCTL_KBD_INITKEYS, pvdd, cbX(*pvdd));
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Joy_CreateInstance |
*
* Attempt to create the device instance through the driver.
* If that is not possible, then use the emulation layer.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* Describes the device being created.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* Receives created instance.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Joy_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
{
return Hel_CreateInstance(&c_cdiJoy, pdevf, ppviOut);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Joy_Ping |
*
* Ask the device driver (or emulation) to get the joystick info.
*
* @parm PVXDDWORDDATA | pvdd |
*
* The instance and the key state.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Joy_Ping(PVXDINSTANCE pvi)
{
return Hel_IoctlChoose(pvi, CEm_Joy_Ping,
IOCTL_JOY_PING8, &pvi, cbX(pvi));
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Joy_ConfigChanged |
*
* Tell vjoyd config has been changed.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Joy_ConfigChanged(DWORD dwFlags)
{
return IoctlHw(IOCTL_JOY_CONFIGCHANGED, &dwFlags, cbX(dwFlags), NULL, 0);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Joy_GetInitParms |
*
* Ask the device driver (or emulation) for
* VJOYD initialization parameters.
*
* In emulation, we assume the internal and external
* IDs are equal (because they may as well be),
* that no flags are set, and there are no versions.
*
* @parm DWORD | dwExternalID |
*
* The external joystick number.
*
* @parm PVXDINITPARMS | pvip |
*
* Receives assorted information.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Joy_GetInitParms(DWORD dwExternalID, PVXDINITPARMS pvip)
{
HRESULT hres;
if ((g_flEmulation & DIEMFL_JOYSTICK) ||
FAILED(hres = IoctlHw(IOCTL_JOY_GETINITPARMS,
&dwExternalID, cbX(dwExternalID),
pvip, cbX(*pvip))) ||
FAILED(hres = pvip->hres)) {
/*
* Do it the emulation way.
*/
ZeroX(*pvip);
pvip->dwId = dwExternalID;
hres = S_OK;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | Hel_Joy_GetAxisCaps |
*
* Obtain a bitmask of the axes supported by the joystick.
* If VJOYD won't tell us, then we figure it out from the
* registry structure passed in.
*
* @parm DWORD | dwExternalID |
*
* The external joystick number.
*
* @parm PVXDAXISCAPS | pvac |
*
* Structure to receive the axis capabilities.
*
* @parm LPJOYREGHWCONFIG | phwc |
*
* The joystick settings as reported by the registry.
*
*****************************************************************************/
HRESULT EXTERNAL
Hel_Joy_GetAxisCaps
(
DWORD dwExternalID,
PVXDAXISCAPS pvac,
LPJOYREGHWCONFIG phwc
)
{
HRESULT hres;
DWORD dwRegAxes;
/*
* Every joystick has an X and Y (no way to tell)
* Mark as position axes or they won't count!
*/
dwRegAxes = JOYPF_X | JOYPF_Y | JOYPF_POSITION;
if (phwc->hws.dwFlags & JOY_HWS_HASZ) {
dwRegAxes |= JOYPF_Z;
}
if ( (phwc->hws.dwFlags & JOY_HWS_HASR) || (phwc->dwUsageSettings & JOY_US_HASRUDDER) ){
dwRegAxes |= JOYPF_R;
}
if (phwc->hws.dwFlags & JOY_HWS_HASU) {
dwRegAxes |= JOYPF_U;
}
if (phwc->hws.dwFlags & JOY_HWS_HASV) {
dwRegAxes |= JOYPF_V;
}
if ((g_flEmulation & DIEMFL_JOYSTICK) ||
FAILED(hres = IoctlHw(IOCTL_JOY_GETAXES,
&dwExternalID, cbX(dwExternalID),
pvac, cbX(*pvac)))) {
/*
* If that didn't work, then just use the registry.
*/
if (phwc->hws.dwFlags & JOY_HWS_HASPOV) {
pvac->dwPos |= JOYPF_POV0;
}
/*
* Old VJOYD clients do not support velocity or any of the
* other stuff.
*/
pvac->dwVel = 0;
pvac->dwAccel = 0;
pvac->dwForce = 0;
hres = S_OK;
}
else
{
/*
* ManBug 28971: Logitech drivers (and probably others) respond to
* probing on axes for which they do not report data so set the
* position axes to whatever is reported in the registry.
* Note this still allows multiple POVs to be picked up as only one
* POV is supported by the registry flags.
*/
pvac->dwPos &= ~JOYPF_ALLAXES;
pvac->dwPos |= dwRegAxes;
}
/*
* CJoy_InitRing3 assumes that this never fails.
*/
AssertF(SUCCEEDED(hres));
return hres;
}