678 lines
17 KiB
C
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;
|
|
}
|
|
|