|
|
/*****************************************************************************
* * DIGenJ.c * * Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Generic IDirectInputDevice callback for joystick. * * Contents: * * CJoy_CreateInstance * *****************************************************************************/
#include "dinputpr.h"
#ifndef WINNT
/*****************************************************************************
* * The sqiffle for this file. * *****************************************************************************/
#define sqfl sqflJoy
/*****************************************************************************
* * Declare the interfaces we will be providing. * * WARNING! If you add a secondary interface, you must also change * CJoy_New! * *****************************************************************************/
Primary_Interface(CJoy, IDirectInputDeviceCallback);
/*****************************************************************************
* * Macro "lookup tables". * * iJoyStateAxis# converts an axis name to an axis number in the * DIJOYSTATE structure. * * ibJoyStateAxis# converts the name to an offset. * * Note that the extra axes in DIJOYSTATE2 are arranged in relative * positions just like a DIJOYSTATE. We will exploit this * arrangement frequently. * *****************************************************************************/
#define iJoyStateAxisX 0
#define iJoyStateAxisY 1
#define iJoyStateAxisZ 2
#define iJoyStateAxisRx 3
#define iJoyStateAxisRy 4
#define iJoyStateAxisRz 5
#define iJoyStateAxisS0 6
#define iJoyStateAxisS1 7
#define cJoyStateAxisMax 8
#define iJoyStateAxisSlider iJoyStateAxisS0 /* Hack for macros */
#define cJoyStateAxis 8
#define iobjPositions (cJoyStateAxis * 0)
#define iobjVelocities (cJoyStateAxis * 1)
#define iobjAccels (cJoyStateAxis * 2)
#define iobjForces (cJoyStateAxis * 3)
#define cJoyStateAxisTotal (cJoyStateAxis * 4)
#define ibJoyStateAxisX (iJoyStateAxisX * cbX(LONG))
#define ibJoyStateAxisY (iJoyStateAxisY * cbX(LONG))
#define ibJoyStateAxisZ (iJoyStateAxisZ * cbX(LONG))
#define ibJoyStateAxisRx (iJoyStateAxisRx * cbX(LONG))
#define ibJoyStateAxisRy (iJoyStateAxisRy * cbX(LONG))
#define ibJoyStateAxisRz (iJoyStateAxisRz * cbX(LONG))
#define ibJoyStateAxisS0 (iJoyStateAxisS0 * cbX(LONG))
#define ibJoyStateAxisS1 (iJoyStateAxisS1 * cbX(LONG))
#define ibJoyStateAxisSlider ibJoyStateAxisS0 /* Hack for macros */
#define cJoyStatePOVTotal 4
#define cJoyStateButtonTotal 128
#define cJoyStateObjTotal (cJoyStateAxisTotal + \
cJoyStatePOVTotal + \ cJoyStateButtonTotal)
/*
* The worst-case data format for joysticks. (Christmas-tree) */ VXDAXISCAPS c_vacMax = { JOYPF_ALLCAPS | JOYPF_POSITION, /* dwPos */ JOYPF_ALLCAPS | JOYPF_VELOCITY, /* dwVel */ JOYPF_ALLCAPS | JOYPF_ACCELERATION, /* dwAccel */ JOYPF_ALLCAPS | JOYPF_FORCE, /* dwForce */ };
/*****************************************************************************
* * @doc INTERNAL * * @func UINT | ibJoyStateAxisFromPosAxis | * * Returns the offset of the <p iPosAxis>'th joystick axis * in the <t DIJOYSTATE> structure. * * @parm UINT | uiStateAxis | * * The index of the requested <t JOYPOS> axis. * X, Y, Z, R, U and V are respectively zero through five. * * Remember that we map R to Rz, U to Slider0 and V to Slider1. * * @returns * * The offset relative to the structure. * *****************************************************************************/
const int c_rgibJoyStateAxisFromPosAxis[6] = { FIELD_OFFSET(DIJOYSTATE, lX), /* X */ FIELD_OFFSET(DIJOYSTATE, lY), /* Y */ FIELD_OFFSET(DIJOYSTATE, lZ), /* Z */ FIELD_OFFSET(DIJOYSTATE, lRz), /* R */ FIELD_OFFSET(DIJOYSTATE, rglSlider[0]), /* U */ FIELD_OFFSET(DIJOYSTATE, rglSlider[1]), /* V */ };
UINT INLINE ibJoyStateAxisFromPosAxis(UINT uiPosAxis) { AssertF(uiPosAxis < cA(c_rgibJoyStateAxisFromPosAxis)); return c_rgibJoyStateAxisFromPosAxis[uiPosAxis]; }
/*****************************************************************************
* * @doc INTERNAL * * @func UINT | iJoyStateAxisFromPosAxis | * * Returns the index of the <p iPosAxis>'th joystick axis * in the <t DIJOYSTATE> structure. * * @parm UINT | uiStateAxis | * * The index of the requested <t JOYPOS> axis. * X, Y, Z, R, U and V are respectively zero through five. * * Remember that we map R to Rz, U to Slider0 and V to Slider1. * * @returns * * The offset relative to the structure. * *****************************************************************************/
const int c_rgiJoyStateAxisFromPosAxis[6] = { iJoyStateAxisX, /* X */ iJoyStateAxisY, /* Y */ iJoyStateAxisZ, /* Z */ iJoyStateAxisRz, /* R */ iJoyStateAxisS0, /* U */ iJoyStateAxisS1, /* V */ };
UINT INLINE iJoyStateAxisFromPosAxis(UINT uiPosAxis) { AssertF(uiPosAxis < cA(c_rgiJoyStateAxisFromPosAxis)); return c_rgiJoyStateAxisFromPosAxis[uiPosAxis]; }
/*****************************************************************************
* * @doc INTERNAL * * @func UINT | ibJoyStateAxisFromStateAxis | * * Returns the offset of the <p iStateAxis>'th joystick axis * in the <t DIJOYSTATE> structure. * * @parm UINT | uiStateAxis | * * The index of the requested <t JOYSTATE> axis. * The first eight axes live at the top, and the * later ones (corresponding to velocity, etc.) * live down at the bottom. * * @returns * * The offset relative to the structure. * *****************************************************************************/
const int c_rgibJoyStateAxisFromStateAxis[cJoyStateAxisMax] = { FIELD_OFFSET(DIJOYSTATE, lX), /* X */ FIELD_OFFSET(DIJOYSTATE, lY), /* Y */ FIELD_OFFSET(DIJOYSTATE, lZ), /* Z */ FIELD_OFFSET(DIJOYSTATE, lRx), /* Rx */ FIELD_OFFSET(DIJOYSTATE, lRy), /* Ry */ FIELD_OFFSET(DIJOYSTATE, lRz), /* Rz */ FIELD_OFFSET(DIJOYSTATE, rglSlider[0]), /* S0 */ FIELD_OFFSET(DIJOYSTATE, rglSlider[1]), /* S1 */ };
UINT INLINE ibJoyStateAxisFromStateAxis(UINT uiStateAxis) { AssertF(uiStateAxis < cA(c_rgibJoyStateAxisFromStateAxis)); return c_rgibJoyStateAxisFromStateAxis[uiStateAxis]; }
/*****************************************************************************
* * @doc INTERNAL * * @func UINT | iJoyPosAxisFromStateAxis | * * Convert a <t DIJOYSTATE> axis number back to * a <t JOYPOS> axis number. * * @parm UINT | uiPosAxis | * * The index of the requested <t JOYSTATE> axis. * * @returns * * The corresponding <t JOYPOS> axis number. * *****************************************************************************/
const int c_rgiJoyPosAxisFromStateAxis[8] = { iJoyPosAxisX, /* X */ iJoyPosAxisY, /* Y */ iJoyPosAxisZ, /* Z */ -1, /* Rx */ -1, /* Ry */ iJoyPosAxisR, /* Rz */ iJoyPosAxisU, /* S0 */ iJoyPosAxisV, /* S1 */ };
UINT INLINE iJoyPosAxisFromStateAxis(UINT uiStateAxis) { AssertF(uiStateAxis < cA(c_rgiJoyPosAxisFromStateAxis)); return c_rgiJoyPosAxisFromStateAxis[uiStateAxis]; }
/*****************************************************************************
* * @doc INTERNAL * * @topic Cooking Joystick Data | * * We always fetch joystick data raw, then cook it before * returning it to the application. * * If the app is in "raw" mode, then we don't cook anything. * * If the app is in "cooked" mode, then things get interesting. * * If there is `centered' cooking, then the center point of the * joystick is reported in the center of the virtual range. * * * Joystick properties work like this: * * <c DIPROP_BUFFERSIZE> - No special semantics. * * <c DIPROP_CALIBRATIONMODE> - Specifies whether * cooked or raw data should be returned. If raw data * is requested, then most other properties have no effect. * * The default is cooked. * * <c DIPROP_GRANULARITY> - No special semantics. * * <c DIPROP_RANGE> - This returns the range of values that * can be returned by the axis. For joysticks, this is a * read/write property. (For most devices, it is a read-only * property.) If you change the property, it affects only * your device instance; it does not affect the ranges of other * devices. * * If the axis is in calibration mode, then setting this value * has no immediate effect. * * We also define a few new properties: * * <c DIPROP_CENTER> - This returns the joystick center (neutral) * position. In other words, this is the position that * DirectInput returns when the user has released the joystick * and allowed it to self-center. * When a joystick device is created, the center position is * initially set to midway between the lower and * upper bounds of the range. An application may change the * center position (although I don't see any reason why). * * If the axis is in calibration mode, then setting this value * has no immediate effect. * * <c DIPROP_DEADZONE> - This returns the size of the joystick * dead zone, as a percentage of total range. * * If the axis is in calibration mode, then setting this value * has no immediate effect. * *****************************************************************************/
/*****************************************************************************
* * @doc INTERNAL * * @struct CJoy | * * The <i IDirectInputDeviceCallback> object for the * generic joystick. * * @field IDirectInputDeviceCallback | didc | * * The object (containing vtbl). * * @field PDIJOYSTATE2 | pjsPhys | * * Pointer to physical joystick status information kept down in the * VxD. * * @field UINT | idJoy | * * Joystick identifier for <f joyGetPosEx> and friends. * * @field DWORD | dwPOVGranularity | * * Granularity of the POV control. * * @field HWND | hwnd | * * The window which we have subclassed in order to watch * for joystick reconfiguration messages. * * @field HKEY | hkType | * * The joystick type key opened with <c MAXIMUM_ALLOWED> access. * * @field VXDINSTANCE * | pvi | * * The DirectInput instance handle. * * @field DIDEVCAPS | dc | * * Device capability information. * * @field DIDATAFORMAT | df | * * The dynamically-generated data format based on the * joystick type. * * @field JOYRANGECONVERT | rgjrc | * * Range conversion structures for each axis. * * @field DIJOYCONFIG | cfg | * * Joystick configuration information. * * @field DIJOYTYPEINFO | typi | * * Joystick type information. * * @field PDIDOBJDEFSEM | rgObjSem | * * Pointer to array of semantics mapped to this device, * calculated during init. * * @field DWORD | dwVersion | * * DirectInput version requested by application * * @field DIAPPHACKS | diHacks | * * Application hack flags * * @field HKEY | hkProp | * * Extended properties for device type. Currently we keep * OEMMapFile under this key. * * @comm * * It is the caller's responsibility to serialize access as * necessary. * *****************************************************************************/
typedef struct CJoy {
/* Supported interfaces */ IDirectInputDeviceCallback dcb;
LPDIJOYSTATE2 pjsPhys;
UINT idJoy; DWORD dwPOVGranularity;
HWND hwnd; HKEY hkType; VXDINSTANCE *pvi; DIDEVCAPS dc; DIDATAFORMAT df; JOYRANGECONVERT rgjrc[cJoyStateAxisMax];
DIJOYCONFIG cfg; DIJOYTYPEINFO typi;
PDIDOBJDEFSEM rgObjSem;
DWORD dwVersion;
DIAPPHACKS diHacks;
HKEY hkProp;
} CJoy, DJ, *PDJ;
#define ThisClass CJoy
#define ThisInterface IDirectInputDeviceCallback
#define riidExpected &IID_IDirectInputDeviceCallback
/*****************************************************************************
* * Forward declarations * * These are out of laziness, not out of necessity. * *****************************************************************************/
STDMETHODIMP CJoy_GetFFConfigKey(PDICB pdcb, DWORD sam, PHKEY phk); void INTERNAL CJoy_InitPhysRanges(PDJ this, LPJOYREGHWCONFIG phwc);
LRESULT CALLBACK CJoy_SubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT uid, ULONG_PTR dwRef);
/*****************************************************************************
* * CJoy::QueryInterface (from IUnknown) * CJoy::AddRef (from IUnknown) * CJoy::Release (from IUnknown) * *****************************************************************************/
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | QueryInterface | * * Gives a client access to other interfaces on an object. * * @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 INTERNAL * * @method HRESULT | CJoy | AddRef | * * Increments the reference count for the interface. * * @returns * * Returns the object reference count. * * @xref OLE documentation for <mf IUnknown::AddRef>. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoy | Release | * * Decrements the reference count for the interface. * If the reference count on the object falls to zero, * the object is freed from memory. * * @returns * * Returns the object reference count. * * @xref OLE documentation for <mf IUnknown::Release>. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoy | QIHelper | * * We don't have any dynamic interfaces and simply forward * to <f Common_QIHelper>. * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoy | AppFinalize | * * We don't have any weak pointers, so we can just * forward to <f Common_Finalize>. * * @parm PV | pvObj | * * Object being released from the application's perspective. * *****************************************************************************/
#ifdef DEBUG
Default_QueryInterface(CJoy) Default_AddRef(CJoy) Default_Release(CJoy)
#else
#define CJoy_QueryInterface Common_QueryInterface
#define CJoy_AddRef Common_AddRef
#define CJoy_Release Common_Release
#endif
#define CJoy_QIHelper Common_QIHelper
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | RemoveSubclass | * * Remove our subclass hook on the window. * * The parameter is intentionally misdeclared as a <t PV> * so that this function can double as the <f CJoy_AppFinalize>. * *****************************************************************************/
void INTERNAL CJoy_RemoveSubclass(PV pvObj) { PDJ this = pvObj;
/*
* If there was an old window, then un-subclass it * and release the hold associated with it. * * You might think that there's a race condition here, where * we might unhold the device while the subclass procedure is * still using it. * * Ah, but that's not a problem, because the only message that * the subclass procedure cares about is the joystick * reconfiguration message, and when it is processing that message, * it does its own artificial hold/unhold to keep the device alive * while it dorks on the device. * * Okay, so there *is* a really tiny race condition where we * might nuke the device while the window procedure is studying * the message to decide whether it cares or not. * * Since that is so extremely rare, we close that window by * hacking it: We revalidate the device before partying on it. * Note that the hack is not perfect, but the race window becomes * only a few instructions long that I'm not going to worry about it. * * By wiping out this->hwnd before removing the subclass, we * can reduce the window to very small indeed. */ if (this->hwnd) { HWND hwnd = this->hwnd; this->hwnd = 0; if (!RemoveWindowSubclass(hwnd, CJoy_SubclassProc, 0)) { /*
* The RemoveWindowSubclass can fail if the window * was destroyed behind our back. */ // AssertF(!IsWindow(hwnd));
} Sleep(0); /* Let the worker thread drain */ Common_Unhold(this); } }
/*****************************************************************************
* * @doc INTERNAL * * @func void | CJoy_Finalize | * * Releases the resources of the device. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/
void INTERNAL CJoy_Finalize(PV pvObj) { PDJ this = pvObj;
if (this->pvi) { HRESULT hres;
hres = Hel_DestroyInstance(this->pvi); AssertF(SUCCEEDED(hres)); FreePpv(&this->df.rgodf); FreePpv(&this->rgObjSem );
if (this->hkType) { RegCloseKey(this->hkType); } if( this->hkProp) { RegCloseKey(this->hkProp); } }
}
/*****************************************************************************
* * @doc INTERNAL * * @func void | CJoy_AppFinalize | * * The application has performed its final release. * Remove our window subclass at this point. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/
#define CJoy_AppFinalize CJoy_RemoveSubclass
/*****************************************************************************
* * @doc INTERNAL * * @func LRESULT | CJoy_SubclassProc | * * Window subclass procedure which watches for * joystick configuration change notifications. * * @parm HWND | hwnd | * * The victim window. * * @parm UINT | wm | * * Window message. * * @parm WPARAM | wp | * * Message-specific data. * * @parm LPARAM | lp | * * Message-specific data. * * @parm UINT | uid | * * Callback identification number, always zero. * * @parm DWORD | dwRef | * * Reference data, a pointer to our joystick device callback. * *****************************************************************************/
LRESULT CALLBACK CJoy_SubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT uid, ULONG_PTR dwRef) { #ifdef XDEBUG
static CHAR s_szProc[] = ""; #endif
AssertF(uid == 0);
if (wm == g_wmJoyChanged) { PDJ this = (PDJ)dwRef;
/*
* Wacky subtlety going on here to avoid race conditions. * See the mondo comment block in CJoy_RemoveSubclass * for details. * * We can get faked out if the memory associated with the * CJoy is still physically allocated, the vtbl is magically * still there and the hwnd field somehow matches our hwnd. */ if (SUCCEEDED(hresPv(this)) && this->hwnd == hwnd) {
HRESULT hres;
Common_Hold(this); /*
* We must ask for DIJC_CALLOUT even though we don't care, * because that will trigger the Microsoft Gamepad hack-o-rama. * * Also, make sure we don't decide that we tried recently */ #ifndef WINNT
g_dwLastBonusPoll = GetTickCount() ^ 0x80000000; #endif
hres = JoyReg_GetConfig(this->idJoy, &this->cfg, DIJC_REGHWCONFIGTYPE | DIJC_CALLOUT); if (SUCCEEDED(hres)) { CJoy_InitPhysRanges(this, &this->cfg.hwc); } Common_Unhold(this);
} }
return DefSubclassProc(hwnd, wm, wp, lp); }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetInstance | * * Obtains the DirectInput instance handle. * * @parm OUT PPV | ppvi | * * Receives the instance handle. * *****************************************************************************/
STDMETHODIMP CJoy_GetInstance(PDICB pdcb, PPV ppvi) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::GetInstance, (_ "p", pdcb));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
*ppvi = (PV)this->pvi; hres = S_OK;
ExitOleProcPpvR(ppvi); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetDataFormat | * * Obtains the device's preferred data format. * * @parm OUT LPDIDEVICEFORMAT * | ppdf | * * <t LPDIDEVICEFORMAT> to receive pointer to device format. * * @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 lpmdr> parameter is not a valid pointer. * *****************************************************************************/
STDMETHODIMP CJoy_GetDataFormat(PDICB pdcb, LPDIDATAFORMAT *ppdf) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::GetDataFormat, (_ "p", pdcb));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
*ppdf = &this->df; hres = S_OK;
ExitOleProcPpvR(ppdf); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetDeviceInfo | * * Obtain general information about the device. * * @parm OUT LPDIDEVICEINSTANCEW | pdiW | * * <t DEVICEINSTANCE> to be filled in. The * <e DEVICEINSTANCE.dwSize> and <e DEVICEINSTANCE.guidInstance> * have already been filled in. * * Secret convenience: <e DEVICEINSTANCE.guidProduct> is equal * to <e DEVICEINSTANCE.guidInstance>. * *****************************************************************************/
STDMETHODIMP CJoy_GetDeviceInfo(PDICB pdcb, LPDIDEVICEINSTANCEW pdiW) { HRESULT hres; PDJ this;
EnterProcI(IDirectInputDeviceCallback::Joy::GetDeviceInfo, (_ "pp", pdcb, pdiW));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
AssertF(IsValidSizeDIDEVICEINSTANCEW(pdiW->dwSize));
/*
* Unlike mouse and keyboard, there can be multiple instances of * the same joystick product, so we can't just leave guidProduct * equal to guidInstance. */
pdiW->guidProduct = GUID_Joystick; AssertF(pdiW->guidInstance.Data1 == (pdiW->guidProduct.Data1 | this->idJoy));
pdiW->dwDevType = this->dc.dwDevType;
#ifdef UNICODE
lstrcpyn(pdiW->tszProductName, this->typi.wszDisplayName, cA(pdiW->tszProductName)); #else
CAssertF(cA(pdiW->tszProductName) >= cA(this->typi.wszDisplayName)); CopyMemory(pdiW->tszProductName, this->typi.wszDisplayName, cbX(this->typi.wszDisplayName)); #endif
/*
* Since we use HID path, there is no meaning to distinguish * the devices by using "Joystick x" name. * We'd better use the same DisplayName for InstanceName. * Shall we do this? * LoadString(g_hinst, IDS_STDJOYSTICK, tszFormat, cA(tszFormat));
#ifdef UNICODE
wsprintf(pdiW->tszInstanceName, tszFormat, this->idJoy + 1); #else
wsprintf(tszName, tszFormat, this->idJoy + 1); AToU(pdiW->tszInstanceName, cA(pdiW->tszInstanceName), tszName); #endif
*/
#ifdef UNICODE
lstrcpyn(pdiW->tszInstanceName, this->typi.wszDisplayName, cA(pdiW->tszInstanceName)); #else
CAssertF(cA(pdiW->tszInstanceName) >= cA(this->typi.wszDisplayName)); CopyMemory(pdiW->tszInstanceName, this->typi.wszDisplayName, cbX(this->typi.wszDisplayName)); #endif
if (pdiW->dwSize >= cbX(DIDEVICEINSTANCE_DX5W)) { HKEY hkFF;
/*
* If there is a force feedback driver, then fetch the driver CLSID * as the FF GUID. */ hres = CJoy_GetFFConfigKey(pdcb, KEY_QUERY_VALUE, &hkFF); if (SUCCEEDED(hres)) { LONG lRc; TCHAR tszClsid[ctchGuid];
lRc = RegQueryString(hkFF, TEXT("CLSID"), tszClsid, cA(tszClsid)); if (lRc == ERROR_SUCCESS && ParseGUID(&pdiW->guidFFDriver, tszClsid)) { } else { ZeroX(pdiW->guidFFDriver); } RegCloseKey(hkFF); } }
hres = S_OK;
ExitOleProcR(); return hres;
}
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | SetAxisProperty | * * If the request is to set a property on the device, * then convert it into separate requests, one for each * axis. * * @parm PDJ | this | * * The device object. * * @parm GETSETJOYPROP | GetSetJoyProp | * * Callback function that gets or sets the property. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the property being set. * * @parm LPCDIPROPHEADER | pdiph | * * Structure containing property value. * * @parm int | ibField | * * Offset to field being set. (Really: Reference data to * pass to callback.) * *****************************************************************************/
STDMETHODIMP CJoy_SetAxisProperty(PDJ this, LPCDIPROPINFO ppropi, LPCDIPROPHEADER pdiph) { HRESULT hres;
/*
* Note that we never pass the type key to CCal_SetProperty * because we keep our calibration data elsewhere. */
if (ppropi->dwDevType == 0) { /* For device */ int iAxis;
for (iAxis = 0; iAxis < cA(this->rgjrc); iAxis++) {
PJOYRANGECONVERT pjrc = &this->rgjrc[iAxis];
hres = CCal_SetProperty(pjrc, ppropi, pdiph, NULL);
if (FAILED(hres)) { goto done; } } hres = S_OK;
} else if ((ppropi->dwDevType & DIDFT_ABSAXIS) && DIDFT_GETINSTANCE(ppropi->dwDevType) < cA(this->rgjrc)) {
PJOYRANGECONVERT pjrc; pjrc = &this->rgjrc[DIDFT_GETINSTANCE(ppropi->dwDevType)];
hres = CCal_SetProperty(pjrc, ppropi, pdiph, NULL);
} else { hres = E_NOTIMPL; }
done:; return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | UpdateAxisCalibration | * * Take our cached calibration information and smash it into * the configuration section of the registry. * * @parm PDJ | this | * * The device object. * *****************************************************************************/
STDMETHODIMP CJoy_UpdateAxisCalibration(PDJ this) { HRESULT hres; DIJOYCONFIG cfg;
hres = JoyReg_GetConfig(this->idJoy, &cfg, DIJC_REGHWCONFIGTYPE); if (SUCCEEDED(hres)) { UINT uiPosAxis;
#define JoyPosValue(phwc, f, i) \
*(LPDWORD)pvAddPvCb(&(phwc)->hwv.jrvHardware.f, \ ibJoyPosAxisFromPosAxis(i)) \
for (uiPosAxis = 0; uiPosAxis < cJoyPosAxisMax; uiPosAxis++) { PJOYRANGECONVERT pjrc; UINT uiStateAxis;
uiStateAxis = iJoyStateAxisFromPosAxis(uiPosAxis);
pjrc = &this->rgjrc[uiStateAxis];
JoyPosValue(&cfg.hwc, jpMin, uiPosAxis) = pjrc->dwPmin; JoyPosValue(&cfg.hwc, jpMax, uiPosAxis) = pjrc->dwPmax; JoyPosValue(&cfg.hwc, jpCenter, uiPosAxis) = pjrc->dwPc;
#undef JoyPosValue
}
hres = JoyReg_SetConfig(this->idJoy, &cfg.hwc, &cfg, DIJC_UPDATEALIAS | DIJC_REGHWCONFIGTYPE);
}
if (FAILED(hres)) { RPF("Joystick::SetProperty::Calibration: " "Unable to update calibration in registry"); }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | SetProperty | * * Set a device property. * * @parm PDJ | this | * * The device object. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the property being set. * * @parm LPCDIPROPHEADER | pdiph | * * Structure containing property value. * * @returns * * <c E_NOTIMPL> for something we didn't handle natively. * The caller will do * the default thing in response to <c E_NOTIMPL>. * *****************************************************************************/
STDMETHODIMP CJoy_SetProperty(PDICB pdcb, LPCDIPROPINFO ppropi, LPCDIPROPHEADER pdiph) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::SetProperty, (_ "pxxp", pdcb, ppropi->pguid, ppropi->iobj, pdiph));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
switch ((DWORD)(UINT_PTR)ppropi->pguid) {
case (DWORD)(UINT_PTR)DIPROP_RANGE: case (DWORD)(UINT_PTR)DIPROP_DEADZONE: case (DWORD)(UINT_PTR)DIPROP_SATURATION: case (DWORD)(UINT_PTR)DIPROP_CALIBRATIONMODE: case (DWORD)(UINT_PTR)DIPROP_CALIBRATION: hres = CJoy_SetAxisProperty(this, ppropi, pdiph); if (SUCCEEDED(hres) && ppropi->pguid == DIPROP_CALIBRATION) { hres = CJoy_UpdateAxisCalibration(this); } break;
case (DWORD)(UINT_PTR)DIPROP_INSTANCENAME: case (DWORD)(UINT_PTR)DIPROP_PRODUCTNAME: { USHORT uVid, uPid; /*
* Friendly names cause all manner of problems with devices that * use auto detection so only allow non-predefined analog devices * to use them. * * Prefix warns (240487) that ParseVIDPID could leave uVid * uninitialized and succeed but it won't. * See the comment in _ParseHex for more details. */ if( ParseVIDPID( &uVid, &uPid, this->cfg.wszType ) && ( uVid == MSFT_SYSTEM_VID ) && ( uPid >= MSFT_SYSTEM_PID + JOY_HW_PREDEFMAX ) && ( ( uPid & 0xff00 ) == MSFT_SYSTEM_PID ) ) { AssertF(this->hkType); if( this->hkType ) { LPDIPROPSTRING pstr = (PV)pdiph;
hres = JoyReg_SetValue(this->hkType, REGSTR_VAL_JOYOEMNAME, REG_SZ, pstr->wsz, cbX(pstr->wsz)); if( SUCCEEDED(hres ) ) { hres = S_OK; } else { hres = E_FAIL; } } } else { hres = E_NOTIMPL; } break; }
default: SquirtSqflPtszV(sqflJoy, TEXT("CJoy_SetProperty: E_NOTIMPL on guid: %08x"), ppropi->pguid);
hres = E_NOTIMPL; break; }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetAxisProperty | * * Handle an axis property. * * @cwrap PDJ | this * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the property being retrieved. * * @parm OUT LPDIPROPHEADER | pdiph | * * Structure to receive property value. * * @returns * * <c S_OK> if the operation completed successfully. * * <c E_NOTIMPL> nothing happened. The caller will do * the default thing in response to <c E_NOTIMPL>. * *****************************************************************************/
STDMETHODIMP CJoy_GetAxisProperty(PDJ this, LPCDIPROPINFO ppropi, LPDIPROPHEADER pdiph) { LPDIPROPRANGE pdiprg = (PV)pdiph; HRESULT hres;
if ((ppropi->dwDevType & DIDFT_ABSAXIS) && DIDFT_GETINSTANCE(ppropi->dwDevType) < cA(this->rgjrc)) {
PJOYRANGECONVERT pjrc; pjrc = &this->rgjrc[DIDFT_GETINSTANCE(ppropi->dwDevType)];
hres = CCal_GetProperty(pjrc, ppropi->pguid, pdiph);
} else { SquirtSqflPtszV(sqflJoy, TEXT("CJoy_GetProperty: E_NOTIMPL on guid: %08x"), ppropi->pguid);
hres = E_NOTIMPL; }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | GetGuidAndPath | * * Get a Joy device's class GUID (namely, the MEDIA guid) * and device interface (path). The path is for the equivalent * HID device if possible, otherwise a NULL string. * * @parm PCHID | this | * * The Joy object. * * @parm LPDIPROPHEADER | pdiph | * * Structure to receive property value. * *****************************************************************************/
VOID INTERNAL CJoy_GetGuidAndPath(PDJ this, LPDIPROPHEADER pdiph) {
/*
* This should never happen on Win2k because all devices are HID * but just in case we build an NT4 SP5 version or something... */ #ifdef WINNT
LPDIPROPGUIDANDPATH pgp = (PV)pdiph;
UNREFERENCED_PARAMETER( this );
pgp->guidClass = GUID_MediaClass; pgp->wszPath[0] = TEXT( '\0' ); #else
LPDIPROPGUIDANDPATH pgp = (PV)pdiph; VXDINITPARMS vip; TCHAR szPath[MAX_PATH]; PTCHAR pszPath;
pgp->guidClass = GUID_MediaClass;
pszPath = JoyReg_JoyIdToDeviceInterface_95( this->idJoy, &vip, szPath ); if( pszPath ) { TToU( pgp->wszPath, cA(pgp->wszPath), pszPath ); } else { pgp->wszPath[0] = TEXT( '\0' ); } #endif
}
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetProperty | * * Retrieve a device property. * * @parm PDJ | this | * * The device object. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the property being retrieved. * * @parm LPDIPROPHEADER | pdiph | * * Structure to receive property value. * * @returns * * <c S_OK> if the operation completed successfully. * * <c E_NOTIMPL> nothing happened. The caller will do * the default thing in response to <c E_NOTIMPL>. * *****************************************************************************/
STDMETHODIMP CJoy_GetProperty(PDICB pdcb, LPCDIPROPINFO ppropi, LPDIPROPHEADER pdiph) { HRESULT hres = E_NOTIMPL; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::GetProperty, (_ "pxxp", pdcb, ppropi->pguid, ppropi->iobj, pdiph));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
switch ((DWORD)(UINT_PTR)ppropi->pguid) { case (DWORD)(UINT_PTR)DIPROP_GRANULARITY: /*
* ISSUE-2001/03/29-timgill All POVs have the same granularity */ if (ppropi->dwDevType & DIDFT_POV) { LPDIPROPDWORD pdipdw = (PV)pdiph; pdipdw->dwData = this->dwPOVGranularity; hres = S_OK; } break; case (DWORD)(UINT_PTR)DIPROP_GUIDANDPATH: if(ppropi->iobj == 0xFFFFFFFF) { CJoy_GetGuidAndPath(this, pdiph); hres = S_OK; } break;
/*
* In DX7, INSTANCENAME and PRODUCTNAME are the same for VJOYD devices. * It is different before DX7. Probably we need make them different again * after DX7. */ case (DWORD)(UINT_PTR)DIPROP_INSTANCENAME: case (DWORD)(UINT_PTR)DIPROP_PRODUCTNAME: { LPDIPROPSTRING pdipstr = (PV)pdiph; /*
* lstrcpW doesn't work in Win95. We have to use memcpy instead. */ //lstrcpyW(pstr->wsz, this->typi.wszDisplayName);
if( cbX(pdipstr->wsz) > cbX(this->typi.wszDisplayName) ) { memset( &pdipstr->wsz[cA(this->typi.wszDisplayName)], 0, cbX(pdipstr->wsz) - cbX(this->typi.wszDisplayName) ); }
CAssertF( cbX(pdipstr->wsz) >= cbX(this->typi.wszDisplayName) ); memcpy( pdipstr->wsz, this->typi.wszDisplayName, cbX(this->typi.wszDisplayName)); if( this->diHacks.nMaxDeviceNameLength < lstrlenW(pdipstr->wsz) ) { pdipstr->wsz[this->diHacks.nMaxDeviceNameLength] = L'\0'; } hres = S_OK; break; }
case (DWORD)(UINT_PTR)DIPROP_JOYSTICKID: if(ppropi->iobj == 0xFFFFFFFF) { LPDIPROPDWORD pdipdw = (PV)pdiph; pdipdw->dwData = this->idJoy; hres = S_OK; } break;
case (DWORD)(UINT_PTR)(DIPROP_MAPFILE): AssertF( ppropi->iobj == 0xFFFFFFFF );
{ LPDIPROPSTRING pdipstr = (PV)pdiph; LONG lRes; DWORD dwBufferSize = cbX(pdipstr->wsz);
lRes = RegQueryStringValueW( this->hkProp, REGSTR_VAL_JOYOEMMAPFILE, pdipstr->wsz, &dwBufferSize ); hres = ( pdipstr->wsz[0] && ( lRes == ERROR_SUCCESS ) ) ? S_OK : DIERR_OBJECTNOTFOUND; } break;
case (DWORD)(UINT_PTR)(DIPROP_TYPENAME): AssertF( ppropi->iobj == 0xFFFFFFFF );
{ LPDIPROPSTRING pdipstr = (PV)pdiph;
if( this->cfg.hwc.dwType >= JOY_HW_PREDEFMIN && this->cfg.hwc.dwType < JOY_HW_PREDEFMAX ) { pdipstr->wsz[0] = L'#'; pdipstr->wsz[1] = L'0' + (WCHAR)this->cfg.hwc.dwType; pdipstr->wsz[2] = L'\0'; hres = S_OK; } else if( this->cfg.wszType[0] != L'\0' ) { /*
* The type MUST be NULL terminated */ #ifdef WINNT
lstrcpyW( pdipstr->wsz, this->cfg.wszType ); #else
UINT uiLen;
uiLen = lstrlenW( this->cfg.wszType ) + 1; AssertF( uiLen <= cA( pdipstr->wsz ) ); memcpy( pdipstr->wsz, this->cfg.wszType, uiLen * cbX(this->cfg.wszType[0]) ); #endif
hres = S_OK; } else { /*
* Don't think this should ever happen so Assert for now * Assert that the hres is a failure (if not quite right) */ AssertF( hres == E_NOTIMPL ); AssertF( !"No type name available in GetProperty" ); } } break;
/*
* Else, it might be something axis-specific. */ default: hres = CJoy_GetAxisProperty(this, ppropi, pdiph); break; }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | GetCapabilities | * * Get joystick device capabilities. * * @parm PDJ | this | * * The joystick object. * * @parm LPDIDEVCAPS | pdc | * * Device capabilities structure to receive result. * * @returns * <c S_OK> on success. * *****************************************************************************/
STDMETHODIMP CJoy_GetCapabilities(PDICB pdcb, LPDIDEVCAPS pdc) { HRESULT hres; PDJ this; JOYINFOEX jix; MMRESULT mmrc = MMSYSERR_ERROR;
EnterProcI(IDirectInputDeviceCallback::Joy::GetCapabilities, (_ "pp", pdcb, pdc));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
AssertF(IsValidSizeDIDEVCAPS(pdc->dwSize)); CopyMemory(pvAddPvCb(pdc, cbX(DWORD)), pvAddPvCb(&this->dc, cbX(DWORD)), pdc->dwSize - cbX(DWORD));
/*
* Joysticks can come and go. Re-query each time. */
/*
* Determine if joystick is physically attached * or is possibly even phantom. * * JOYERR_ATTACHED - Is attached * JOYERR_UNPLUGGED - Is not attached * Anything else - Is phantom */ jix.dwSize = sizeof(JOYINFOEX); jix.dwFlags = JOY_CAL_READALWAYS | JOY_RETURNALL; mmrc = joyGetPosEx(this->idJoy, &jix);
pdc->dwFlags &= ~DIDC_ATTACHED; if (mmrc == JOYERR_NOERROR) { pdc->dwFlags |= DIDC_ATTACHED; } else if (mmrc == JOYERR_UNPLUGGED) { } else { pdc->dwFlags |= DIDC_PHANTOM; }
hres = S_OK;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | GetPhysicalState | * * Read the physical joystick state into <p pjsOut>. * * After getting the physical data, * we cook the axes as necessary. * * @parm LPDIJOYSTATE2 | pjsOut | * * Where to put the joystick state. * * @returns * * None. * *****************************************************************************/
void INLINE CJoy_GetPhysicalState(PDJ this, LPDIJOYSTATE2 pjsOut) { UINT uiStateAxis;
AssertF(this->pjsPhys);
*pjsOut = *this->pjsPhys;
/*
* Note only absolute positional data gets calibrated */ if( ( this->pvi->fl & VIFL_RELATIVE ) == 0 ) { for (uiStateAxis = 0; uiStateAxis < cA(this->rgjrc); uiStateAxis++) { PLONG pl = pvAddPvCb(pjsOut, ibJoyStateAxisFromStateAxis(uiStateAxis)); CCal_CookRange(&this->rgjrc[uiStateAxis], pl); } } }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | Poll | * * Ping down into the driver to get the latest data. * * @returns * * <c S_OK> if we pinged okay. * <c DIERR_UNPLUGGED> if we did not * *****************************************************************************/
STDMETHODIMP CJoy_Poll(PDICB pdcb) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::Poll, (_ "p", pdcb));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
hres = Hel_Joy_Ping(this->pvi); if (FAILED(hres)) { AssertF(hres == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_DEV_NOT_EXIST)); hres = DIERR_UNPLUGGED; } /*
* Note, we don't keep this->pvi->fl:VIFL_UNPLUGGED up-to-date because * we don't use the flag and always retest the connectivity in GetCaps. */
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetDeviceState | * * Obtains the state of the joystick device. * * It is the caller's responsibility to have validated all the * parameters and ensure that the device has been acquired. * * @parm OUT LPVOID | lpvData | * * joystick data in the preferred data format. * * @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 lpmdr> parameter is not a valid pointer. * *****************************************************************************/
STDMETHODIMP CJoy_GetDeviceState(PDICB pdcb, LPVOID pvData) { HRESULT hres; PDJ this; LPDIJOYSTATE2 pjsOut = pvData; EnterProcI(IDirectInputDeviceCallback::Joy::GetDeviceState, (_ "pp", pdcb, pvData));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
AssertF(this->pvi); AssertF(this->pjsPhys);
if (this->pvi->fl & VIFL_ACQUIRED) { CJoy_GetPhysicalState(this, pjsOut); hres = S_OK; } else { hres = DIERR_INPUTLOST; }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | CookDeviceData | * * Manipulate buffered device data. * * If the item describe an axis, we need to cook it. * * @parm DWORD | cdod | * * Number of objects to cook; zero is a valid value. * * @parm LPDIDEVICEOBJECTDATA | pdod | * * Array of object data to cook. * * @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_UNSUPPORTED> = <c E_NOTIMPL>: The callback does * not cook device data. * * <c DIERR_NOTACQUIRED>: The device could not be acquired. * ***************************************************************************/
STDMETHODIMP CJoy_CookDeviceData ( PDICB pdcb, DWORD cdod, LPDIDEVICEOBJECTDATA pdod ) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::CookDeviceData, (_ "pxp", pdcb, cdod, pdod));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
/*
* Step through array of either element size cooking the data. */ for( ; cdod; cdod-- ) { DWORD dwType = this->df.rgodf[pdod->dwOfs].dwType; if( dwType & DIDFT_ABSAXIS ) { PJOYRANGECONVERT pjrc;
AssertF( DIDFT_GETINSTANCE( dwType ) < cA( this->rgjrc ) ); pjrc = &this->rgjrc[DIDFT_GETINSTANCE( dwType )];
CCal_CookRange(pjrc, (PV)&pdod->dwData); } pdod++; }
hres = S_OK; return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | OpenIdSubkey | * * Given an object ID, attempt to open the subkey that * corresponds to it. * * @cwrap PDJ | this * * @parm DWORD | dwId | * * Object id. * * @parm PHKEY | phk | * * Receives the key on success. * * @returns * * Returns a COM error code. * *****************************************************************************/
HRESULT INLINE CJoy_OpenIdSubkey(PDJ this, DWORD dwId, PHKEY phk) { return CType_OpenIdSubkey(this->hkType, dwId, KEY_QUERY_VALUE, phk); }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetObjectInfo | * * Obtain the friendly name and FF/HID information * of an object. * * @parm IN LPCDIPROPINFO | ppropi | * * Information describing the object being accessed. * * @parm IN OUT LPDIDEVICEOBJECTINSTANCEW | pdidioiW | * * Structure to receive information. The * <e DIDEVICEOBJECTINSTANCE.guidType>, * <e DIDEVICEOBJECTINSTANCE.dwOfs>, * and * <e DIDEVICEOBJECTINSTANCE.dwType> * <e DIDEVICEOBJECTINSTANCE.dwFlags> * fields have already been filled in. * * @returns * * Returns a COM error code. * *****************************************************************************/
STDMETHODIMP CJoy_GetObjectInfo(PDICB pdcb, LPCDIPROPINFO ppropi, LPDIDEVICEOBJECTINSTANCEW pdidoiW) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::GetObjectInfo, (_ "pxp", pdcb, ppropi->iobj, pdidoiW));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
AssertF(IsValidSizeDIDEVICEOBJECTINSTANCEW(pdidoiW->dwSize)); if (ppropi->iobj < this->df.dwNumObjs) {
AssertF(ppropi->dwDevType == this->df.rgodf[ppropi->iobj].dwType);
CType_RegGetObjectInfo(this->hkType, ppropi->dwDevType, pdidoiW);
/*
* If we couldn't get a name from the registry, * then grab one of the standard names. */ if (pdidoiW->tszName[0] == L'\0') { UINT dids;
if (ppropi->dwDevType & DIDFT_AXIS) { dids = 0; LoadStringW(g_hinst, IDS_JOYSTICKOBJECT + dids + DIDFT_GETINSTANCE(ppropi->dwDevType), pdidoiW->tszName, cA(pdidoiW->tszName)); } else if (ppropi->dwDevType & DIDFT_BUTTON) { GetNthButtonString(pdidoiW->tszName, DIDFT_GETINSTANCE(ppropi->dwDevType)); } else { AssertF(ppropi->dwDevType & DIDFT_POV); GetNthPOVString(pdidoiW->tszName, DIDFT_GETINSTANCE(ppropi->dwDevType)); }
}
/*
* ISSUE-2001/03/29-timgill Need a faster way of checking VJoyD devices * On Win9x, many HID devices cannot use our ring 3 HID path so try * to get any axis or POV usage from VJoyD * This is not cheap to get but until we have a better way to * make sure the VJoyD device has not changed under us this is better * than caching it. This is not inner loop code anyway. */ #ifndef WINNT
if( pdidoiW->dwSize >= cbX(DIDEVICEOBJECTINSTANCE_DX5W) ) { VXDINITPARMS vip; hres = Hel_Joy_GetInitParms(this->idJoy, &vip);
if( SUCCEEDED( hres ) && ( vip.dwFlags & VIP_ISHID ) ) { if( ppropi->dwDevType & DIDFT_AXIS ) { int AxisIdx;
AxisIdx = c_rgiJoyPosAxisFromStateAxis[DIDFT_GETINSTANCE(ppropi->dwDevType)]; pdidoiW->wUsagePage = HIWORD( vip.Usages[AxisIdx] ); pdidoiW->wUsage = LOWORD( vip.Usages[AxisIdx] ); } else if( ppropi->dwDevType & DIDFT_BUTTON ) { /*
* Only JoyHID uses this interface and it only counts * button page buttons so assume the simplest case. */ pdidoiW->wUsagePage = HID_USAGE_PAGE_BUTTON; pdidoiW->wUsage = 1 + DIDFT_GETINSTANCE( ppropi->dwDevType ); } else { AssertF(ppropi->dwDevType & DIDFT_POV); pdidoiW->wUsagePage = HIWORD( ((PDWORD)(&vip.dwPOV0usage))[DIDFT_GETINSTANCE(ppropi->dwDevType)] ); pdidoiW->wUsage = LOWORD( ((PDWORD)(&vip.dwPOV0usage))[DIDFT_GETINSTANCE(ppropi->dwDevType)] ); } } } /*
* Ignore any errors getting params. */ #endif
hres = S_OK; } else { hres = E_INVALIDARG; }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | SetCooperativeLevel | * * The app changed the cooperative level. * Un-subclass the old window and en-subclass the new window. * * @parm IN HWND | hwnd | * * The window handle. * * @parm IN DWORD | dwFlags | * * The cooperativity level. * *****************************************************************************/
STDMETHODIMP CJoy_SetCooperativeLevel(PDICB pdcb, HWND hwnd, DWORD dwFlags) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::SetCooperativityLevel, (_ "pxx", pdcb, hwnd, dwFlags));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
/*
* First get out of the old window. */ CJoy_RemoveSubclass(this); /*
* Prefix warns that "this" may have been freed (mb:34574) however * If you're in SetCooperativeLevel and you have a window subclassed * then there must be a hold for the subclassed window as well as * one for the unreleased interface so the Common_Unhold won't free * the pointer. */
/*
* If a new window is passed, then subclass it so we can * watch for joystick configuration change messages. * * If we can't, don't worry. All it means that we won't * be able to catch when the user recalibrates the joystick, * which isn't very often. */ if (hwnd) { if (SetWindowSubclass(hwnd, CJoy_SubclassProc, 0, (ULONG_PTR)this)) { this->hwnd = hwnd; Common_Hold(this); }
} else { RPF("SetCooperativeLevel: You really shouldn't pass hwnd = 0; " "joystick calibration may be dodgy"); }
hres = S_OK;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | RunControlPanel | * * Run the joystick control panel. * * @parm IN HWND | hwndOwner | * * The owner window. * * @parm DWORD | dwFlags | * * Flags. * *****************************************************************************/
TCHAR c_tszJoyCpl[] = TEXT("joy.cpl");
STDMETHODIMP CJoy_RunControlPanel(PDICB pdcb, HWND hwnd, DWORD dwFlags) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::RunControlPanel, (_ "pxx", pdcb, hwnd, dwFlags));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
hres = hresRunControlPanel(c_tszJoyCpl);
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetFFConfigKey | * * Open and return the registry key that contains * force feedback configuration information. * * @parm DWORD | sam | * * Security access mask. * * @parm PHKEY | phk | * * Receives the registry key. * *****************************************************************************/
STDMETHODIMP CJoy_GetFFConfigKey(PDICB pdcb, DWORD sam, PHKEY phk) { HRESULT hres; PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::GetFFConfigKey, (_ "px", pdcb, sam));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
hres = JoyReg_OpenFFKey(this->hkType, sam, phk);
AssertF(fLeqvFF(SUCCEEDED(hres), *phk));
ExitBenignOleProcPpvR(phk); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | CreateEffect | * * Create an <i IDirectInputEffectDriver> interface. * * @parm LPDIRECTINPUTEFFECTSHEPHERD * | ppes | * * Receives the shepherd for the effect driver. * *****************************************************************************/
STDMETHODIMP CJoy_CreateEffect(PDICB pdcb, LPDIRECTINPUTEFFECTSHEPHERD *ppes) { HRESULT hres; PDJ this; HKEY hk; EnterProcI(IDirectInputDeviceCallback::Joy::CreateEffect, (_ "p", pdcb));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
hres = CJoy_GetFFConfigKey(pdcb, KEY_QUERY_VALUE, &hk); if (SUCCEEDED(hres)) { hres = CEShep_New(hk, 0, &IID_IDirectInputEffectShepherd, ppes); if (SUCCEEDED(hres)) { #ifndef WINNT
VXDINITPARMS vip; CHAR szPath[MAX_PATH]; PCHAR pszPath;
pszPath = JoyReg_JoyIdToDeviceInterface_95( this->idJoy, &vip, szPath ); if( pszPath ) { DIHIDFFINITINFO init; WCHAR wszPath[MAX_PATH];
ZeroX(init); init.dwSize = cbX(init);
TToU( wszPath, cA(wszPath), pszPath ); init.pwszDeviceInterface = wszPath; hresFindHIDDeviceInterface(pszPath, &init.GuidInstance);
hres = (*ppes)->lpVtbl->DeviceID((*ppes), this->idJoy, TRUE, &init); } else #endif
{ hres = (*ppes)->lpVtbl->DeviceID((*ppes), this->idJoy, TRUE, 0); } } if (SUCCEEDED(hres)) { } else { Invoke_Release(ppes); } RegCloseKey(hk); } else { hres = E_NOTIMPL; *ppes = 0; }
ExitOleProcPpvR(ppes); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | GetVersions | * * Ping down into the driver to get the driver version info. * * @parm LPDIDRIVERVERSIONS | pvers | * * A structure which should be filled in with version information * describing the hardware, firmware, and driver. * * DirectInput will set the <e DIDRIVERVERSIONS.dwSize> field * to sizeof(DIDRIVERVERSIONS) before calling this method. * * @returns * * <c S_OK> if we succeeded. * *****************************************************************************/
STDMETHODIMP CJoy_GetVersions(PDICB pdcb, LPDIDRIVERVERSIONS pvers) { HRESULT hres; PDJ this; VXDINITPARMS vip; EnterProcI(IDirectInputDeviceCallback::Joy::GetVersions, (_ "p", pdcb));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
AssertF(pvers->dwSize == cbX(*pvers));
hres = Hel_Joy_GetInitParms(this->idJoy, &vip);
if (SUCCEEDED(hres)) { pvers->dwFirmwareRevision = vip.dwFirmwareRevision; pvers->dwHardwareRevision = vip.dwHardwareRevision; pvers->dwFFDriverVersion = vip.dwFFDriverVersion; }
return hres; }
/*****************************************************************************
* * Joystick registry usage * ***************************************************************************** * * Global joystick information is kept under * * HKEY_LOCAL_MACHINE\ * System\ * CurrentControlSet\ * Control\ * MediaProperties\ * Joystick\ * OEM * * Under this key is a number of subkeys, each corresponding to a brand * of joystick. * * Under each OEM\<name> key, you can find the following values: * * OEMData * * This is a binary value containing a structure of two dwords. * The first is the JOYREGHWCONFIG.hws.dwFlags and the second * is the JOYREGHWCONFIG.hws.dwNumButtons. * * OEMName * * This is a string which gives a friendly name for the unit. * * ***************************************************************************** * * Under the driver key is kept information about the particular joystick. * * HKEY_LOCAL_MACHINE\ * System\ * CurrentControlSet\ * Control\ * MediaResources\ * Joystick\ * <driver key name>\ * CurrentJoystickSettings * * Under this key, there are a bunch of values named * Joystick##Configuration, where ## is the joystick number * (1 through 16). Each value contains binary data in the form * of a JOYREGHWCONFIG, which looks like this: * * DWORD hws.dwFlags; // JOY_HWS_* \ * DWORD hws.dwNumButtons; _\ JOYREGHWSETTINGS * DWORD dwUsageSettings;// JOY_US_* _____
* DWORD hwv.jrvHardware.jpMin.dwX; \ | * DWORD hwv.jrvHardware.jpMin.dwY; \ | * DWORD hwv.jrvHardware.jpMin.dwZ; \ * DWORD hwv.jrvHardware.jpMin.dwR; | * DWORD hwv.jrvHardware.jpMin.dwU; > JOYREGHWVALUES.JOYRANGE * DWORD hwv.jrvHardware.jpMin.dwV; | * DWORD hwv.jrvHardware.jpMax.dwX; | | * DWORD hwv.jrvHardware.jpMax.dwY; | | * DWORD hwv.jrvHardware.jpMax.dwZ; | \ * DWORD hwv.jrvHardware.jpMax.dwR; | > JOYREGHWVALUES * DWORD hwv.jrvHardware.jpMax.dwU; | / * DWORD hwv.jrvHardware.jpMax.dwV; | | * DWORD hwv.jrvHardware.jpCenter.dwX; | | * DWORD hwv.jrvHardware.jpCenter.dwY; | | * DWORD hwv.jrvHardware.jpCenter.dwZ; | | * DWORD hwv.jrvHardware.jpCenter.dwR; / | * DWORD hwv.jrvHardware.jpCenter.dwU; / | * DWORD hwv.jrvHardware.jpCenter.dwV; / | * DWORD hwv.dwPOVValues[4]; | * DWORD hwv.dwCalFlags; ______| * DWORD dwType; // JOY_HW_*
* DWORD dwReserved; * * * Also under this key are optional values named Joystick##OEMName. * If present, it is a string-data key whose contents are the name * of another key that describes the joystick, stored in the global * section described above. * * Meanwhile, under the key * * HKEY_LOCAL_MACHINE\ * System\ * CurrentControlSet\ * Control\ * MediaResources\ * Joystick\ * <driver key name> * * is a value called "JoystickUserValues". This is a binary key * that contains a JOYREGUSERVALUES structure: * * DWORD dwTimeOut; * DWORD jrvRanges.jpMin.dwX; \ * DWORD jrvRanges.jpMin.dwY; \ * DWORD jrvRanges.jpMin.dwZ; \ * DWORD jrvRanges.jpMin.dwR; | * DWORD jrvRanges.jpMin.dwU; > JOYRANGE * DWORD jrvRanges.jpMin.dwV; | * DWORD jrvRanges.jpMax.dwX; | * DWORD jrvRanges.jpMax.dwY; | * DWORD jrvRanges.jpMax.dwZ; | * DWORD jrvRanges.jpMax.dwR; | * DWORD jrvRanges.jpMax.dwU; | * DWORD jrvRanges.jpMax.dwV; | * DWORD jrvRanges.jpCenter.dwX; | (ignored) * DWORD jrvRanges.jpCenter.dwY; | (ignored) * DWORD jrvRanges.jpCenter.dwZ; | (ignored) * DWORD jrvRanges.jpCenter.dwR; / (ignored) * DWORD jrvRanges.jpCenter.dwU; / (ignored) * DWORD jrvRanges.jpCenter.dwV; / (ignored) * DWORD jpDeadZone.dwX; \ * DWORD jpDeadZone.dwY; \ * DWORD jpDeadZone.dwZ; \ JOYPOS * DWORD jpDeadZone.dwR; / Dead zone is recorded as a * DWORD jpDeadZone.dwU; / percentage of total range * DWORD jpDeadZone.dwV; / * * If there is no JoystickUserValues, then the following defaults * are used: * * jpMin.dw# = 0; * jpMax.dw# = 65535; * jpCenter.dw# = jpMax.dw# / 2; * jrvDeadZone.dw# = 5; * * (See ibmjoy\msjstick.c, function jsReadRegistry for the code that * sets the defaults.) * * We will also use the defaults if Min > Max or if Max >= 0x80000000 * or if DeadZone > 100. * *****************************************************************************/
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | InitPhysRanges | * * Initialize (or re-initialize) * the physical min/max/center values. This is * done as part of device initialization as well as in response * to a notification that the Joystick control panel has been * dinked with. * * It is assumed that the <e DJ.hwc> already contains the * registry hardware settings. * * After the phys ranges are set, the ramps are recalculated. * * *****************************************************************************/
void INTERNAL CJoy_InitPhysRanges(PDJ this, LPJOYREGHWCONFIG phwc) { UINT uiPosAxis; UINT uiStateAxis;
#define GetJoyPosValue(phwc, f, i) \
*(LPDWORD)pvAddPvCb(&phwc->hwv.jrvHardware.f, \ ibJoyPosAxisFromPosAxis(i)) \
for (uiPosAxis = 0; uiPosAxis < cJoyPosAxisMax; uiPosAxis++) { DWORD dwMax, dwC; PJOYRANGECONVERT pjrc;
uiStateAxis = iJoyStateAxisFromPosAxis(uiPosAxis);
pjrc = &this->rgjrc[uiStateAxis];
pjrc->dwPmin = GetJoyPosValue(phwc, jpMin, uiPosAxis);
/*
* HACKHACK - Uncalibrated joysticks will have max == 0, in which * case we use a fake max of 655, just like VJOYD. */ dwMax = GetJoyPosValue(phwc, jpMax, uiPosAxis); if (dwMax == 0) { dwMax = 655; }
pjrc->dwPmax = dwMax;
/*
* HACKHACK - Uncalibrated joysticks will have center == 0, * in which case we use a fake center of midway between min and * max, just like VJOYD. * * Quirk - Z, R, U, and V typically are not center-calibrated, * so if the jpCenter value is not strictly between min and * max, then assume it's one of the bogus cases and slam it * into the middle of the range. */
dwC = GetJoyPosValue(phwc, jpCenter, uiPosAxis); if (dwC <= pjrc->dwPmin || dwC >= pjrc->dwPmax) { dwC = (pjrc->dwPmin + pjrc->dwPmax) / 2; }
pjrc->dwPc = dwC;
if( pjrc->dwCPointsNum == 0 ) { //use two control points by default
pjrc->dwCPointsNum = 2; pjrc->cp[0].lP = pjrc->dwPmin; pjrc->cp[0].dwLog = 0; pjrc->cp[1].lP = pjrc->dwPmax; pjrc->cp[1].dwLog = RANGEDIVISIONS; } else { pjrc->cp[0].lP = pjrc->dwPmin; pjrc->cp[pjrc->dwCPointsNum-1].lP = pjrc->dwPmax; } SquirtSqflPtszV(sqfl, TEXT("CJoy_PhysRange %d -> %d: %08x / %08x / %08x"), uiPosAxis, uiStateAxis, pjrc->dwPmin, pjrc->dwPc, pjrc->dwPmax);
}
#undef GetJoyValue
/*
* These two phantom axes are always raw because they don't exist. */ this->rgjrc[iJoyStateAxisRx].fRaw = TRUE; this->rgjrc[iJoyStateAxisRy].fRaw = TRUE;
/*
* Now compute all the dependent variables. */ for (uiStateAxis = 0; uiStateAxis < cA(this->rgjrc); uiStateAxis++) { CCal_RecalcRange(&this->rgjrc[uiStateAxis]); } }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | InitLogRanges | * * Initialize the logical ranges from the user values. * *****************************************************************************/
void INLINE CJoy_InitLogRanges(PDJ this) { HRESULT hres; UINT uiPosAxis; UINT uiStateAxis; DIJOYUSERVALUES juv;
hres = JoyReg_GetUserValues(&juv, DIJU_USERVALUES); AssertF(SUCCEEDED(hres));
#define pJoyValue(jp, i) \
(LPDWORD)pvAddPvCb(&(jp), ibJoyPosAxisFromPosAxis(i)) \
for (uiPosAxis = 0; uiPosAxis < cJoyPosAxisMax; uiPosAxis++) {
PJOYRANGECONVERT pjrc;
AssertF((int)*pJoyValue(juv.ruv.jrvRanges.jpMax, uiPosAxis) >= 0); AssertF(*pJoyValue(juv.ruv.jrvRanges.jpMin, uiPosAxis) < *pJoyValue(juv.ruv.jrvRanges.jpMax, uiPosAxis));
uiStateAxis = iJoyStateAxisFromPosAxis(uiPosAxis);
pjrc = &this->rgjrc[uiStateAxis];
pjrc->lMin = *pJoyValue(juv.ruv.jrvRanges.jpMin, uiPosAxis); pjrc->lMax = *pJoyValue(juv.ruv.jrvRanges.jpMax, uiPosAxis);
/*
* Note that we do *not* use the jpCenter value. Strange * but true. * * The sum cannot overflow due to the sanity checks we did above. */
pjrc->lC = CCal_Midpoint(pjrc->lMin, pjrc->lMax);
/*
* Now do the dead zone. Convert from percent to range units. */ pjrc->dwDz = *pJoyValue(juv.ruv.jpDeadZone, uiPosAxis) * (RANGEDIVISIONS / 100);
if (pjrc->dwDz > RANGEDIVISIONS) { pjrc->dwDz = 5 * (RANGEDIVISIONS / 100); }
/*
* Now do the saturation level. It always defaults to 100%. */ pjrc->dwSat = RANGEDIVISIONS;
}
}
#undef pJoyValue
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | BuildAxes | * * Study a single capabilities flag and add axis items to the data * format accordingly. * * @parm DWORD | dwCaps | * * Collection of <c JOYPF_*> flags describing the axes supported. * * @parm UINT | ib | * * Data format offset at which this data is provided. * * @parm UINT | uiObj | * * Instance index for the first item. * * @parm DWORD | dwAspect | * * <c DIDOI_ASPECT*> for these axes. * *****************************************************************************/
typedef struct AXISATTRIBUTES { DWORD dwMask; /* Mask that identifies the axis */ UINT uidObj; /* Object index delta from X axis */ } AXISATTRIBUTES, *PAXISATTRIBUTES; typedef const AXISATTRIBUTES *PCAXISATTRIBUTES;
typedef struct AXISMAPPINGS { PCGUID pguid; /* GUID for the object */ DWORD dwSemantic; /* Default semantic map */ } AXISMAPPINGS, *PAXISMAPPINGS; typedef const AXISMAPPINGS *PCAXISMAPPINGS;
const AXISATTRIBUTES c_rgaattrJoy[] = { { JOYPF_X, iJoyStateAxisX, }, { JOYPF_Y, iJoyStateAxisY, }, { JOYPF_Z, iJoyStateAxisZ, }, { JOYPF_R, iJoyStateAxisRz, }, { JOYPF_U, iJoyStateAxisS0, }, { JOYPF_V, iJoyStateAxisS1, }, };
const AXISMAPPINGS c_rgamapJoy[6] = { { &GUID_XAxis, DISEM_FLAGS_X | DISEM_TYPE_AXIS }, { &GUID_YAxis, DISEM_FLAGS_Y | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_RzAxis, DISEM_FLAGS_R | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, };
const AXISMAPPINGS c_rgamap6DOF[6] = { { &GUID_XAxis, DISEM_FLAGS_X | DISEM_TYPE_AXIS }, { &GUID_YAxis, DISEM_FLAGS_Y | DISEM_TYPE_AXIS }, { &GUID_ZAxis, DISEM_FLAGS_Z | DISEM_TYPE_AXIS }, { &GUID_RzAxis, DISEM_FLAGS_R | DISEM_TYPE_AXIS }, { &GUID_RyAxis, DISEM_FLAGS_U | DISEM_TYPE_AXIS }, { &GUID_RxAxis, DISEM_FLAGS_V | DISEM_TYPE_AXIS }, };
const AXISMAPPINGS c_rgamapZJoy[6] = { { &GUID_XAxis, DISEM_FLAGS_X | DISEM_TYPE_AXIS }, { &GUID_YAxis, DISEM_FLAGS_Y | DISEM_TYPE_AXIS }, { &GUID_ZAxis, DISEM_FLAGS_Z | DISEM_TYPE_AXIS }, { &GUID_RzAxis, DISEM_FLAGS_R | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, };
/*
* Since default HID mapping maps accel to Y and brake to Rz * use the GUIDs to match the correct axis to those mappings * and set appropriate semantics. */
const AXISMAPPINGS c_rgamapCarZY[6] = { { &GUID_XAxis, DISEM_FLAGS_X | DISEM_TYPE_AXIS }, { &GUID_RzAxis, DISEM_FLAGS_B | DISEM_TYPE_AXIS }, { &GUID_YAxis, DISEM_FLAGS_A | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, };
const AXISMAPPINGS c_rgamapCarYR[6] = { { &GUID_XAxis, DISEM_FLAGS_X | DISEM_TYPE_AXIS }, { &GUID_YAxis, DISEM_FLAGS_A | DISEM_TYPE_AXIS }, { &GUID_ZAxis, DISEM_FLAGS_Z | DISEM_TYPE_AXIS }, { &GUID_RzAxis, DISEM_FLAGS_B | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, };
const AXISMAPPINGS c_rgamapCarZR[6] = { { &GUID_XAxis, DISEM_FLAGS_X | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_YAxis, DISEM_FLAGS_A | DISEM_TYPE_AXIS }, { &GUID_RzAxis, DISEM_FLAGS_B | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, { &GUID_Slider, DISEM_FLAGS_S | DISEM_TYPE_AXIS }, };
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | InitFromHwc | * * Initialize the information that is kept in the * <t JOYREGHWCONFIG>. * * Broken out from CJoy_InitRing3 to make things less monolithic. * * The <e CJoy.cfg> structure already contains joystick * configuration information. * *****************************************************************************/
HRESULT INLINE CJoy_InitFromHwc(PDJ this) { HRESULT hres; DWORD dwTestType;
if( this->cfg.hwc.dwType >= JOY_HW_PREDEFMIN && this->cfg.hwc.dwType < JOY_HW_PREDEFMAX ) { WCHAR wszType[4]; wszType[0] = L'#'; wszType[1] = L'0' + (WCHAR)this->cfg.hwc.dwType; wszType[2] = L'\0'; hres = JoyReg_GetPredefTypeInfo( wszType, &this->typi, DITC_DISPLAYNAME); } else if (this->cfg.wszType[0] != L'\0' ) { hres = JoyReg_GetTypeInfo(this->cfg.wszType, &this->typi, DITC_DISPLAYNAME | DITC_FLAGS2 );
if (SUCCEEDED(hres)) { } else { #ifdef WINNT
ZeroMemory(&this->typi, cbX(this->typi)); #else
//lstrcpyW( this->typi.wszDisplayName, this->cfg.wszType );
memset( &this->typi.wszDisplayName[0], 0, sizeof(this->typi.wszDisplayName) ); memcpy( &this->typi.wszDisplayName[0], &this->cfg.wszType[0], sizeof(this->cfg.wszType)); #endif
} } else { ZeroMemory(&this->typi, cbX(this->typi)); }
#define hwc this->cfg.hwc
if( ( this->typi.dwFlags2 & ( JOYTYPE_HIDEACTIVE | JOYTYPE_GAMEHIDE ) ) == ( JOYTYPE_HIDEACTIVE | JOYTYPE_GAMEHIDE ) ) { this->dc.dwFlags |= DIDC_HIDDEN; }
dwTestType = GetValidDI8DevType( this->typi.dwFlags2, this->dc.dwButtons, hwc.hws.dwFlags );
if( dwTestType ) { /*
* If a valid override exists just use it */ this->dc.dwDevType = dwTestType; } else { #ifdef XDEBUG
if( GET_DIDEVICE_TYPEANDSUBTYPE( this->typi.dwFlags2 ) ) { RPF( "Ignoring invalid type/subtype Flags2 value 0x%08x for joystick", this->typi.dwFlags2 ); } #endif
if (hwc.hws.dwFlags & JOY_HWS_ISYOKE) { dwTestType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_FLIGHT, DI8DEVTYPEFLIGHT_STICK); } else if (hwc.hws.dwFlags & JOY_HWS_ISGAMEPAD) { dwTestType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_GAMEPAD, DI8DEVTYPEGAMEPAD_STANDARD); } else if (hwc.hws.dwFlags & JOY_HWS_ISCARCTRL) { AssertF( this->dc.dwAxes > 1 ); if ( this->dc.dwAxes <= 2 ) { dwTestType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_DRIVING, DI8DEVTYPEDRIVING_COMBINEDPEDALS ); } else { dwTestType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_DRIVING, DI8DEVTYPEDRIVING_DUALPEDALS ); } } else if (hwc.hws.dwFlags & JOY_HWS_ISHEADTRACKER) { dwTestType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_SUPPLEMENTAL, DI8DEVTYPESUPPLEMENTAL_HEADTRACKER); } else { dwTestType = MAKE_DIDEVICE_TYPE(DI8DEVTYPE_JOYSTICK, DI8DEVTYPEJOYSTICK_STANDARD); }
/*
* Use the common function to make this a limited type if the * number of buttons or flags dictate it. * Since the type and subtype are known to be valid, the return * value should never be a failure (zero). */ this->dc.dwDevType = GetValidDI8DevType( dwTestType, this->dc.dwButtons, hwc.hws.dwFlags ); AssertF( this->dc.dwDevType ); }
#undef hwc
/*
* Now that we know the type, then make up a name ourselves if we * previously failed to do so. */ if (this->typi.wszDisplayName[0] == TEXT('\0')) { CType_MakeGameCtrlName( this->typi.wszDisplayName, this->dc.dwDevType, this->dc.dwAxes, this->dc.dwButtons, this->dc.dwPOVs ); }
hres = S_OK;
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | AddObject | * * Add one object to the device format. * * If the device is polled, then the object is polled, too. * * @cwrap PDJ | this * * @parm PCGUID | pguid | * * The <t GUID> that classifies the device. * * @parm DWORD | dwOfs | * * Data offset. * * @parm DWORD | dwDevType | * * Device type flags to apply to the object. * * @parm UINT | uiObj | * * Object instance number. * * @parm DWORD | dwAspect | * * Optional <c DIDOI_ASPECT*> flag. * *****************************************************************************/
void INTERNAL CJoy_AddObject(PDJ this, PCGUID pguid, DWORD dwOfs, DWORD dwDevType, UINT uiObj, DWORD dwAspect, BOOL fReal) { LPDIOBJECTDATAFORMAT podf;
podf = &this->df.rgodf[this->df.dwNumObjs++]; podf->pguid = pguid; podf->dwOfs = dwOfs; podf->dwType = dwDevType | DIDFT_MAKEINSTANCE(uiObj); podf->dwFlags = dwAspect;
if (this->dc.dwFlags & DIDC_POLLEDDEVICE) { podf->dwFlags |= DIDOI_POLLED; }
if( fReal ) { CType_RegGetTypeInfo(this->hkType, podf, FALSE); if( ( GET_DIDEVICE_TYPE( this->dc.dwDevType ) == DI8DEVTYPE_DRIVING ) && ( podf->dwFlags & DIDOI_FFACTUATOR ) && ( podf->pguid != &GUID_XAxis ) ) { /*
* IHVs set FF attributes on non-FF axes for wheels because * first generation FF apps were only written to support joysticks. * Since we now munge the various configurations of pedal axes to * report all split pedals in the same way, the fake Y axis can * land up on different axes, usually Slider0. Rather than have * people code to these different fake axes, strip out actuator * status from any driving axis except the wheel. */ podf->dwFlags &= ~DIDOI_FFACTUATOR; podf->dwType &= ~DIDFT_FFACTUATOR; } } }
void INTERNAL CJoy_BuildAxes(PDJ this, DWORD dwCaps, UINT ib, UINT uiObj, DWORD dwAspect, PCAXISMAPPINGS pamap, BOOL fReal ) { int iaattr;
for (iaattr = 0; iaattr < cA(c_rgaattrJoy); iaattr++) { PCAXISATTRIBUTES paattr = &c_rgaattrJoy[iaattr]; if (dwCaps & paattr->dwMask) { CJoy_AddObject(this, pamap[iaattr].pguid, (cbX(LONG)*paattr->uidObj) + ib, DIDFT_ABSAXIS, paattr->uidObj + uiObj, dwAspect, fReal); } }
}
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoy | BuildDataFormat | * * Study the device capabilities and build the device * data format. * *****************************************************************************/
void INTERNAL CJoy_BuildDataFormat(PDJ this, PVXDAXISCAPS pvac, DWORD dwButtons, PCAXISMAPPINGS pamap, BOOL fReal) { DWORD dw;
this->dc.dwAxes = 0; this->dc.dwButtons = 0; this->dc.dwPOVs = 0;
this->df.dwSize = cbX(DIDATAFORMAT); this->df.dwObjSize = cbX(DIOBJECTDATAFORMAT); this->df.dwDataSize = sizeof(DIJOYSTATE2); AssertF(this->df.dwFlags == 0); this->df.dwNumObjs = 0;
/*
* Repeat for each set of axes. */
#define CheckAxisOrder(fStart, p, f) \
CAssertF(FIELD_OFFSET(DIJOYSTATE2, p##f) == \ FIELD_OFFSET(DIJOYSTATE2, fStart) + ibJoyStateAxis##f) \
CheckAxisOrder(lX, l, X); CheckAxisOrder(lX, l, Y); CheckAxisOrder(lX, l, Z); CheckAxisOrder(lX, l, Rx); CheckAxisOrder(lX, l, Ry); CheckAxisOrder(lX, l, Rz); CheckAxisOrder(lX, rgl, Slider);
if (pvac->dwPos & JOYPF_POSITION) { CJoy_BuildAxes(this, pvac->dwPos, FIELD_OFFSET(DIJOYSTATE2, lX), iobjPositions, DIDOI_ASPECTPOSITION, pamap, fReal); }
CheckAxisOrder(lVX, lV, X); CheckAxisOrder(lVX, lV, Y); CheckAxisOrder(lVX, lV, Z); CheckAxisOrder(lVX, lV, Rx); CheckAxisOrder(lVX, lV, Ry); CheckAxisOrder(lVX, lV, Rz); CheckAxisOrder(lVX, rglV, Slider);
if (pvac->dwPos & JOYPF_VELOCITY) { CJoy_BuildAxes(this, pvac->dwVel, FIELD_OFFSET(DIJOYSTATE2, lVX), iobjVelocities, DIDOI_ASPECTVELOCITY, pamap, fReal); }
CheckAxisOrder(lAX, lA, X); CheckAxisOrder(lAX, lA, Y); CheckAxisOrder(lAX, lA, Z); CheckAxisOrder(lAX, lA, Rx); CheckAxisOrder(lAX, lA, Ry); CheckAxisOrder(lAX, lA, Rz); CheckAxisOrder(lAX, rglA, Slider);
if (pvac->dwPos & JOYPF_ACCELERATION) { CJoy_BuildAxes(this, pvac->dwAccel, FIELD_OFFSET(DIJOYSTATE2, lAX), iobjAccels, DIDOI_ASPECTACCEL, pamap, fReal); }
CheckAxisOrder(lFX, lF, X); CheckAxisOrder(lFX, lF, Y); CheckAxisOrder(lFX, lF, Z); CheckAxisOrder(lFX, lF, Rx); CheckAxisOrder(lFX, lF, Ry); CheckAxisOrder(lFX, lF, Rz); CheckAxisOrder(lFX, rglF, Slider);
if (pvac->dwPos & JOYPF_FORCE) { CJoy_BuildAxes(this, pvac->dwForce, FIELD_OFFSET(DIJOYSTATE2, lFX), iobjForces, DIDOI_ASPECTFORCE, pamap, fReal); }
#undef CheckAxisOrder
this->dc.dwAxes = this->df.dwNumObjs;
/*
* Doing the POVs is a bit tricky but not that bad. */ for (dw = 0; dw < cJoyStatePOVTotal; dw++) { if (pvac->dwPos & JOYPF_POV(dw)) { CJoy_AddObject(this, &GUID_POV, FIELD_OFFSET(DIJOYSTATE2, rgdwPOV[dw]), DIDFT_POV, dw, DIDOI_ASPECTUNKNOWN, fReal); this->dc.dwPOVs++; } }
/*
* Doing the buttons is easy since they don't have * any interesting attributes. */ this->dc.dwButtons = min(dwButtons, cJoyStateButtonTotal);
for (dw = 0; dw < this->dc.dwButtons; dw++) { CJoy_AddObject(this, &GUID_Button, FIELD_OFFSET(DIJOYSTATE2, rgbButtons[dw]), DIDFT_PSHBUTTON, dw, DIDOI_ASPECTUNKNOWN, fReal); }
AssertF(this->df.dwNumObjs <= cJoyStateObjTotal);
}
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | PreInit | * * Preallocate all the memory we will need up front, so we * don't waste time reallocating later. * *****************************************************************************/
HRESULT INLINE CJoy_PreInit(PDJ this) { HRESULT hres;
hres = ReallocCbPpv(cbCxX(cJoyStateObjTotal, DIOBJECTDATAFORMAT), &this->df.rgodf); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | InitRing0 | * * Initialize the ring 0 information maintained about the object. * * Broken out from CJoy_Init to make things less monolithic. * *****************************************************************************/
HRESULT INTERNAL CJoy_InitRing0(PDJ this) { HRESULT hres; VXDDEVICEFORMAT devf;
/*
* Note that we now allow the device to be created even if * the joystick doesn't physically exist. This is necessary * so that IDirectInputJoyConfig8 can calibrate the joystick * that doesn't exist yet. * * This won't confuse applications, however, because * IDirectInput::EnumDevices will not return phantom devices * unless the application explicitly asks for phantom devices * to be included. */
/*
* See if this joystick supports fancy notifications. * The default is "no". * * Also see if this is really a HID device (and hence our * interface is an alias). * * These things are all 9x-specific. */ #ifdef WINNT
this->dc.dwFlags |= DIDC_POLLEDDEVICE; #else
VXDINITPARMS vip;
this->dc.dwFlags |= DIDC_POLLEDDEVICE;
hres = Hel_Joy_GetInitParms(this->idJoy, &vip); if (SUCCEEDED(hres)) { if (vip.dwFlags & VIP_SENDSNOTIFY) { this->dc.dwFlags &= ~DIDC_POLLEDDEVICE; } if (vip.dwFlags & VIP_ISHID) { /*
* Use VJOYD as Alias if the device is HID * (the may be revised in CJoy_InitRing3). */ this->dc.dwFlags |= DIDC_ALIAS; } } #endif /* WINNT */
this->dc.dwSize = cbX(DIDEVCAPS);
/*
* Build the worst-case data format for the VxD. * * We must always build worst-case because sometime * later, a newer more capable joystick might show up, * with more objects than the one we imprinted on. * * Use the GUIDs for a joystick for now, and pass the flag * indicating that there is no need to check registry settings. */
CJoy_BuildDataFormat(this, &c_vacMax, cJoyStateButtonTotal, c_rgamapJoy, FALSE);
/*
* It won't actually get that high because of the * nonexistent Rx and Ry axes. */ AssertF(this->df.dwNumObjs <= cJoyStateObjTotal);
devf.cbData = cbX(DIJOYSTATE2); devf.dwExtra = this->idJoy; devf.cObj = this->df.dwNumObjs; devf.rgodf = this->df.rgodf; devf.dwEmulation = 0;
hres = Hel_Joy_CreateInstance(&devf, &this->pvi); if (SUCCEEDED(hres)) { AssertF(this->pvi); this->pjsPhys = this->pvi->pState; } else { /* IOCTL failed; hres already set */ }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | InitSemanticMap | * * Initialize the semantic mapping information. * *****************************************************************************/
void INLINE CJoy_GetAxisMap( PDJ this, PCAXISMAPPINGS* ppamap ) { if( GET_DIDEVICE_TYPEANDSUBTYPE( this->dc.dwDevType ) == MAKE_DIDEVICE_TYPE( DI8DEVTYPE_1STPERSON, DI8DEVTYPE1STPERSON_SIXDOF ) ) { *ppamap = &c_rgamap6DOF[0]; } else if( ( GET_DIDEVICE_TYPE( this->dc.dwDevType ) == DI8DEVTYPE_DRIVING ) && ( this->dc.dwAxes > 2 ) ) { /*
* There are three common forms of pedals * a. Split Y axis, below center accel, above brake * b. Y break, Z accel * c. Y accel, R brake * a. is just like a joystick but b and c need their own look ups * * Use a registry flag if one is set, otherwise, select using HASR * as there is at least one case of an X,Y,R device that reports * the presence of a bogus Z axis. */ switch( this->typi.dwFlags2 & JOYTYPE_INFOMASK ) { case JOYTYPE_INFOYYPEDALS: *ppamap = &c_rgamapJoy[0]; break;
case JOYTYPE_INFOZYPEDALS: *ppamap = &c_rgamapCarZY[0]; break;
case JOYTYPE_INFOYRPEDALS: *ppamap = &c_rgamapCarYR[0]; break;
case JOYTYPE_INFOZRPEDALS: *ppamap = &c_rgamapCarZR[0]; break;
default: if( ( this->cfg.hwc.hws.dwFlags & JOY_HWS_HASR ) && ( this->cfg.hwc.hws.dwFlags & JOY_HWS_HASZ ) ) { *ppamap = &c_rgamapCarZR[0]; } else if( this->cfg.hwc.hws.dwFlags & JOY_HWS_HASR ) { *ppamap = &c_rgamapCarYR[0]; } else if( this->cfg.hwc.hws.dwFlags & JOY_HWS_HASZ ) { *ppamap = &c_rgamapCarZY[0]; } else { *ppamap = &c_rgamapJoy[0]; } } } else { /*
* The default map is joystick * * Check for Z axis behavior override * Since the default behavior is to use Z as a slider, * only the override to a Z axis is needed here. */ if( this->typi.dwFlags2 & JOYTYPE_INFOZISZ ) { *ppamap = &c_rgamapZJoy[0]; } else { *ppamap = &c_rgamapJoy[0]; } } }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | InitSemanticMap | * * Initialize the semantic mapping information. * *****************************************************************************/
HRESULT INLINE CJoy_InitSemanticMap( PDJ this, DWORD dwAxisMask, PCAXISMAPPINGS pamap ) { HRESULT hres; /*
* Prefix warns (mb:34681) that the pointer would be null if the * number of objects on the device is zero. * This cannot be, so assert it but don't check in retail. */ AssertF( this->dc.dwAxes + this->dc.dwPOVs + this->dc.dwButtons != 0 ); if( SUCCEEDED( hres = AllocCbPpv(cbCxX( (this->dc.dwAxes + this->dc.dwPOVs + this->dc.dwButtons ), DIDOBJDEFSEM), &this->rgObjSem) ) ) { UINT Idx; PDIDOBJDEFSEM pObjSem = this->rgObjSem; LPDIOBJECTDATAFORMAT podf;
/*
* The axis mapping table does most of the work */ for( Idx = 0; Idx < cA(c_rgaattrJoy); Idx++ ) { PCAXISATTRIBUTES paattr = &c_rgaattrJoy[Idx]; if( dwAxisMask & c_rgaattrJoy[Idx].dwMask) { pObjSem->dwID = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE(c_rgaattrJoy[Idx].uidObj); pObjSem->dwSemantic = pamap[Idx].dwSemantic; pObjSem++; } }
AssertF( pObjSem == &this->rgObjSem[this->dc.dwAxes] );
/*
* POVs and buttons require no look ups. */
for( Idx = 0; Idx < this->dc.dwPOVs; Idx++ ) { pObjSem->dwID = DIDFT_POV | DIDFT_MAKEINSTANCE(Idx); pObjSem->dwSemantic = DISEM_TYPE_POV | DISEM_INDEX_SET(Idx+1); pObjSem++; }
AssertF( pObjSem == &this->rgObjSem[this->dc.dwAxes + this->dc.dwPOVs] );
for( Idx = 0; Idx < this->dc.dwButtons; Idx++ ) { pObjSem->dwID = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE(Idx); pObjSem->dwSemantic = DISEM_TYPE_BUTTON | DISEM_INDEX_SET(Idx+1); pObjSem++; }
AssertF( pObjSem == &this->rgObjSem[this->dc.dwAxes + this->dc.dwPOVs + this->dc.dwButtons] );
/*
* Now go back to pick up any extra type bits, such as FF. * Because we use the same order in creating the semantic table as * is used to create the data format, this is optimized to only * search from where the previous match was found. We cannot rely * on a one-to-one mapping because of the non-positional axes. */
podf = &this->df.rgodf[this->df.dwNumObjs]; for( pObjSem--; pObjSem >= this->rgObjSem; pObjSem-- ) { do { podf--; if( ( podf->dwType & (DIDFT_INSTANCEMASK | DIDFT_TYPEMASK) ) == pObjSem->dwID ) { pObjSem->dwID = podf->dwType; break; } } while( podf >= this->df.rgodf );
AssertF( podf >= this->df.rgodf ); } }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | InitRing3 | * * Initialize the ring 3 information maintained about the object. * * Broken out from CJoy_Init to make things less monolithic. * *****************************************************************************/
HRESULT INTERNAL CJoy_InitRing3(PDJ this ) { HRESULT hres; VXDAXISCAPS vac; PCAXISMAPPINGS pamap;
/*
* We must ask for DIJC_CALLOUT even though we don't care, * because that will trigger the Microsoft Gamepad hack-o-rama. */ hres = JoyReg_GetConfig(this->idJoy, &this->cfg, DIJC_REGHWCONFIGTYPE | DIJC_CALLOUT ); if (SUCCEEDED(hres)) { /*
* Fix phantom devices bug. See manbug: 23186 */ if( this->cfg.hwc.dwType == JOY_HW_NONE ) { hres = E_FAIL; goto done; }
/*
* Open the type key so we can grovel into the type info. * If the RegOpenKeyEx fails, the value of this->hkType * will stay zero so we won't run with garbage. * * Note that failure to open the type key is not an error. * * We need to do this before building the data format, because * BuildDataFormat needs the hkType to get the attributes. */ AssertF(this->hkType == 0); /*
* Only open the key if it is intended to exist */ if( this->cfg.hwc.dwUsageSettings & JOY_US_ISOEM ) { JoyReg_OpenTypeKey(this->cfg.wszType, MAXIMUM_ALLOWED, REG_OPTION_NON_VOLATILE, &this->hkType); }
if FAILED (JoyReg_OpenPropKey(this->cfg.wszType, MAXIMUM_ALLOWED, REG_OPTION_NON_VOLATILE, &this->hkProp)) { /*
* If we fail to open the prop key - we will continue to function with loss in functionality * Specifically no device images, etc */ }
ZeroX(vac);
hres = Hel_Joy_GetAxisCaps(this->idJoy, &vac, &this->cfg.hwc ); /*
* HACKHACK * In the case of a DX5 VJoyD, the POV0 flag can be stripped out of * the vac if the poll returns a POV0 value other than (DWORD)-1. * So add it back if the registry says we have it. */ if( this->cfg.hwc.hws.dwFlags & JOY_HWS_HASPOV ) { DWORD dwVersion = GetVersion();
/*
* Check for any Win95 version */ if( ( LOBYTE( dwVersion ) == 4 ) && ( HIBYTE( LOWORD( dwVersion ) ) < 10 ) ) { vac.dwPos |= JOYPF_POV0; }
}
AssertF(SUCCEEDED(hres));
/*
* Previous versions of DInput allow a POV granularity of 1 if * joyGetCaps returned a wCaps with JOYCAPS_POV4DIR set. * This does no good as neither drivers nor WinMM really * support this. */ this->dwPOVGranularity = 9000;
/*
* Logical ranges must be done before physical ranges, * because initializing the physical ranges will also * recompute the ramp conversion parameters. */ CJoy_InitLogRanges(this);
CJoy_InitPhysRanges(this, &this->cfg.hwc);
/*
* Need to init from HWC before building real data format so type * overrides can be taken into account. Unfortunately, until the * data format is built, we don't know what axes are available. * Since the code is already here, build the data format again. * Use the joystick look-ups and don't bother with registry flags. */ CJoy_BuildDataFormat(this, &vac, this->cfg.hwc.hws.dwNumButtons, c_rgamapJoy, FALSE ); hres = CJoy_InitFromHwc( this );
if( SUCCEEDED( hres ) ) { CJoy_GetAxisMap( this, &pamap ); /*
* At last, time to build the data format for real */ CJoy_BuildDataFormat(this, &vac, this->cfg.hwc.hws.dwNumButtons, pamap, TRUE ); hres = CJoy_InitSemanticMap( this, vac.dwPos, pamap ); }
#ifndef WINNT
if( this->hkType ) { DWORD dwFlags1; if( SUCCEEDED( JoyReg_GetValue( this->hkType, REGSTR_VAL_FLAGS1, REG_BINARY, &dwFlags1, cbX(dwFlags1) ) ) ) { if( dwFlags1 & JOYTYPE_NOHIDDIRECT ) { this->dc.dwFlags &= ~DIDC_ALIAS; } } } #endif
this->diHacks.nMaxDeviceNameLength = MAX_PATH;
} else { RPF("Unexpected error 0x%08x obtaining joystick capabilities",hres); hres = E_FAIL; }
done: return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | Init | * * Initialize the object by establishing the data format * based on the joystick capabilities. * *****************************************************************************/
HRESULT INTERNAL CJoy_Init(PDJ this, REFGUID rguid) { HRESULT hres; EnterProc(CJoy_Init, (_ "pG", this, rguid));
this->idJoy = rguid->Data1 & 0xF;
/* If joystick number is vaguely valid */ if (this->idJoy < cJoyMax) {
if (SUCCEEDED(hres = CJoy_PreInit(this)) && SUCCEEDED(hres = CJoy_InitRing0(this)) && SUCCEEDED(hres = CJoy_InitRing3(this))) { } } else { hres = DIERR_DEVICENOTREG; }
ExitOleProc(); return hres; }
/*****************************************************************************
* * CJoy_New (constructor) * *****************************************************************************/
STDMETHODIMP CJoy_New(PUNK punkOuter, REFGUID rguid, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcI(IDirectInputDeviceCallback::Joy::<constructor>, (_ "Gp", riid, ppvObj));
hres = Common_NewRiid(CJoy, punkOuter, riid, ppvObj);
if (SUCCEEDED(hres)) { /* Must use _thisPv in case of aggregation */ PDJ this = _thisPv(*ppvObj);
if (SUCCEEDED(hres = CJoy_Init(this, rguid))) { } else { Invoke_Release(ppvObj); }
}
ExitOleProcPpvR(ppvObj); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | SetDIData | * * Set DirectInput version and apphack data from CDIDev *. * * @parm DWORD | dwVer | * * DirectInput version * * @parm LPVOID | lpdihacks | * * AppHack data * * @returns * * <c S_OK> because we cannot fail. * *****************************************************************************/
STDMETHODIMP CJoy_SetDIData(PDICB pdcb, DWORD dwVer, LPVOID lpdihacks) { PDJ this; EnterProcI(IDirectInputDeviceCallback::Joy::SetDIData, (_ "pup", pdcb, dwVer, lpdihacks));
/*
* This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb);
this->dwVersion = dwVer;
CopyMemory(&this->diHacks, (LPDIAPPHACKS)lpdihacks, sizeof(this->diHacks));
ExitProcR(); return S_OK; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoy | BuildDefaultActionMap | * * Generate default mappings for the objects on this device. * * @parm LPDIACTIONFORMATW | pActionFormat | * * Actions to map. * * @parm DWORD | dwFlags | * * Flags used to indicate mapping preferences. * * @parm REFGUID | guidInst | * * Device instance GUID. * * @returns * * <c E_NOTIMPL> * *****************************************************************************/
STDMETHODIMP CJoy_BuildDefaultActionMap ( PDICB pdcb, LPDIACTIONFORMATW paf, DWORD dwFlags, REFGUID guidInst ) { HRESULT hres; PDJ this; PDIDOBJDEFSEM pObjDefSemTemp;
/*
* This is an internal interface, so we can skimp on validation. */ EnterProcI(IDirectInputDeviceCallback::Joy::BuildDefaultActionMap, (_ "ppxG", pdcb, paf, dwFlags, guidInst));
this = _thisPvNm(pdcb, dcb);
/*
* Prefix warns (win:199090) that the pointer would be null if the * size of the semantic object list is zero. * This cannot be, so assert it but don't check in retail. */ AssertF( cbCxX( this->dc.dwAxes + this->dc.dwPOVs + this->dc.dwButtons, DIDOBJDEFSEM ) ); hres = AllocCbPpv( cbCxX( ( this->dc.dwAxes + this->dc.dwPOVs + this->dc.dwButtons ), DIDOBJDEFSEM ), &pObjDefSemTemp );
if( SUCCEEDED( hres ) ) { memcpy( pObjDefSemTemp, this->rgObjSem, cbCxX( ( this->dc.dwAxes + this->dc.dwPOVs + this->dc.dwButtons ), DIDOBJDEFSEM ) ); hres = CMap_BuildDefaultDevActionMap( paf, dwFlags, guidInst, pObjDefSemTemp, this->dc.dwAxes, this->dc.dwPOVs, this->dc.dwButtons ); FreePv( pObjDefSemTemp ); }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * The long-awaited vtbls and templates * *****************************************************************************/
#define CJoy_Signature 0x2044424B /* "Joy " */
Interface_Template_Begin(CJoy) Primary_Interface_Template(CJoy, IDirectInputDeviceCallback) Interface_Template_End(CJoy)
Primary_Interface_Begin(CJoy, IDirectInputDeviceCallback) CJoy_GetInstance, CJoy_GetVersions, CJoy_GetDataFormat, CJoy_GetObjectInfo, CJoy_GetCapabilities, CDefDcb_Acquire, CDefDcb_Unacquire, CJoy_GetDeviceState, CJoy_GetDeviceInfo, CJoy_GetProperty, CJoy_SetProperty, CDefDcb_SetEventNotification, CJoy_SetCooperativeLevel, CJoy_RunControlPanel, CJoy_CookDeviceData, CJoy_CreateEffect, CJoy_GetFFConfigKey, CDefDcb_SendDeviceData, CJoy_Poll, CDefDcb_GetUsage, CDefDcb_MapUsage, CJoy_SetDIData, CJoy_BuildDefaultActionMap, Primary_Interface_End(CJoy, IDirectInputDeviceCallback)
#endif
|