/***************************************************************************** * * DIGenJ.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Generic IDirectInputDevice callback for joystick. * * Contents: * * CJoy_CreateInstance * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * 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) #if 0 #define c_podfJoyXY (&c_rgodfJoy[iJoyStateAxisX]) #define c_podfJoyZ (&c_rgodfJoy[iJoyStateAxisZ]) #define c_podfJoyR (&c_rgodfJoy[iJoyStateAxisRz]) #define c_podfJoyU (&c_rgodfJoy[iJoyStateAxisS0]) #define c_podfJoyV (&c_rgodfJoy[iJoyStateAxisS1]) #define c_podfJoyButtons (&c_rgodfJoy[cJoyStateAxisMax]) #define c_podfJoyPOV (&c_rgodfJoy[cJoyStateAxisMax+32]) #endif /* * 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

'th joystick axis * in the structure. * * @parm UINT | uiStateAxis | * * The index of the requested 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

'th joystick axis * in the structure. * * @parm UINT | uiStateAxis | * * The index of the requested 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

'th joystick axis * in the structure. * * @parm UINT | uiStateAxis | * * The index of the requested 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 axis number back to * a axis number. * * @parm UINT | uiPosAxis | * * The index of the requested axis. * * @returns * * The corresponding 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: * * - No special semantics. * * - 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. * * - No special semantics. * * - 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: * * - 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. * * - 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 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 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 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. * * @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; DWORD dwVersion; //DirectInput version #if (DIRECTINPUT_VERSION > 0x061A) DIAPPHACKS diHacks; #endif } 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_PTR 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 . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoy | AddRef | * * Increments the reference count for the interface. * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @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 . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoy | QIHelper | * * We don't have any dynamic interfaces and simply forward * to . * * @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 . * * @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 * so that this function can double as the . * *****************************************************************************/ 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); if (this->hkType) { RegCloseKey(this->hkType); } } } /***************************************************************************** * * @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_PTR 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. */ hres = JoyReg_GetConfig(this->idJoy, NULL, &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 | * * 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. * * = : The operation completed successfully. * * = : The *

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 | * * to be filled in. The * and * have already been filled in. * * Secret convenience: is equal * to . * *****************************************************************************/ 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); #if DIRECTINPUT_VERSION <= 0x0400 AssertF(pdiW->dwSize == cbX(*pdiW)); #else AssertF(IsValidSizeDIDEVICEINSTANCEW(pdiW->dwSize)); #endif /* * 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)); // lstrcatW( pdiW->tszInstanceName, L" (VJOYD)"); #endif #ifdef IDirectInputDevice2Vtbl 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); } } #endif 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, this->dwVersion); 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, this->dwVersion); } 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, NULL, &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 * * for something we didn't handle natively. * The caller will do * the default thing in response to . * *****************************************************************************/ 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 * * if the operation completed successfully. * * nothing happened. The caller will do * the default thing in response to . * *****************************************************************************/ 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, this->dwVersion); } 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 * * if the operation completed successfully. * * nothing happened. The caller will do * the default thing in response to . * *****************************************************************************/ 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; /* * ISSUE-2001/03/29-timgill DX7 VJOYD devices have same INSTANCENAME and PRODUCTNAME * 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; 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 (DIRECTINPUT_VERSION > 0x061A) if( this->diHacks.nMaxDeviceNameLength < lstrlenW(pdipstr->wsz) ) { pdipstr->wsz[this->diHacks.nMaxDeviceNameLength] = L'\0'; } #endif 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; /* * 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 * 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

. * * 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 * * if we pinged okay. * *****************************************************************************/ 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); if( this->diHacks.fNoPollUnacquire ) { hres = Hel_Joy_Ping8(this->pvi); } else { hres = Hel_Joy_Ping(this->pvi); } if (FAILED(hres)) { if( this->dwVersion < 0x05B2 ) { /* * Some Win9x games will break if Poll fails. * See OSR 4.1 Bug 62999. So we make it always succeeded. */ hres = S_OK; } else { /* * The NT and onwards CPL requires poll to return true status */ AssertF(hres == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_DEV_NOT_EXIST)); hres = DIERR_UNPLUGGED; } } 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. * * = : The operation completed successfully. * * = : The *

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 UINT | cdod | * * Number of objects to cook; zero is a valid value. * * @parm LPDIDEVICEOBJECTDATA | rgdod | * * 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. * * = : The operation completed successfully. * * = : The callback does * not cook device data. * * : The device could not be acquired. * ***************************************************************************/ STDMETHODIMP CJoy_CookDeviceData(PDICB pdcb, UINT cdod, LPDIDEVICEOBJECTDATA rgdod) { HRESULT hres; PDJ this; UINT idod; EnterProcI(IDirectInputDeviceCallback::Joy::CookDeviceData, (_ "pxp", pdcb, cdod, rgdod)); /* * This is an internal interface, so we can skimp on validation. */ this = _thisPvNm(pdcb, dcb); for (idod = 0; idod < cdod; idod++) { DWORD dwDevType = DICOOK_IDFROMDFOFS(rgdod[idod].dwOfs); if ((dwDevType & DIDFT_ABSAXIS) && DIDFT_GETINSTANCE(dwDevType) < cA(this->rgjrc)) { PJOYRANGECONVERT pjrc; pjrc = &this->rgjrc[DIDFT_GETINSTANCE(dwDevType)]; CCal_CookRange(pjrc, (PV)&rgdod[idod].dwData); } } 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 * , * , * and * * * 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)); } } 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); /* * 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; } #ifdef IDirectInputDevice2Vtbl /***************************************************************************** * * @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 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 field * to sizeof(DIDRIVERVERSIONS) before calling this method. * * @returns * * 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; } #endif /***************************************************************************** * * 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\ 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\ * \ * 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\ * * * 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. * *****************************************************************************/ #if DIRECTINPUT_VERSION > 0x0300 /***************************************************************************** * * @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 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; 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 #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoy | InitFromHwc | * * Initialize the information that is kept in the * . * * Broken out from CJoy_InitRing3 to make things less monolithic. * * The structure already contains joystick * configuration information. * *****************************************************************************/ HRESULT INLINE CJoy_InitFromHwc(PDJ this) { HRESULT hres; #define hwc this->cfg.hwc if (hwc.hws.dwFlags & JOY_HWS_ISYOKE) { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_FLIGHTSTICK); } else if (hwc.hws.dwFlags & JOY_HWS_ISGAMEPAD) { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_GAMEPAD); } else if (hwc.hws.dwFlags & JOY_HWS_ISCARCTRL) { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_WHEEL); } else if (hwc.hws.dwFlags & JOY_HWS_ISHEADTRACKER) { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_HEADTRACKER); } else if (hwc.dwUsageSettings & JOY_US_HASRUDDER) { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_RUDDER); } else if (hwc.dwType < JOY_HW_PREDEFMAX && hwc.dwType != JOY_HW_NONE) { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_TRADITIONAL); } else { this->dc.dwDevType = MAKE_DIDEVICE_TYPE(DIDEVTYPE_JOYSTICK, DIDEVTYPEJOYSTICK_UNKNOWN); } #undef hwc 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] != TEXT('\0')) { hres = JoyReg_GetTypeInfo(this->cfg.wszType, &this->typi, DITC_DISPLAYNAME); if (SUCCEEDED(hres)) { } else { if( fWinnt ) { 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)); } } } else { ZeroMemory(&this->typi, cbX(this->typi)); } /* * If that was unsuccessful, then make one up ourselves. */ 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 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 flag. * *****************************************************************************/ void INTERNAL CJoy_AddObject(PDJ this, PCGUID pguid, DWORD dwOfs, DWORD dwDevType, UINT uiObj, DWORD dwAspect) { LPDIOBJECTDATAFORMAT podf; EnterProc(CJoy_AddObject, (_ "pGxxx", this, pguid, dwDevType, uiObj, dwAspect)); 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; } CType_RegGetTypeInfo(this->hkType, podf, FALSE); } /***************************************************************************** * * @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 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 | * * for these axes. * *****************************************************************************/ typedef struct AXISATTRIBUTES { DWORD dwMask; /* Mask that identifies the axis */ PCGUID pguid; /* GUID for the object */ UINT dib; /* Delta for the data offset */ UINT diObj; /* Delta for object index */ } AXISATTRIBUTES, *PAXISATTRIBUTES; typedef const AXISATTRIBUTES *PCAXISATTRIBUTES; const AXISATTRIBUTES c_rgaattrJoy[] = { { JOYPF_X, &GUID_XAxis, ibJoyStateAxisX, iJoyStateAxisX, }, { JOYPF_Y, &GUID_YAxis, ibJoyStateAxisY, iJoyStateAxisY, }, { JOYPF_Z, &GUID_ZAxis, ibJoyStateAxisZ, iJoyStateAxisZ, }, { JOYPF_R, &GUID_RzAxis, ibJoyStateAxisRz, iJoyStateAxisRz, }, { JOYPF_U, &GUID_Slider, ibJoyStateAxisS0, iJoyStateAxisS0, }, { JOYPF_V, &GUID_Slider, ibJoyStateAxisS1, iJoyStateAxisS1, }, }; void INTERNAL CJoy_BuildAxes(PDJ this, DWORD dwCaps, UINT ib, UINT uiObj, DWORD dwAspect) { int iaattr; for (iaattr = 0; iaattr < cA(c_rgaattrJoy); iaattr++) { PCAXISATTRIBUTES paattr = &c_rgaattrJoy[iaattr]; if (dwCaps & paattr->dwMask) { CJoy_AddObject(this, paattr->pguid, paattr->dib + ib, DIDFT_ABSAXIS, uiObj + paattr->diObj, dwAspect); } } } /***************************************************************************** * * @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) { 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); } 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); } 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); } 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); } #undef CheckAxisOrder this->dc.dwAxes = this->df.dwNumObjs; /* * 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); } /* * Doing the POVs is a bit trickier 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); this->dc.dwPOVs++; } } 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 IDirectInputJoyConfig 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. */ /* * This code is indented an extra level for historical reasons. */ this->dc.dwSize = cbX(DIDEVCAPS); /* this->dc.dwDevType will be initialized later */ /* * 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. */ this->dc.dwFlags |= DIDC_POLLEDDEVICE; /* This never happens on NT */ if( !fWinnt ) { VXDINITPARMS vip; 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) { #ifdef DEBUG TCHAR szJoyProp[] = REGSTR_PATH_PRIVATEPROPERTIES TEXT("\\Joystick"); HKEY hkJoyProp; TCHAR szUseHid[] = TEXT("UseHidPath"); DWORD dwUseHid = 0; hres = hresMumbleKeyEx(HKEY_LOCAL_MACHINE, szJoyProp, DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &hkJoyProp); if ( SUCCEEDED(hres) ) { DWORD cb = sizeof(dwUseHid); LONG lRc; lRc = RegQueryValueEx(hkJoyProp, szUseHid, 0, 0, (LPBYTE)&dwUseHid, &cb); if ( lRc != ERROR_SUCCESS ) { DWORD dwDefault = 1; dwUseHid = dwDefault; lRc = RegSetValueEx(hkJoyProp, szUseHid, 0, REG_DWORD, (LPBYTE)&dwDefault, cb); } RegCloseKey(hkJoyProp); } if( dwUseHid ) { this->dc.dwFlags |= DIDC_ALIAS; } #endif /* * Always use VJOYD as Alias if the device is HID. */ this->dc.dwFlags |= DIDC_ALIAS; } } } /* * 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. */ CJoy_BuildDataFormat(this, &c_vacMax, cJoyStateButtonTotal); /* * 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 | 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; JOYCAPS jc; /* * 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, &jc, &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); } hres = Hel_Joy_GetAxisCaps(this->idJoy, &vac, &jc); /* * 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; } } jc.wNumButtons = this->cfg.hwc.hws.dwNumButtons; AssertF(SUCCEEDED(hres)); CJoy_BuildDataFormat(this, &vac, jc.wNumButtons); if (jc.wCaps & JOYCAPS_POV4DIR) { this->dwPOVGranularity = 9000; } else { this->dwPOVGranularity = 1; } /* * 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); hres = CJoy_InitFromHwc(this); #if !defined(WINNT) && DIRECTINPUT_VERSION > 0x050A 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 #if (DIRECTINPUT_VERSION > 0x061A) this->diHacks.nMaxDeviceNameLength = MAX_PATH; #endif } else { RPF("Unexpected error obtaining joystick capabilities"); 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::, (_ "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 * * because we don't support usages. * *****************************************************************************/ STDMETHODIMP CJoy_SetDIData(PDICB pdcb, DWORD dwVer, LPVOID lpdihacks) { HRESULT hres; 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; if( ( this->dwVersion < 0x0700 ) && ( this->dwVersion != 0x05B2 ) ) { /* * Post DX7 Gold Fix * Keep this as the default interface for older apps * unless it's the CPL */ this->dc.dwFlags &= ~DIDC_ALIAS; } ((LPDIAPPHACKS)lpdihacks)->dwDevType = this->dc.dwDevType; CopyMemory(&this->diHacks, (LPDIAPPHACKS)lpdihacks, sizeof(this->diHacks)); hres = S_OK; 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, Primary_Interface_End(CJoy, IDirectInputDeviceCallback)