mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3452 lines
106 KiB
3452 lines
106 KiB
/*****************************************************************************
|
|
*
|
|
* DIJoyCfg.c
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* IDirectInputJoyConfig8
|
|
*
|
|
* Contents:
|
|
*
|
|
* CJoyCfg_New
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "dinputpr.h"
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* The sqiffle for this file.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define sqfl sqflJoyCfg
|
|
|
|
|
|
BOOL fVjoydDeviceNotExist = TRUE;
|
|
#ifdef WINNT
|
|
WCHAR wszDITypeName[128];
|
|
#endif
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Declare the interfaces we will be providing.
|
|
*
|
|
* WARNING! If you add a secondary interface, you must also change
|
|
* CJoyCfg_New!
|
|
*
|
|
*****************************************************************************/
|
|
|
|
Primary_Interface(CJoyCfg, IDirectInputJoyConfig8);
|
|
|
|
Interface_Template_Begin(CJoyCfg)
|
|
Primary_Interface_Template(CJoyCfg, IDirectInputJoyConfig8)
|
|
Interface_Template_End(CJoyCfg)
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @struct CJoyCfg |
|
|
*
|
|
* The <i IDirectInputJoyConfig8> object. Note that this is
|
|
* aggregated onto the main <i IDirectInput> object.
|
|
*
|
|
* @field IDirectInputJoyConfig8 | djc |
|
|
*
|
|
* The object (containing vtbl).
|
|
*
|
|
* @field BOOL | fAcquired:1 |
|
|
*
|
|
* Set if joystick configuration has been acquired.
|
|
*
|
|
* @field BOOL | fCritInited:1 |
|
|
*
|
|
* Set if the critical section has been initialized.
|
|
*
|
|
* @field HKEY | hkTypesW |
|
|
*
|
|
* Read/write key to access the joystick types.
|
|
* This key is created only while acquired.
|
|
*
|
|
* @field DWORD | idJoyCache |
|
|
*
|
|
* The identifier of the joystick in the effect shepherd cache,
|
|
* if there is anything in the cache at all.
|
|
*
|
|
* @field IDirectInputEffectShepherd * | pes |
|
|
*
|
|
* The cached effect shepherd itself.
|
|
*
|
|
* @field LONG | cCrit |
|
|
*
|
|
* Number of times the critical section has been taken.
|
|
* Used only in XDEBUG to check whether the caller is
|
|
* releasing the object while another method is using it.
|
|
*
|
|
* @field DWORD | thidCrit |
|
|
*
|
|
* The thread that is currently in the critical section.
|
|
* Used only in DEBUG for internal consistency checking.
|
|
*
|
|
* @field CRITICAL_SECTION | crst |
|
|
*
|
|
* Object critical section. Must be taken when accessing
|
|
* volatile member variables.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct CJoyCfg
|
|
{
|
|
|
|
/* Supported interfaces */
|
|
IDirectInputJoyConfig8 djc;
|
|
|
|
BOOL fAcquired:1;
|
|
BOOL fCritInited:1;
|
|
|
|
HKEY hkTypesW;
|
|
HWND hwnd;
|
|
|
|
DWORD discl;
|
|
|
|
DWORD idJoyCache;
|
|
LPDIRECTINPUTEFFECTSHEPHERD pes;
|
|
|
|
RD(LONG cCrit;)
|
|
D(DWORD thidCrit;)
|
|
CRITICAL_SECTION crst;
|
|
|
|
} CJoyCfg, JC, *PJC;
|
|
|
|
typedef LPDIRECTINPUTJOYCONFIG8 PDJC;
|
|
|
|
|
|
#define ThisClass CJoyCfg
|
|
#define ThisInterface IDirectInputJoyConfig8
|
|
#define ThisInterfaceT IDirectInputJoyConfig8
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Forward references
|
|
*
|
|
* Not really needed; just a convenience, because Finalize
|
|
* calls Unacquire to clean up in the case where the caller forgot.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP CJoyCfg_InternalUnacquire(PV pdd);
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func TCHAR | CJoyCfg_CharFromType |
|
|
*
|
|
* Convert a predefined type number to a character.
|
|
*
|
|
* @func UINT | CJoyCfg_TypeFromChar |
|
|
*
|
|
* Convert a character back to a predefined type number.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define CJoyCfg_CharFromType(t) ((TCHAR)(L'0' + t))
|
|
#define CJoyCfg_TypeFromChar(tch) ((tch) - L'0')
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputJoyConfig8 | EnterCrit |
|
|
*
|
|
* Enter the object critical section.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INLINE
|
|
CJoyCfg_EnterCrit(PJC this)
|
|
{
|
|
EnterCriticalSection(&this->crst);
|
|
D(this->thidCrit = GetCurrentThreadId());
|
|
RD(InterlockedIncrement(&this->cCrit));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputJoyConfig8 | LeaveCrit |
|
|
*
|
|
* Leave the object critical section.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INLINE
|
|
CJoyCfg_LeaveCrit(PJC this)
|
|
{
|
|
#ifdef XDEBUG
|
|
AssertF(this->cCrit);
|
|
AssertF(this->thidCrit == GetCurrentThreadId());
|
|
if(InterlockedDecrement(&this->cCrit) == 0)
|
|
{
|
|
D(this->thidCrit = 0);
|
|
}
|
|
#endif
|
|
LeaveCriticalSection(&this->crst);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @mfunc BOOL | CJoyCfg | InCrit |
|
|
*
|
|
* Nonzero if we are in the critical section.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL INTERNAL
|
|
CJoyCfg_InCrit(PJC this)
|
|
{
|
|
return this->cCrit && this->thidCrit == GetCurrentThreadId();
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CJoyCfg | IsAcquired |
|
|
*
|
|
* Check that the device is acquired.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns
|
|
* <c S_OK> if all is well, or <c DIERR_NOTACQUIRED> if
|
|
* the device is not acquired.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef XDEBUG
|
|
\
|
|
#define CJoyCfg_IsAcquired_(pdd, z) \
|
|
_CJoyCfg_IsAcquired_(pdd) \
|
|
|
|
#endif
|
|
|
|
HRESULT INLINE
|
|
CJoyCfg_IsAcquired_(PJC this, LPCSTR s_szProc)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("ERROR %s: Not acquired", s_szProc);
|
|
hres = DIERR_NOTACQUIRED;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
#define CJoyCfg_IsAcquired(pdd) \
|
|
CJoyCfg_IsAcquired_(pdd, s_szProc) \
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CJoyCfg::QueryInterface (from IUnknown)
|
|
* CJoyCfg::AddRef (from IUnknown)
|
|
* CJoyCfg::Release (from IUnknown)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CJoyCfg | 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 | CJoyCfg | 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 | CJoyCfg | 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 | CJoyCfg | 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 | CJoyCfg | 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(CJoyCfg)
|
|
Default_AddRef(CJoyCfg)
|
|
Default_Release(CJoyCfg)
|
|
|
|
#else
|
|
|
|
#define CJoyCfg_QueryInterface Common_QueryInterface
|
|
#define CJoyCfg_AddRef Common_AddRef
|
|
#define CJoyCfg_Release Common_Release
|
|
|
|
#endif
|
|
|
|
#define CJoyCfg_QIHelper Common_QIHelper
|
|
#define CJoyCfg_AppFinalize Common_AppFinalize
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CJoyCfg | InternalUnacquire |
|
|
*
|
|
* Do the real work of an unacquire.
|
|
*
|
|
* See <mf IDirectInputJoyConfig8::Unacquire> for more
|
|
* information.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG88
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code.
|
|
* See <mf IDirectInputJoyConfig8::Unacquire> for more
|
|
* information.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_InternalUnacquire(PJC this)
|
|
{
|
|
HRESULT hres;
|
|
EnterProc(CJoyCfg_InternalUnacquire, (_ "p", this));
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* interfering with us while we're unacquiring.
|
|
*/
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
|
|
AssertF(this->hkTypesW);
|
|
|
|
RegCloseKey(this->hkTypesW);
|
|
|
|
this->hkTypesW = 0;
|
|
|
|
Invoke_Release(&this->pes);
|
|
|
|
Excl_Unacquire(&IID_IDirectInputJoyConfig, this->hwnd, this->discl);
|
|
|
|
this->fAcquired = 0;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
CJoyCfg_LeaveCrit(this);
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | CJoyCfg_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
|
|
CJoyCfg_Finalize(PV pvObj)
|
|
{
|
|
PJC this = pvObj;
|
|
|
|
#ifdef XDEBUG
|
|
if(this->cCrit)
|
|
{
|
|
RPF("IDirectInputJoyConfig8::Release: Another thread is using the object; crash soon!");
|
|
}
|
|
#endif
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
CJoyCfg_InternalUnacquire(this);
|
|
}
|
|
|
|
AssertF(this->pes == 0);
|
|
|
|
if(this->hkTypesW)
|
|
{
|
|
RegCloseKey(this->hkTypesW);
|
|
}
|
|
|
|
if(this->fCritInited)
|
|
{
|
|
DeleteCriticalSection(&this->crst);
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | SetCooperativeLevel |
|
|
*
|
|
* Establish the cooperativity level for the instance of
|
|
* the device.
|
|
*
|
|
* The only cooperative levels supported for the
|
|
* <i IDirectInputJoyConfig8> interface are
|
|
* <c DISCL_EXCLUSIVE> and <c DISCL_BACKGROUND>.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm HWND | hwnd |
|
|
*
|
|
* The window associated with the interface. This parameter
|
|
* must be non-NULL and must be a top-level window.
|
|
*
|
|
* It is an error to destroy the window while it is still
|
|
* associated with an <i IDirectInputJoyConfig8> interface.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags which describe the cooperativity level associated
|
|
* with the device.
|
|
*
|
|
* The value must be
|
|
* <c DISCL_EXCLUSIVE> <vbar> <c DISCL_BACKGROUND>.
|
|
*
|
|
* @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 hwnd> parameter is not a valid pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_SetCooperativeLevel(PDJC pdjc, HWND hwnd, DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::SetCooperativityLevel,
|
|
(_ "pxx", pdjc, hwnd, dwFlags));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
if(dwFlags != (DISCL_EXCLUSIVE | DISCL_BACKGROUND))
|
|
{
|
|
RPF("%s: Cooperative level must be "
|
|
"DISCL_EXCLUSIVE | DISCL_BACKGROUND", s_szProc);
|
|
hres = E_NOTIMPL;
|
|
} else if(GetWindowPid(hwnd) == GetCurrentProcessId())
|
|
{
|
|
this->hwnd = hwnd;
|
|
this->discl = dwFlags;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("ERROR %s: window must belong to current process", s_szProc);
|
|
hres = E_HANDLE;
|
|
}
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | Acquire |
|
|
*
|
|
* Acquire "joystick configuration mode". Only one application can
|
|
* be in joystick configuration mode at a time; subsequent
|
|
* applications will receive the error <c DIERR_OTHERAPPHASPRIO>.
|
|
*
|
|
* After entering configuration mode, the application may
|
|
* make alterations to the global joystick configuration
|
|
* settings. It is encouraged that the application
|
|
* re-check the existing settings before installing the new
|
|
* ones in case another application had changed the settings
|
|
* in the interim.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @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_OTHERAPPHASPRIO>: Another application is already
|
|
* in joystick configuration mode.
|
|
*
|
|
* <c DIERR_INSUFFICIENTPRIVS>: The current user does not have
|
|
* the necessary permissions to alter the joystick configuration.
|
|
*
|
|
* <c DIERR_DEVICECHANGE>: Another application has changed
|
|
* the global joystick configuration. The interface needs
|
|
* to be re-initialized.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_Acquire(PDJC pdjc)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::Acquire, (_ "p", pdjc));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* acquiring or changing the data format while we're acquiring.
|
|
*/
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(this->discl == 0)
|
|
{
|
|
RPF("%s: Cooperative level not yet set", s_szProc);
|
|
hres = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
AssertF(this->hkTypesW);
|
|
hres = S_FALSE;
|
|
} else if(SUCCEEDED(hres = Excl_Acquire(&IID_IDirectInputJoyConfig,
|
|
this->hwnd, this->discl)))
|
|
{
|
|
AssertF(this->hkTypesW == 0);
|
|
|
|
|
|
hres = hresMumbleKeyEx(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_JOYOEM,
|
|
DI_KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&this->hkTypesW);
|
|
|
|
if(SUCCEEDED(hres) )
|
|
{
|
|
this->fAcquired = 1;
|
|
} else
|
|
{
|
|
RegCloseKey(this->hkTypesW);
|
|
this->hkTypesW = 0;
|
|
hres = DIERR_INSUFFICIENTPRIVS;
|
|
}
|
|
|
|
}
|
|
|
|
done:;
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | Unacquire |
|
|
*
|
|
* Unacquire "joystick configuration mode". Before unacquiring
|
|
* configuration mode, the application must perform an
|
|
* <mf IDirectInputJoyConfig8::SendNotify> to propagate
|
|
* the changes in the joystick configuration
|
|
* to all device drivers and applications.
|
|
*
|
|
* Applications which hold interfaces to a joystick which is
|
|
* materially affected by a change in configuration will
|
|
* receive the <c DIERR_DEVICECHANGE> error code until the
|
|
* device is re-initialized.
|
|
*
|
|
* Examples of material changes to configuration include
|
|
* altering the number of axes or the number of buttons.
|
|
* In comparison, changes to device calibration
|
|
* are handled internally by
|
|
* DirectInput and are transparent to the application.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration mode was
|
|
* not acquired.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_Unacquire(PDJC pdjc)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::Unacquire, (_ "p", pdjc));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
hres = CJoyCfg_InternalUnacquire(this);
|
|
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | SendNotify |
|
|
*
|
|
* Notifies device drivers and applications that changes to
|
|
* the device configuration have been made. An application
|
|
* which changes device configurations must invoke this
|
|
* method after the changes have been made (and before
|
|
* unacquiring).
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration mode was
|
|
* not acquired.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_SendNotify(PDJC pdjc)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::SendNotify, (_ "p", pdjc));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
#ifdef WINNT
|
|
Excl_SetConfigChangedTime( GetTickCount() );
|
|
PostMessage(HWND_BROADCAST, g_wmJoyChanged, 0, 0L);
|
|
#else
|
|
joyConfigChanged(0);
|
|
#endif
|
|
|
|
/*
|
|
* If we don't have a joyConfigChanged, it's probably just
|
|
* because we're running on NT and don't need it.
|
|
*/
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
hres = DIERR_NOTACQUIRED;
|
|
}
|
|
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | JoyCfg_ConvertCurrentConfigs |
|
|
*
|
|
* Converts any OEMType name matching the first input string and
|
|
* replaces it with the other input string.
|
|
*
|
|
* @parm IN LPTSTR | szFindType |
|
|
*
|
|
* String to match.
|
|
*
|
|
* @parm IN LPTSTR | szReplaceType |
|
|
*
|
|
* String to replace any matches instances.
|
|
*
|
|
* @returns
|
|
*
|
|
* A COM success code unless the current configuration key could not
|
|
* be opened, or a type that needed to be replaced could not be
|
|
* overwritten.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT JoyCfg_ConvertCurrentConfigs( LPTSTR szFindType, LPTSTR szReplaceType )
|
|
{
|
|
HRESULT hres;
|
|
LONG lRc;
|
|
HKEY hkCurrCfg;
|
|
UINT JoyId;
|
|
TCHAR szTestType[MAX_JOYSTRING];
|
|
TCHAR szTypeName[MAX_JOYSTRING];
|
|
DWORD cb;
|
|
|
|
EnterProcI(JoyCfg_ConvertCurrentConfigs, (_ "ss", szFindType, szReplaceType ));
|
|
|
|
hres = JoyReg_OpenConfigKey( (UINT)(-1), KEY_WRITE, REG_OPTION_NON_VOLATILE, &hkCurrCfg );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
for( JoyId = 0; (JoyId < 16) || ( lRc == ERROR_SUCCESS ); JoyId++ )
|
|
{
|
|
wsprintf( szTypeName, REGSTR_VAL_JOYNOEMNAME, JoyId+1 );
|
|
cb = sizeof( szTestType );
|
|
lRc = RegQueryValueEx( hkCurrCfg, szTypeName, 0, NULL, (PBYTE)szTestType, &cb );
|
|
if( lRc == ERROR_SUCCESS )
|
|
{
|
|
if( !lstrcmpi( szTestType, szFindType ) )
|
|
{
|
|
cb = sizeof( szReplaceType) * (1 + lstrlen( szReplaceType ));
|
|
lRc = RegSetValueEx( hkCurrCfg, szTypeName, 0, REG_SZ, (PBYTE)szReplaceType, cb );
|
|
if( lRc != ERROR_SUCCESS )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("RegSetValueEx failed to replace type of %s 0x%08x"),
|
|
szTypeName, lRc );
|
|
/* This is the only error that counts as an error in this loop */
|
|
hres = hresReg( lRc );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("JoyReg_OpenConfigKey failed code 0x%08x"), hres );
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
|
|
} /* JoyCfg_ConvertCurrentConfigs */
|
|
|
|
|
|
#ifdef WINNT
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | JoyCfg_FixHardwareId |
|
|
*
|
|
* Fixes the hardwareId for an analog type by assinging a VID/PID to
|
|
* it and recreating the type using that hardwareId.
|
|
*
|
|
* @parm IN HKEY | hkTypesR |
|
|
*
|
|
* Handle of key opened to the root of types.
|
|
*
|
|
* @parm IN HKEY | hkSrc |
|
|
*
|
|
* Handle of key opened to the original type.
|
|
*
|
|
* @parm IN PTCHAR | ptszPrefName |
|
|
*
|
|
* VID&PID name from the HardwareID if present, NULL otherwise
|
|
*
|
|
* @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 key is valid
|
|
* <c DI_NOEFFECT> = <c S_FALSE> The key should be ignored
|
|
*
|
|
* <c OLE_E_ENUM_NOMORE> = The key has been fixed but enumeration
|
|
* must be restarted.
|
|
* <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>: Out of memory.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
JoyCfg_FixHardwareId( HKEY hkTypesR, HKEY hkSrc, PTCHAR szSrcType , PTCHAR ptszPrefName)
|
|
{
|
|
HRESULT hres;
|
|
HKEY hkNew;
|
|
BYTE PIDlow;
|
|
DWORD ClassLen;
|
|
PTCHAR szClassName;
|
|
TCHAR szDestType[sizeof( ANALOG_ID_ROOT ) + 2]; //Two digits will be appended
|
|
TCHAR szHardwareId[MAX_JOYSTRING];
|
|
|
|
EnterProcI(JoyCfg_FixHardwareId, (_ "xxs", hkTypesR, hkSrc, szSrcType));
|
|
|
|
hres = hresReg( RegQueryInfoKey( hkSrc, // handle to key to query
|
|
NULL, // Class
|
|
&ClassLen, // ClassLen
|
|
NULL, // Reserved
|
|
NULL, NULL, NULL, // NumSubKeys, MaxSubKeyLen, MaxClassLen
|
|
NULL, NULL, NULL, // NumValues, MaxValueNameLen, MaxValueLen
|
|
NULL, NULL ) ); // Security descriptor, last write
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
ClassLen++;
|
|
/*
|
|
* Part of mb:34633 (see below, 2 comments) was that prefix considers
|
|
* the case of zero bytes being requested in the following call so
|
|
* assert that we always ask for some memory otherwise checking the
|
|
* result does not guarantee that the pointer is valid.
|
|
*/
|
|
AssertF( ClassLen * sizeof(szClassName[0]) );
|
|
hres = AllocCbPpv( ClassLen * sizeof(szClassName[0]), &szClassName );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
hres = hresReg( RegQueryInfoKey( hkSrc, // handle to key to query
|
|
szClassName, // Class
|
|
&ClassLen, // ClassLen
|
|
NULL, // Reserved
|
|
NULL, NULL, NULL, // NumSubKeys, MaxSubKeyLen, MaxClassLen
|
|
NULL, NULL, NULL, // NumValues, MaxValueNameLen, MaxValueLen
|
|
NULL, NULL ) ); // Security descriptor, last write
|
|
if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("RegQueryInfoKey on type %s for class name failed 0x%04x"),
|
|
szSrcType, LOWORD(hres) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("Failed to allocate %d bytes for class name of type %s, error 0x%04x"),
|
|
ClassLen, szSrcType, LOWORD(hres) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("RegQueryInfoKey on type %s for class name length failed 0x%04x"),
|
|
szSrcType, LOWORD(hres) );
|
|
/* Make sure not to free an uninitialized pointer */
|
|
szClassName = NULL;
|
|
}
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
for( PIDlow = JOY_HW_PREDEFMAX+1; PIDlow; PIDlow++ )
|
|
{
|
|
if (ptszPrefName)
|
|
{
|
|
lstrcpy( szDestType, ptszPrefName);
|
|
#ifdef UNICODE
|
|
CharUpperW(szDestType);
|
|
#else
|
|
CharUpper(szDestType);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
wsprintf( szDestType, TEXT("%s%02X"), ANALOG_ID_ROOT, PIDlow );
|
|
}
|
|
hres = hresRegCopyKey( hkTypesR, szSrcType, szClassName, hkTypesR, szDestType, &hkNew );
|
|
if( hres == S_OK )
|
|
{
|
|
/*
|
|
* Prefix warns that hkNew may be uninitialized (mb:34633)
|
|
* however hresRegCopyKey only returns a SUCCESS if hkNew
|
|
* is initialized to an opened key handle.
|
|
*/
|
|
hres = hresRegCopyBranch( hkSrc, hkNew );
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
if (!ptszPrefName)
|
|
{
|
|
#ifdef MULTI_SZ_HARDWARE_IDS
|
|
/*
|
|
* Make up the hardwareId using the assigned PID with a generic hardwareId appended
|
|
*/
|
|
int CharIdx = 0;
|
|
while( TRUE )
|
|
{
|
|
CharIdx += wsprintf( &szHardwareId[CharIdx], TEXT("%s%s%02X"), TEXT("GamePort\\"), ANALOG_ID_ROOT, PIDlow );
|
|
CharIdx++; /* Leave NULL terminator in place */
|
|
if( PIDlow )
|
|
{
|
|
PIDlow = 0; /* Trash this value to make the generic PID on second iteration */
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
szHardwareId[CharIdx++] = TEXT('\0'); /* MULTI_SZ */
|
|
|
|
hres = hresReg( RegSetValueEx( hkNew, REGSTR_VAL_JOYOEMHARDWAREID, 0,
|
|
REG_MULTI_SZ, (PBYTE)szHardwareId, (DWORD)( sizeof(szHardwareId[0]) * CharIdx ) ) );
|
|
if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("JoyCfg_FixHardwareId: failed to write hardware ID %s"), szHardwareId );
|
|
}
|
|
#else
|
|
/*
|
|
* Make up the hardwareId using the assigned PID
|
|
*/
|
|
int CharIdx = 0;
|
|
CharIdx = wsprintf( szHardwareId, TEXT("%s%s%02X"), TEXT("GamePort\\"), ANALOG_ID_ROOT, PIDlow );
|
|
CharIdx++; /* Leave NULL terminator in place */
|
|
|
|
hres = hresReg( RegSetValueEx( hkNew, REGSTR_VAL_JOYOEMHARDWAREID, 0,
|
|
REG_SZ, (PBYTE)szHardwareId, (DWORD)( sizeof(szHardwareId[0]) * CharIdx ) ) );
|
|
if( FAILED( hres ) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("JoyCfg_FixHardwareId: failed to write hardware ID %s"), szHardwareId );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Prefix warns that hkNew may be uninitialized (mb:34633)
|
|
* however hresRegCopyKey only returns a SUCCESS if hkNew
|
|
* is initialized to an opened key handle.
|
|
*/
|
|
RegCloseKey( hkNew );
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
hres = JoyCfg_ConvertCurrentConfigs( szSrcType, szDestType );
|
|
}
|
|
|
|
DIWinnt_RegDeleteKey( hkTypesR, ( SUCCEEDED( hres ) ) ? szSrcType
|
|
: szDestType );
|
|
break;
|
|
}
|
|
else if( SUCCEEDED( hres ) )
|
|
{
|
|
/*
|
|
* Prefix warns that hkNew may be uninitialized (mb:37926)
|
|
* however hresRegCopyKey only returns a SUCCESS if hkNew
|
|
* is initialized to an opened key handle.
|
|
*/
|
|
/*
|
|
* The key already existed so keep looking
|
|
*/
|
|
RegCloseKey( hkNew );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* RegCopyKey should have already posted errors
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
if( !PIDlow )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("JoyCfg_FixHardwareId: no free analog keys for type %s"),
|
|
szSrcType );
|
|
hres = DIERR_NOTFOUND;
|
|
}
|
|
}
|
|
|
|
if( szClassName )
|
|
{
|
|
FreePpv( &szClassName );
|
|
}
|
|
|
|
|
|
ExitOleProc();
|
|
|
|
return( hres );
|
|
} /* JoyCfg_FixHardwareId */
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | JoyCfg_CheckTypeKey |
|
|
*
|
|
* Checks the contents of a type key for validity on the current OS
|
|
* and if not valid, try to make it so.
|
|
*
|
|
* Only custom analog types can be fixed and this only needs to be
|
|
* done on a WDM enabled OS as non-WDM requirements are a sub-set of
|
|
* the WDM ones.
|
|
*
|
|
* @parm IN HKEY | hkTypesR |
|
|
*
|
|
* Handle of key opened to the root of types.
|
|
*
|
|
* @parm IN LPTSTR | szType |
|
|
*
|
|
* Receives a pointer either an ansi or UNICODE key name to test.
|
|
*
|
|
* @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 key is valid
|
|
* <c DI_NOEFFECT> = <c S_FALSE> The key should be ignored
|
|
*
|
|
* <c OLE_E_ENUM_NOMORE> = The key has been fixed but enumeration
|
|
* must be restarted.
|
|
* <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>: Out of memory.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
JoyCfg_CheckTypeKey( HKEY hkTypesR, LPTSTR szType )
|
|
{
|
|
HRESULT hres;
|
|
HKEY hk;
|
|
LONG lRc;
|
|
DWORD cb;
|
|
|
|
TCHAR tszCallout[MAX_JOYSTRING];
|
|
TCHAR tszHardwareId[MAX_JOYSTRING];
|
|
#ifdef WINNT
|
|
JOYREGHWSETTINGS hws;
|
|
TCHAR* ptszLastSlash=NULL;
|
|
#endif
|
|
|
|
EnterProcI(JoyCfg_CheckTypeKey, (_ "xs",hkTypesR, szType));
|
|
|
|
/*
|
|
* Open read only just in case we don't have better permission to any
|
|
* of the type sub-keys.
|
|
*/
|
|
lRc = RegOpenKeyEx( hkTypesR, szType, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hk );
|
|
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
/*
|
|
* Gather the needed results using standard registry functions so
|
|
* that the exact return code is known.
|
|
*/
|
|
|
|
lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMNAME, NULL, NULL, NULL, NULL );
|
|
|
|
#ifdef WINNT
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
cb = cbX(hws);
|
|
lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMDATA, NULL, NULL, (PBYTE)&hws, &cb );
|
|
if( ( lRc == ERROR_SUCCESS ) && ( hws.dwFlags & JOY_HWS_AUTOLOAD ) )
|
|
{
|
|
/*
|
|
* WARNING goto
|
|
* If we have a name and JOY_HWS_AUTOLOAD is set, that's all we need
|
|
*/
|
|
RegCloseKey( hk );
|
|
hres = S_OK;
|
|
goto fast_out;
|
|
}
|
|
|
|
if( lRc == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
hws.dwFlags = 0;
|
|
lRc = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
cb = cbX(tszCallout);
|
|
lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMCALLOUT, NULL, NULL, (PBYTE)tszCallout, &cb );
|
|
if( lRc == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
tszCallout[0] = TEXT('\0');
|
|
lRc = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
cb = cbX(tszHardwareId);
|
|
lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMHARDWAREID, NULL, NULL, (PBYTE)tszHardwareId, &cb );
|
|
if( lRc == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
tszHardwareId[0] = TEXT('\0');
|
|
lRc = ERROR_SUCCESS;
|
|
}
|
|
#ifdef WINNT
|
|
else
|
|
{
|
|
TCHAR* ptsz;
|
|
for (ptsz = tszHardwareId;*ptsz!='\0';++ptsz)
|
|
{
|
|
if (*ptsz == '\\')
|
|
{
|
|
ptszLastSlash = ptsz;
|
|
}
|
|
}
|
|
if (ptszLastSlash)
|
|
{
|
|
ptszLastSlash++; //next char is the one we want
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
if(lRc != ERROR_SUCCESS )
|
|
{
|
|
RegCloseKey( hk );
|
|
}
|
|
}
|
|
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
#ifdef WINNT
|
|
SHORT DontCare;
|
|
#endif
|
|
WCHAR wszType[18];
|
|
|
|
TToU( wszType, cA(wszType),szType );
|
|
|
|
/*
|
|
* Work out the status of this type based on the OS and the registry data
|
|
*
|
|
* Note on 98 we allow WDM types to be enumerated but do not convert
|
|
* analog types to WDM. We may want to convert analog types if we get
|
|
* WDM gameport drivers appear for gameports that are incompatible with
|
|
* msanalog.
|
|
*/
|
|
|
|
#define HAS_VIDPID ( ParseVIDPID( &DontCare, &DontCare, wszType ) )
|
|
#define HAS_HARDWARE_ID ( tszHardwareId[0] != TEXT('\0') )
|
|
#define HAS_OEMCALLOUT ( tszCallout[0] != TEXT('\0') )
|
|
#define IS_ANALOG \
|
|
( tszHardwareId[ sizeof( ANALOG_ID_ROOT ) - 1 ] = TEXT('\0'), \
|
|
( !lstrcmpi( tszHardwareId, ANALOG_ID_ROOT ) ) )
|
|
#define IS_WIN98 (HidD_GetHidGuid)
|
|
|
|
#ifdef WINNT
|
|
if (HAS_HARDWARE_ID)
|
|
{
|
|
//Need to check if there is a VID and PID in the HW ID
|
|
if (ParseVIDPID(&DontCare, &DontCare, ptszLastSlash))
|
|
{
|
|
//If the type VIDPID doesn't match the HardwareId VIDPID
|
|
//we need to fix it
|
|
if (!lstrcmpi(ptszLastSlash,wszType))
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("OEMHW %s(%s) and/or Type %s have matching VID/PID"),
|
|
tszHardwareId,ptszLastSlash,wszType);
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hres = OLE_E_ENUM_NOMORE;
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("OEMHW %s(%s) and/or Type %s have non-matching VID/PID. Fix Needed."),
|
|
tszHardwareId,ptszLastSlash,wszType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = S_OK; //no VIDPID in the type
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("OEMHW %s(%s) and/or Type %s have no VID/PID"),
|
|
tszHardwareId,ptszLastSlash,wszType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (HAS_VIDPID)
|
|
{
|
|
hres = DIERR_MOREDATA;
|
|
}
|
|
else
|
|
{
|
|
if (HAS_OEMCALLOUT)
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
hres = OLE_E_ENUM_NOMORE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
hres = (IS_WIN98) ? S_OK /* Anything goes on 98 */
|
|
: (HAS_OEMCALLOUT) ? S_OK /* Win9x device, OK */
|
|
: (HAS_HARDWARE_ID) ? (IS_ANALOG) ? S_OK /* Analog type, OK */
|
|
: S_FALSE /* WDM device, ignore */
|
|
: S_OK; /* Analog type, OK */
|
|
#endif
|
|
|
|
switch( hres )
|
|
{
|
|
#ifdef WINNT
|
|
case DIERR_MOREDATA:
|
|
/*
|
|
* The device is not marked as autoload but has a VID/PID type
|
|
* name. If the OEMCallout is blank or "joyhid.vxd" we'll assume
|
|
* the type should be autoload and correct it.
|
|
* If there's any other value, we could assume either that we
|
|
* have a bogus Win9x driver type key and hide it or that the
|
|
* device is autoload.
|
|
* Safest route, now that our expose code is smart enough to not
|
|
* expose a device without a hardware ID, is to enumerate it as
|
|
* non-autoload as Win2k did. It won't work if you try to add
|
|
* it but at least the type will be enumerated if the device
|
|
* does show up from PnP (so nobody will get confused by a
|
|
* device without a type).
|
|
*
|
|
* ISSUE-2001/01/04-MarcAnd should use common joyhid string
|
|
* Not sure if the compiler/linker will resolve the various
|
|
* instances of L"joyhid.vxd" to a single string. Should
|
|
* reference the same one to be certain.
|
|
*/
|
|
|
|
if( !HAS_OEMCALLOUT
|
|
|| ( !lstrcmpi( tszCallout, L"joyhid.vxd" ) ) )
|
|
{
|
|
HKEY hkSet;
|
|
|
|
/*
|
|
* Need to open a new handle for the key as the one we have
|
|
* is read-only.
|
|
*/
|
|
lRc = RegOpenKeyEx( hkTypesR, szType, 0, KEY_SET_VALUE, &hkSet );
|
|
|
|
if( lRc == ERROR_SUCCESS )
|
|
{
|
|
hws.dwFlags |= JOY_HWS_AUTOLOAD;
|
|
cb = cbX(hws);
|
|
lRc = RegSetValueEx( hkSet, REGSTR_VAL_JOYOEMDATA, 0,
|
|
REG_BINARY, (PBYTE)&hws, (DWORD)( cbX(hws) ) );
|
|
|
|
if( lRc == ERROR_SUCCESS )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflTrace,
|
|
TEXT("FIXED Type %s to have JOY_HWS_AUTOLOAD"), szType );
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("Failed to set JOY_HWS_AUTOLOAD on Type %s (rc=%d,le=%d)"),
|
|
szType, lRc, GetLastError() );
|
|
}
|
|
|
|
RegCloseKey( hkSet );
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("Failed to open Type %s to fix JOY_HWS_AUTOLOAD(rc=%d,le=%d)"),
|
|
szType, lRc, GetLastError() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("Type %s with OEMCallout<%s> has no HardwareId so cannot be added"),
|
|
szType, tszCallout );
|
|
}
|
|
|
|
/*
|
|
* Whether or not we fixed this, we want to enumerate the key.
|
|
*/
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case OLE_E_ENUM_NOMORE:
|
|
{
|
|
HRESULT hres0;
|
|
hres0 = JoyCfg_FixHardwareId( hkTypesR, hk, szType , ptszLastSlash);
|
|
if( FAILED( hres0 ) )
|
|
{
|
|
/*
|
|
* Failed to fix type it must be ignored to avoid an infinite loop
|
|
*/
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("Ignoring type %s as fix failed"), szType );
|
|
hres = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflTrace,
|
|
TEXT("FIXED Type %s with HardwareId<%s> and OEMCallout<%s>"),
|
|
szType, tszHardwareId, tszCallout );
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case S_FALSE:
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("Ignoring type %s with HardwareId<%s> and OEMCallout<%s>"),
|
|
szType, tszHardwareId, tszCallout );
|
|
break;
|
|
case S_OK:
|
|
SquirtSqflPtszV(sqfl | sqflTrace,
|
|
TEXT("Enumerating type %s with HardwareId<%s> and OEMCallout<%s>"),
|
|
szType, tszHardwareId, tszCallout );
|
|
break;
|
|
}
|
|
|
|
RegCloseKey( hk );
|
|
|
|
#undef HAS_VIDPID
|
|
#undef HAS_HARDWARE_ID
|
|
#undef HAS_OEMCALLOUT
|
|
#undef IS_ANALOG
|
|
#undef IS_WIN98
|
|
|
|
}
|
|
else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("Ignoring type %s due to registry error 0x%08x"), szType, lRc );
|
|
/*
|
|
* It seems a bit bogus, to return success for an error but this
|
|
* makes sure the key is ignored and enumeration will proceed.
|
|
*/
|
|
hres = S_FALSE;
|
|
}
|
|
#ifdef WINNT
|
|
fast_out:;
|
|
#endif
|
|
|
|
ExitOleProc();
|
|
|
|
return( hres );
|
|
|
|
} /* JoyCfg_CheckTypeKey */
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIJoyCfg | SnapTypes |
|
|
*
|
|
* Snapshot the list of subkeys for OEM types.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm OUT LPWSTR * | ppwszz |
|
|
*
|
|
* Receives a pointer to a UNICODEZZ
|
|
* list of type names. Note that the returned list
|
|
* is pre-populated with the predefined types, too.
|
|
*
|
|
* We need to snapshot the names up front because
|
|
* the caller might create or delete OEM types during the
|
|
* enumeration.
|
|
*
|
|
* As we enumerate we check each key for validity and repair any
|
|
* analog custom configurations that we can.
|
|
*
|
|
* @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_OUTOFMEMORY> = <c E_OUTOFMEMORY>: Out of memory.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
CJoyCfg_SnapTypes(PJC this, LPWSTR *ppwszz)
|
|
{
|
|
HRESULT hres;
|
|
LONG lRc;
|
|
HKEY hkTypesR;
|
|
DWORD chkSub;
|
|
BOOL fRetry;
|
|
|
|
EnterProcI(CJoyCfg_SnapTypes, (_ "p", this));
|
|
|
|
RD(*ppwszz = 0);
|
|
|
|
/*
|
|
* If an analog configuration needs to be fixed, the enumeration is
|
|
* restarted because adding/removing keys may mess with the key indicies.
|
|
* Since registry keys can go stale, start from scratch.
|
|
*/
|
|
|
|
do
|
|
{
|
|
fRetry=FALSE;
|
|
|
|
/*
|
|
* Note that it is not safe to cache the registry key in
|
|
* the object. If somebody deletes the registry key, our
|
|
* cached handle goes stale and becomes useless.
|
|
*/
|
|
lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_JOYOEM, 0,
|
|
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkTypesR);
|
|
|
|
/*
|
|
* Note also that if the registry key is not available,
|
|
* we still want to return the predefined types.
|
|
*/
|
|
|
|
if(lRc == ERROR_SUCCESS)
|
|
{
|
|
lRc = RegQueryInfoKey(hkTypesR, 0, 0, 0, &chkSub,
|
|
0, 0, 0, 0, 0, 0, 0);
|
|
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
} else
|
|
{
|
|
chkSub = 0;
|
|
}
|
|
} else
|
|
{
|
|
hkTypesR = 0;
|
|
chkSub = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Each predefined name is of the form #n\0,
|
|
* which is 3 characters.
|
|
*/
|
|
hres = AllocCbPpv(cbCwch( chkSub * MAX_JOYSTRING +
|
|
(JOY_HW_PREDEFMAX - JOY_HW_PREDEFMIN)
|
|
* 3 + 1), ppwszz);
|
|
|
|
// Not really a bug,we never get to this point with a NULL ptr,
|
|
// but lets keep prefix happy Manbugs: 29340
|
|
if(SUCCEEDED(hres) && *ppwszz != NULL ){
|
|
DWORD dw;
|
|
LPWSTR pwsz;
|
|
|
|
/*
|
|
* First add the predef keys.
|
|
*/
|
|
for(dw = JOY_HW_PREDEFMIN, pwsz = *ppwszz;
|
|
dw < JOY_HW_PREDEFMAX; dw++)
|
|
{
|
|
*pwsz++ = L'#';
|
|
*pwsz++ = CJoyCfg_CharFromType(dw);
|
|
*pwsz++ = L'\0';
|
|
}
|
|
|
|
/*
|
|
* Now add the named keys.
|
|
*/
|
|
for(dw = 0; dw < chkSub; dw++)
|
|
{
|
|
#ifdef UNICODE
|
|
lRc = RegEnumKey(hkTypesR, dw, pwsz, MAX_JOYSTRING);
|
|
#else
|
|
CHAR sz[MAX_JOYSTRING];
|
|
lRc = RegEnumKey(hkTypesR, dw, sz, MAX_JOYSTRING);
|
|
#endif
|
|
if(lRc == ERROR_SUCCESS )
|
|
{
|
|
#ifdef UNICODE
|
|
hres = JoyCfg_CheckTypeKey( hkTypesR, pwsz );
|
|
#else
|
|
hres = JoyCfg_CheckTypeKey( hkTypesR, sz );
|
|
#endif
|
|
if( FAILED( hres ) )
|
|
{
|
|
/*
|
|
* Had to fix type so restart
|
|
*/
|
|
FreePpv( ppwszz );
|
|
break;
|
|
}
|
|
|
|
if( hres != S_OK )
|
|
{
|
|
/*
|
|
* Ignore this type
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
pwsz += lstrlenW(pwsz) + 1;
|
|
#else
|
|
pwsz += AToU(pwsz, MAX_JOYSTRING, sz);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
*pwsz = L'\0'; /* Make it ZZ */
|
|
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
fRetry = TRUE;
|
|
}
|
|
}
|
|
|
|
if(hkTypesR)
|
|
{
|
|
RegCloseKey(hkTypesR);
|
|
}
|
|
|
|
} while( fRetry );
|
|
|
|
ExitOleProcPpv(ppwszz);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | EnumTypes |
|
|
*
|
|
* Enumerate the joystick types currently supported by
|
|
* DirectInput. A "joystick type" describes how DirectInput
|
|
* should communicate with a joystick device. It includes
|
|
* information such as the presence and
|
|
* locations of each of the axes and the number of buttons
|
|
* supported by the device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm LPDIJOYTYPECALLBACK | lpCallback |
|
|
*
|
|
* Points to an application-defined callback function.
|
|
* For more information, see the description of the
|
|
* <f DIEnumJoyTypeProc> callback function.
|
|
*
|
|
* @parm IN LPVOID | pvRef |
|
|
*
|
|
* Specifies a 32-bit application-defined
|
|
* value to be passed to the callback function. This value
|
|
* may be any 32-bit value; it is prototyped as an <t LPVOID>
|
|
* for convenience.
|
|
*
|
|
* @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.
|
|
* Note that if the callback stops the enumeration prematurely,
|
|
* the enumeration is considered to have succeeded.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* callback procedure returned an invalid status code.
|
|
*
|
|
* @cb BOOL CALLBACK | DIEnumJoyTypeProc |
|
|
*
|
|
* An application-defined callback function that receives
|
|
* DirectInput joystick types as a result of a call to the
|
|
* <om IDirectInputJoyConfig8::EnumTypes> method.
|
|
*
|
|
* @parm IN LPCWSTR | pwszTypeName |
|
|
*
|
|
* The name of the joystick type. A buffer of <c MAX_JOYSTRING>
|
|
* characters will be sufficient to hold the type name.
|
|
* The type name should never be shown to the end user; instead,
|
|
* the "display name" should be shown. Use
|
|
* <mf IDirectInputJoyConfig8::GetTypeInfo> to obtain the
|
|
* display name of a joystick type.
|
|
*
|
|
* Type names that begin with a sharp character ("#")
|
|
* represent predefined types which cannot be modified
|
|
* or deleted.
|
|
*
|
|
* @parm IN OUT LPVOID | pvRef |
|
|
* Specifies the application-defined value given in the
|
|
* <mf IDirectInputJoyConfig8::EnumTypes> function.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns <c DIENUM_CONTINUE> to continue the enumeration
|
|
* or <c DIENUM_STOP> to stop the enumeration.
|
|
*
|
|
* @devnote
|
|
*
|
|
* EnumTypes must snapshot because people will try to get/set/delete
|
|
* during the enumeration.
|
|
*
|
|
* EnumTypes enumerates the predefined types as "#digit".
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_EnumTypes(PDJC pdjc, LPDIJOYTYPECALLBACK ptc, LPVOID pvRef)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::EnumTypes, (_ "ppx", pdjc, ptc, pvRef));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidPfn(ptc, 1)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
LPWSTR pwszKeys;
|
|
|
|
hres = CJoyCfg_SnapTypes(this, &pwszKeys);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
LPWSTR pwsz;
|
|
|
|
/*
|
|
* Prefix warns that pwszKeys could be null (mb:34685)
|
|
* Little does it know that CJoyCfg_SnapTypes can only return a
|
|
* success if the pointer is not NULL.
|
|
*/
|
|
AssertF( pwszKeys );
|
|
|
|
/*
|
|
* Surprise! Win95 implements lstrlenW.
|
|
*/
|
|
for(pwsz = pwszKeys; *pwsz; pwsz += lstrlenW(pwsz) + 1)
|
|
{
|
|
BOOL fRc;
|
|
|
|
/*
|
|
* WARNING! "goto" here! Make sure that nothing
|
|
* is held while we call the callback.
|
|
*/
|
|
fRc = Callback(ptc, pwsz, pvRef);
|
|
|
|
switch(fRc)
|
|
{
|
|
case DIENUM_STOP: goto enumdoneok;
|
|
case DIENUM_CONTINUE: break;
|
|
default:
|
|
RPF("%s: Invalid return value from callback", s_szProc);
|
|
ValidationException();
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreePpv(&pwszKeys);
|
|
hres = DIPort_SnapTypes(&pwszKeys);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
LPWSTR pwsz;
|
|
|
|
/*
|
|
* Surprise! Win95 implements lstrlenW.
|
|
*/
|
|
for(pwsz = pwszKeys; *pwsz; pwsz += lstrlenW(pwsz) + 1)
|
|
{
|
|
BOOL fRc;
|
|
|
|
/*
|
|
* WARNING! "goto" here! Make sure that nothing
|
|
* is held while we call the callback.
|
|
*/
|
|
fRc = Callback(ptc, pwsz, pvRef);
|
|
|
|
switch(fRc)
|
|
{
|
|
case DIENUM_STOP: goto enumdoneok;
|
|
case DIENUM_CONTINUE: break;
|
|
default:
|
|
RPF("%s: Invalid return value from callback", s_szProc);
|
|
ValidationException();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
enumdoneok:;
|
|
FreePpv(&pwszKeys);
|
|
hres = S_OK;
|
|
}
|
|
|
|
hres = S_OK;
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | GetTypeInfo |
|
|
*
|
|
* Obtain information about a joystick type.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm LPCWSTR | pwszTypeName |
|
|
*
|
|
* Points to the name of the type, previously obtained
|
|
* from a call to <mf IDirectInputJoyConfig8::EnumTypes>.
|
|
*
|
|
* @parm IN OUT LPDIJOYTYPEINFO | pjti |
|
|
*
|
|
* Receives information about the joystick type.
|
|
* The caller "must" initialize the <e DIJOYTYPEINFO.dwSize>
|
|
* field before calling this method.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Zero or more <c DITC_*> flags
|
|
* which specify which parts of the structure pointed
|
|
* to by <p pjti> are to be filled in.
|
|
*
|
|
* @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>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c DIERR_NOTFOUND>: The joystick type was not found.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_GetTypeInfo(PDJC pdjc, LPCWSTR pwszType,
|
|
LPDIJOYTYPEINFO pjti, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::GetTypeInfo,
|
|
(_ "pWpx", pdjc, pwszType, pjti, fl));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrW(pwszType, MAX_JOYSTRING, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb3(pjti,
|
|
DIJOYTYPEINFO_DX8,
|
|
DIJOYTYPEINFO_DX6,
|
|
DIJOYTYPEINFO_DX5, 2)) &&
|
|
SUCCEEDED( (pjti->dwSize == cbX(DIJOYTYPEINFO_DX8) )
|
|
? ( hres = hresFullValidFl(fl, DITC_GETVALID, 3) )
|
|
: (pjti->dwSize == cbX(DIJOYTYPEINFO_DX6 ) )
|
|
? ( hres = hresFullValidFl(fl, DITC_GETVALID_DX6, 3) )
|
|
: ( hres = hresFullValidFl(fl, DITC_GETVALID_DX5, 3) ) ) )
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
GUID guid;
|
|
BOOL fParseGuid;
|
|
|
|
#ifndef UNICODE
|
|
TCHAR tszType[MAX_PATH/4];
|
|
|
|
UToT( tszType, cA(tszType), pwszType );
|
|
fParseGuid = ParseGUID(&guid, tszType);
|
|
#else
|
|
fParseGuid = ParseGUID(&guid, pwszType);
|
|
#endif
|
|
|
|
if(pwszType[0] == TEXT('#'))
|
|
{
|
|
hres = JoyReg_GetPredefTypeInfo(pwszType, pjti, fl);
|
|
} else if( fParseGuid )
|
|
{
|
|
hres = DIBusDevice_GetTypeInfo(&guid, pjti, fl);
|
|
}else
|
|
{
|
|
hres = JoyReg_GetTypeInfo(pwszType, pjti, fl);
|
|
}
|
|
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | hresFullValidStructStr |
|
|
*
|
|
* Validate a string field in a struct.
|
|
*
|
|
* @parm IN LPCWSTR | pwsz |
|
|
*
|
|
* String to be validated.
|
|
*
|
|
* @parm UINT | cwch |
|
|
*
|
|
* Maximum string length.
|
|
*
|
|
* @parm LPCSTR | pszName |
|
|
*
|
|
* Field name.
|
|
*
|
|
* @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>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef XDEBUG
|
|
\
|
|
#define hresFullValidStructStr_(pwsz, cwch, pszName, z, i) \
|
|
_hresFullValidStructStr_(pwsz, cwch) \
|
|
|
|
#endif
|
|
|
|
#define hresFullValidStructStr(Struct, f, iarg) \
|
|
hresFullValidStructStr_(Struct->f, cA(Struct->f), #f, s_szProc,iarg)\
|
|
|
|
|
|
HRESULT INLINE
|
|
hresFullValidStructStr_(LPCWSTR pwsz, UINT cwch, LPCSTR pszName,
|
|
LPCSTR s_szProc, int iarg)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(SUCCEEDED(hres = hresFullValidReadStrW(pwsz, cwch, iarg)))
|
|
{
|
|
} else
|
|
{
|
|
#ifdef XDEBUG
|
|
RPF("%s: Invalid value for %s", s_szProc, pszName);
|
|
#endif
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | hresValidFlags2 |
|
|
*
|
|
* Validate the dwFlags2 value for SetTypeInfo.
|
|
*
|
|
* @parm IN DWORD | dwFlags2 |
|
|
*
|
|
* Flags to be validated.
|
|
*
|
|
* @returns
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The flags appear to be valid.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The flags are invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifdef XDEBUG
|
|
|
|
#define hresValidFlags2( flags, iarg ) hresValidFlags2_( flags, s_szProc, iarg )
|
|
|
|
HRESULT INLINE hresValidFlags2_
|
|
(
|
|
DWORD dwFlags2,
|
|
LPCSTR s_szProc,
|
|
int iarg
|
|
)
|
|
|
|
#else
|
|
|
|
#define hresValidFlags2( flags, iarg ) hresValidFlags2_( flags )
|
|
|
|
HRESULT hresValidFlags2_
|
|
(
|
|
DWORD dwFlags2
|
|
)
|
|
|
|
#endif
|
|
{
|
|
if( !( dwFlags2 & ~JOYTYPE_FLAGS2_SETVALID )
|
|
&& ( ( GET_DIDEVICE_TYPEANDSUBTYPE( dwFlags2 ) == 0 )
|
|
|| GetValidDI8DevType( dwFlags2, 0, 0 ) ) )
|
|
{
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
#ifdef XDEBUG
|
|
if( dwFlags2 & ~JOYTYPE_FLAGS2_SETVALID )
|
|
{
|
|
RPF("%s: Invalid flags 0x%04x in HIWORD(dwFlags2) of arg %d",
|
|
s_szProc, HIWORD(dwFlags2), iarg);
|
|
}
|
|
if( GET_DIDEVICE_TYPEANDSUBTYPE( dwFlags2 )
|
|
&&!GetValidDI8DevType( dwFlags2, 127, JOY_HWS_HASPOV | JOY_HWS_HASZ ) )
|
|
{
|
|
RPF("%s: Invalid type:subtype 0x%02x:%02x in dwFlags2 of arg %d",
|
|
s_szProc, GET_DIDEVICE_TYPE( dwFlags2 ),
|
|
GET_DIDEVICE_SUBTYPE( dwFlags2 ), iarg );
|
|
}
|
|
#endif
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | SetTypeInfo |
|
|
*
|
|
* Creates a new joystick type
|
|
* or redefine information about an existing joystick type.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm LPCWSTR | pwszTypeName |
|
|
*
|
|
* Points to the name of the type. The name of the type may
|
|
* not exceed MAX_JOYSTRING characters, including the terminating
|
|
* null character.
|
|
*
|
|
* If the type name does not already exist, then it is created.
|
|
*
|
|
* You cannot change the type information for a predefined type.
|
|
*
|
|
* The name may not begin with
|
|
* a "#" character. Types beginning with "#" are reserved
|
|
* by DirectInput.
|
|
*
|
|
* @parm IN LPDIJOYTYPEINFO | pjti |
|
|
*
|
|
* Contains information about the joystick type.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Zero or more <c DITC_*> flags
|
|
* which specify which parts of the structure pointed
|
|
* to by <p pjti> contain values which are to be set.
|
|
*
|
|
* @parm OUT LPWSTR | pwszVIDPIDTypeName |
|
|
* If the type name is an OEM type not in VID_xxxx&PID_yyyy format,
|
|
* pwszVIDPIDTypeName will return the name in VID_xxxx&PID_yyyy
|
|
* format that is assigned by Dinput.
|
|
* This VID_xxxx&PID_yyyy name should be used in DIJOYCONFIG.wszType
|
|
* field when calling SetConfig.
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration has not been
|
|
* acquired. You must call <mf IDirectInputJoyConfig8::Acquire>
|
|
* before you can alter joystick configuration settings.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c DIERR_READONLY>: Attempted to change a predefined type.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct _TYPENAME {
|
|
WCHAR wszRealTypeName[MAX_JOYSTRING];
|
|
WCHAR wszDITypeName[MAX_JOYSTRING/4];
|
|
} TYPENAME, *LPTYPENAME;
|
|
|
|
#ifdef WINNT
|
|
BOOL CALLBACK CJoyCfg_FindTypeProc( LPCWSTR pwszTypeName, LPVOID pv )
|
|
{
|
|
DIJOYTYPEINFO dijti;
|
|
LPTYPENAME lptype = (LPTYPENAME)pv;
|
|
|
|
ZeroMemory( &dijti, sizeof(dijti));
|
|
dijti.dwSize = sizeof(dijti);
|
|
if( pwszTypeName[0] == L'\0' || pwszTypeName[0] == L'#' )
|
|
{
|
|
return TRUE;
|
|
} else {
|
|
if( SUCCEEDED(JoyReg_GetTypeInfo(pwszTypeName, &dijti, DITC_REGHWSETTINGS | DITC_DISPLAYNAME)) )
|
|
{
|
|
if( !lstrcmpW(dijti.wszDisplayName, lptype->wszRealTypeName) ) {
|
|
lstrcpynW(lptype->wszDITypeName, pwszTypeName, sizeof(lptype->wszDITypeName)-1 );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
#endif // #ifdef WINNT
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_SetTypeInfo(PDJC pdjc, LPCWSTR pwszType,
|
|
LPCDIJOYTYPEINFO pjti, DWORD fl, LPWSTR pwszDITypeName)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::SetTypeInfo,
|
|
(_ "pWpx", pdjc, pwszType, pjti, fl));
|
|
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrW(pwszType, MAX_JOYSTRING, 1)) &&
|
|
|
|
SUCCEEDED(hres = hresFullValidReadPxCb3((PV)pjti,
|
|
DIJOYTYPEINFO_DX8,
|
|
DIJOYTYPEINFO_DX6,
|
|
DIJOYTYPEINFO_DX5, 2)) &&
|
|
#ifdef WINNT
|
|
SUCCEEDED(hres = hresFullValidFl(pjti->dwFlags1, JOYTYPE_FLAGS1_SETVALID, 3) ) &&
|
|
#endif
|
|
SUCCEEDED( (pjti->dwSize == cbX(DIJOYTYPEINFO_DX8) )
|
|
? ( hres = hresFullValidFl(fl, DITC_SETVALID, 3) )
|
|
: (pjti->dwSize == cbX(DIJOYTYPEINFO_DX6 ) )
|
|
? ( hres = hresFullValidFl(fl, DITC_SETVALID_DX6, 3) )
|
|
: ( hres = hresFullValidFl(fl, DITC_SETVALID_DX5, 3) ) ) &&
|
|
fLimpFF(fl & DITC_HARDWAREID,
|
|
SUCCEEDED(hres = hresFullValidStructStr(pjti, wszHardwareId, 2))) &&
|
|
fLimpFF(fl & DITC_DISPLAYNAME,
|
|
SUCCEEDED(hres = hresFullValidStructStr(pjti, wszDisplayName, 2))) &&
|
|
#ifndef WINNT
|
|
fLimpFF(fl & DITC_CALLOUT,
|
|
SUCCEEDED(hres = hresFullValidStructStr(pjti, wszCallout, 2))) &&
|
|
#endif
|
|
fLimpFF(fl & DITC_FLAGS2,
|
|
SUCCEEDED(hres = hresValidFlags2( pjti->dwFlags2, 2)) ) &&
|
|
fLimpFF(fl & DITC_MAPFILE,
|
|
SUCCEEDED(hres = hresFullValidStructStr(pjti, wszMapFile, 2)))
|
|
)
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))
|
|
{
|
|
switch(pwszType[0])
|
|
{
|
|
|
|
case L'\0':
|
|
RPF("%s: Invalid pwszType (null)", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
break;
|
|
|
|
case L'#':
|
|
RPF("%s: Invalid pwszType (predefined)", s_szProc);
|
|
hres = DIERR_READONLY;
|
|
break;
|
|
|
|
default:
|
|
hres = JoyReg_SetTypeInfo(this->hkTypesW, pwszType, pjti, fl);
|
|
|
|
if( SUCCEEDED(hres) ) {
|
|
#ifdef WINNT
|
|
TYPENAME type;
|
|
short DontCare;
|
|
|
|
if( (pjti->wszHardwareId[0] == TEXT('\0')) &&
|
|
!(ParseVIDPID(&DontCare, &DontCare, pwszType)) )
|
|
{
|
|
lstrcpyW(type.wszRealTypeName, pwszType);
|
|
hres = CJoyCfg_EnumTypes(pdjc, CJoyCfg_FindTypeProc, &type);
|
|
if( SUCCEEDED(hres) ) {
|
|
if( !IsBadWritePtr((LPVOID)pwszDITypeName, lstrlenW(type.wszDITypeName)) )
|
|
{
|
|
CharUpperW(type.wszDITypeName);
|
|
lstrcpyW(pwszDITypeName, type.wszDITypeName);
|
|
} else {
|
|
hres = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if( !IsBadWritePtr((LPVOID)pwszDITypeName, lstrlenW(pwszType)) )
|
|
{
|
|
lstrcpyW(pwszDITypeName, pwszType);
|
|
} else {
|
|
hres = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | DeleteType |
|
|
*
|
|
* Removes information about a joystick type.
|
|
*
|
|
* Use this method with caution; it is the caller's responsibility
|
|
* to ensure that no joystick refers to the deleted type.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm LPCWSTR | pwszTypeName |
|
|
*
|
|
* Points to the name of the type. The name of the type may
|
|
* not exceed <c MAX_PATH> characters, including the terminating
|
|
* null character.
|
|
*
|
|
* The name may not begin with
|
|
* a "#" character. Types beginning with "#" are reserved
|
|
* by DirectInput.
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration has not been
|
|
* acquired. You must call <mf IDirectInputJoyConfig8::Acquire>
|
|
* before you can alter joystick configuration settings.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_DeleteType(PDJC pdjc, LPCWSTR pwszType)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::DeleteType, (_ "pW", pdjc, pwszType));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrW( pwszType, MAX_JOYSTRING, 1)))
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))
|
|
{
|
|
LONG lRc;
|
|
switch(pwszType[0])
|
|
{
|
|
|
|
case L'\0':
|
|
RPF("%s: Invalid pwszType (null)", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
break;
|
|
|
|
case L'#':
|
|
RPF("%s: Invalid pwszType (predefined)", s_szProc);
|
|
hres = DIERR_READONLY;
|
|
break;
|
|
|
|
default:
|
|
|
|
#ifdef WINNT
|
|
#ifdef UNICODE
|
|
lRc = DIWinnt_RegDeleteKey(this->hkTypesW, (LPTSTR)pwszType);
|
|
#else
|
|
{
|
|
CHAR sz[MAX_PATH];
|
|
UToA( sz, cA(sz), pwszType );
|
|
lRc = DIWinnt_RegDeleteKey(this->hkTypesW, (LPTSTR)sz);
|
|
}
|
|
#endif
|
|
#else
|
|
#ifdef UNICODE
|
|
lRc = RegDeleteKey(this->hkTypesW, (LPTSTR)pwszType);
|
|
#else
|
|
{
|
|
CHAR sz[MAX_PATH];
|
|
UToA( sz, cA(sz), pwszType );
|
|
lRc = RegDeleteKey(this->hkTypesW, (LPTSTR)sz);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
#ifdef WINNT
|
|
lRc = DIWinnt_RegDeleteKey(this->hkTypesW, pwszType);
|
|
#else
|
|
lRc = RegDeleteKeyW(this->hkTypesW, pwszType);
|
|
#endif
|
|
*/
|
|
|
|
if(lRc == ERROR_SUCCESS)
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
if(lRc == ERROR_KEY_DELETED || lRc == ERROR_BADKEY)
|
|
{
|
|
lRc = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
hres = hresLe(lRc);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | GetConfig |
|
|
*
|
|
* Obtain information about a joystick's configuration.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm UINT | uiJoy |
|
|
*
|
|
* Joystick identification number. This is a nonnegative
|
|
* integer. To enumerate joysticks, begin with joystick
|
|
* zero and increment the joystick number by one until the
|
|
* function returns <c DIERR_NOMOREITEMS>.
|
|
*
|
|
* Yes, it's different from all other DirectX enumerations.
|
|
*
|
|
*
|
|
* @parm IN OUT LPDIJOYCONFIG | pjc |
|
|
*
|
|
* Receives information about the joystick configuration.
|
|
* The caller "must" initialize the <e DIJOYCONFIG.dwSize>
|
|
* field before calling this method.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Zero or more <c DIJC_*> flags
|
|
* which specify which parts of the structure pointed
|
|
* to by <p pjc> are to be filled in.
|
|
*
|
|
* @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 S_FALSE>: The specified joystick has not yet been
|
|
* configured.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c DIERR_NOMOREITEMS>: No more joysticks.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_GetConfig(PDJC pdjc, UINT uiJoy, LPDIJOYCONFIG pjc, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::GetConfig,
|
|
(_ "pupx", pdjc, uiJoy, pjc, fl));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb2(pjc,
|
|
DIJOYCONFIG_DX6,
|
|
DIJOYCONFIG_DX5, 2)) &&
|
|
SUCCEEDED( (pjc->dwSize == cbX(DIJOYCONFIG)
|
|
? (hres = hresFullValidFl(fl, DIJC_GETVALID, 3) )
|
|
: (hres = hresFullValidFl(fl, DIJC_GETVALID_DX5, 3)))) )
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
/*
|
|
* Note that we always get the DIJC_REGHWCONFIGTYPE because
|
|
* we need to check if the joystick type is "none".
|
|
*/
|
|
hres = JoyReg_GetConfig(uiJoy, pjc, fl | DIJC_REGHWCONFIGTYPE);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
#ifndef WINNT
|
|
static WCHAR s_wszMSGAME[] = L"MSGAME.VXD";
|
|
|
|
if(memcmp(pjc->wszCallout, s_wszMSGAME, cbX(s_wszMSGAME)) == 0)
|
|
{
|
|
; // do nothing
|
|
} else
|
|
#endif
|
|
if(fInOrder(JOY_HW_PREDEFMIN, pjc->hwc.dwType,
|
|
JOY_HW_PREDEFMAX))
|
|
{
|
|
pjc->wszType[0] = TEXT('#');
|
|
pjc->wszType[1] = CJoyCfg_CharFromType(pjc->hwc.dwType);
|
|
pjc->wszType[2] = TEXT('\0');
|
|
|
|
}
|
|
|
|
if(pjc->hwc.dwType == JOY_HW_NONE)
|
|
{
|
|
hres = S_FALSE;
|
|
} else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
/*
|
|
* In DEBUG, re-scramble the hwc and type if the caller
|
|
* didn't ask for it.
|
|
*/
|
|
if(!(fl & DIJC_REGHWCONFIGTYPE))
|
|
{
|
|
ScrambleBuf(&pjc->hwc, cbX(pjc->hwc));
|
|
ScrambleBuf(&pjc->wszType, cbX(pjc->wszType));
|
|
}
|
|
}
|
|
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
ExitBenignOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#if 0
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CJoyCfg | UpdateGlobalGain |
|
|
*
|
|
* Create the device callback so we can talk to its driver and
|
|
* tell it to change the gain value.
|
|
*
|
|
* This function must be called under the object critical section.
|
|
*
|
|
* @cwrap PJC | this
|
|
*
|
|
* @parm DWORD | idJoy |
|
|
*
|
|
* The joystick identifier.
|
|
*
|
|
* @parm DWORD | dwCplGain |
|
|
*
|
|
* New global gain.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_UpdateGlobalGain(PJC this, DWORD idJoy, DWORD dwCplGain)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcI(CJoyCfg_UpdateGlobalGain, (_ "puu", this, idJoy, dwCplGain));
|
|
|
|
AssertF(CJoyCfg_InCrit(this));
|
|
|
|
/*
|
|
* Create the deviceeffect shepherd if we don't already have it.
|
|
*/
|
|
|
|
if(this->pes && idJoy == this->idJoyCache)
|
|
{
|
|
hres = S_OK;
|
|
} else if(idJoy < cA(rgGUID_Joystick))
|
|
{
|
|
PCGUID rguid;
|
|
#ifdef DEBUG
|
|
CREATEDCB CreateDcb;
|
|
#endif
|
|
IDirectInputDeviceCallback *pdcb;
|
|
|
|
/*
|
|
* Assume the creation will work.
|
|
*/
|
|
this->idJoyCache = idJoy;
|
|
|
|
/*
|
|
* Out with the old...
|
|
*/
|
|
Invoke_Release(&this->pes);
|
|
|
|
/*
|
|
* And in with the new...
|
|
*/
|
|
rguid = &rgGUID_Joystick[idJoy];
|
|
|
|
#ifdef DEBUG
|
|
hres = hresFindInstanceGUID(rguid, &CreateDcb, 1);
|
|
AssertF(SUCCEEDED(hres));
|
|
AssertF(CreateDcb == CJoy_New);
|
|
#endif
|
|
|
|
if(SUCCEEDED(hres = CJoy_New(0, rguid,
|
|
&IID_IDirectInputDeviceCallback,
|
|
(PPV)&pdcb)))
|
|
{
|
|
hres = pdcb->lpVtbl->CreateEffect(pdcb, &this->pes);
|
|
|
|
Invoke_Release(&pdcb);
|
|
}
|
|
|
|
} else
|
|
{
|
|
hres = DIERR_DEVICENOTREG;
|
|
}
|
|
|
|
/*
|
|
* If we have an effect shepherd, then tell it what the new
|
|
* global gain is.
|
|
*/
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
AssertF(this->pes && idJoy == this->idJoyCache);
|
|
|
|
hres = this->pes->lpVtbl->SetGlobalGain(this->pes, dwCplGain);
|
|
}
|
|
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | SetConfig |
|
|
*
|
|
* Create or redefine configuration information about a joystick.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm UINT | idJoy |
|
|
*
|
|
* Zero-based joystick identification number.
|
|
*
|
|
* @parm IN LPDIJOYCONFIG | pcfg |
|
|
*
|
|
* Contains information about the joystick.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Zero or more <c DIJC_*> flags
|
|
* which specify which parts of the structure pointed
|
|
* to by <p pjc> contain information to be set.
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration has not been
|
|
* acquired. You must call <mf IDirectInputJoyConfig8::Acquire>
|
|
* before you can alter joystick configuration settings.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* @devnote
|
|
*
|
|
* This one is tricky. If the type begins with a sharp, then
|
|
* it's an internal type. And if it is null, then it's a
|
|
* custom type.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_SetConfig(PDJC pdjc, UINT idJoy, LPCDIJOYCONFIG pcfg, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::SetConfig,
|
|
(_ "pupx", pdjc, idJoy, pcfg, fl));
|
|
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidReadPxCb2(pcfg,
|
|
DIJOYCONFIG_DX6,
|
|
DIJOYCONFIG_DX5, 2)) &&
|
|
SUCCEEDED( (pcfg->dwSize == cbX(DIJOYCONFIG)
|
|
? ( hres = hresFullValidFl(fl, DIJC_SETVALID, 3) )
|
|
: ( hres = hresFullValidFl(fl, DIJC_SETVALID_DX5,3)) )) &&
|
|
fLimpFF(fl & DIJC_REGHWCONFIGTYPE,
|
|
SUCCEEDED(hres = hresFullValidStructStr(pcfg, wszType, 2))) &&
|
|
#ifndef WINNT
|
|
fLimpFF(fl & DIJC_CALLOUT,
|
|
SUCCEEDED(hres = hresFullValidStructStr(pcfg, wszCallout, 2))) &&
|
|
#endif
|
|
fLimpFF(fl & DIJC_WDMGAMEPORT,
|
|
SUCCEEDED(hres = hresFullValidGuid(&pcfg->guidGameport, 2)))
|
|
)
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))
|
|
{
|
|
JOYREGHWCONFIG jwc;
|
|
|
|
// We just ignore the DIJC_WDMGAMEPORT flag for Win9x passed by user.
|
|
// We will detect it ourself.
|
|
#ifndef WINNT
|
|
fl &= ~DIJC_WDMGAMEPORT;
|
|
#endif
|
|
|
|
|
|
if(fl & DIJC_REGHWCONFIGTYPE)
|
|
{
|
|
LPDWORD lpStart, lp;
|
|
|
|
jwc = pcfg->hwc;
|
|
|
|
/*
|
|
* Need to check whether the whole jwc is zero.
|
|
* If all are zero, we won't set it to JOY_HW_CUSTOM type.
|
|
* See manbug: 39542.
|
|
*/
|
|
for( lpStart=(LPDWORD)&jwc, lp=(LPDWORD)&jwc.dwReserved; lp >= lpStart; lp-- ) {
|
|
if( *lp ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( lp < lpStart ) {
|
|
goto _CONTINUE_SET;
|
|
}
|
|
|
|
jwc.dwUsageSettings &= ~JOY_US_ISOEM;
|
|
|
|
if(pcfg->wszType[0] == TEXT('\0'))
|
|
{
|
|
jwc.dwType = JOY_HW_CUSTOM;
|
|
} else if(pcfg->wszType[0] == TEXT('#'))
|
|
{
|
|
jwc.dwType = CJoyCfg_TypeFromChar(pcfg->wszType[1]);
|
|
if(fInOrder(JOY_HW_PREDEFMIN, jwc.dwType,
|
|
JOY_HW_PREDEFMAX) &&
|
|
pcfg->wszType[2] == TEXT('\0'))
|
|
{
|
|
/*
|
|
* If we want to use WDM for predefined devices,
|
|
* then take away the comments.
|
|
*
|
|
* fl |= DIJC_WDMGAMEPORT;
|
|
*/
|
|
} else
|
|
{
|
|
RPF("%s: Invalid predefined type \"%ls\"",
|
|
s_szProc, pcfg->wszType);
|
|
hres = E_INVALIDARG;
|
|
goto done;
|
|
}
|
|
} else
|
|
{
|
|
/*
|
|
* The precise value of jwc.dwType is not relevant.
|
|
* The Windows 95 joystick control panel sets the
|
|
* value to JOY_HW_PREDEFMAX + id, so we will too.
|
|
*/
|
|
jwc.dwUsageSettings |= JOY_US_ISOEM;
|
|
jwc.dwType = JOY_HW_PREDEFMAX + idJoy;
|
|
|
|
#ifndef WINNT
|
|
if( !(fl & DIJC_WDMGAMEPORT) ) {
|
|
HKEY hk;
|
|
|
|
hres = JoyReg_OpenTypeKey(pcfg->wszType, MAXIMUM_ALLOWED, REG_OPTION_NON_VOLATILE, &hk);
|
|
|
|
if( SUCCEEDED( hres ) ) {
|
|
hres = JoyReg_IsWdmGameport( hk );
|
|
if( SUCCEEDED(hres) ) {
|
|
fl |= DIJC_WDMGAMEPORT;
|
|
}
|
|
RegCloseKey( hk );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
_CONTINUE_SET:
|
|
|
|
#ifdef WINNT
|
|
fl |= DIJC_WDMGAMEPORT;
|
|
|
|
if(
|
|
#else
|
|
if( (fl & DIJC_WDMGAMEPORT) &&
|
|
#endif
|
|
(cbX(*pcfg) >= cbX(DIJOYCONFIG_DX6)) )
|
|
{
|
|
#ifndef WINNT
|
|
if( (pcfg->hwc.hws.dwFlags & JOY_HWS_ISANALOGPORTDRIVER) // USB joystick
|
|
&& !fVjoydDeviceNotExist ) // WDM gameport joystick and no VJOYD is used.
|
|
{
|
|
/*
|
|
* This is in Win9X, and VJOYD devices are being used.
|
|
* We don't want to add WDM device at the same time.
|
|
*/
|
|
hres = E_FAIL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
DIJOYCONFIG cfg;
|
|
GUID guidGameport = {0xcae56030, 0x684a, 0x11d0, 0xd6, 0xf6, 0x00, 0xa0, 0xc9, 0x0f, 0x57, 0xda};
|
|
|
|
if( fHasAllBitsFlFl( fl, DIJC_GUIDINSTANCE | DIJC_REGHWCONFIGTYPE | DIJC_GAIN | DIJC_WDMGAMEPORT ) )
|
|
{
|
|
memcpy( &cfg, pcfg, sizeof(DIJOYCONFIG) );
|
|
} else {
|
|
hres = JoyReg_GetConfig( idJoy, &cfg, DIJC_GUIDINSTANCE | DIJC_REGHWCONFIGTYPE | DIJC_GAIN | DIJC_WDMGAMEPORT);
|
|
|
|
if( SUCCEEDED(hres) ) {
|
|
if( fl & DIJC_GUIDINSTANCE ) {
|
|
cfg.guidInstance = pcfg->guidInstance;
|
|
}
|
|
|
|
if( fl & DIJC_GAIN ) {
|
|
cfg.dwGain = pcfg->dwGain;
|
|
}
|
|
|
|
if( fl & DIJC_REGHWCONFIGTYPE ) {
|
|
memcpy( &cfg.hwc, &pcfg->hwc, sizeof(JOYREGHWCONFIG) );
|
|
memcpy( &cfg.wszType, &pcfg->wszType, sizeof(pcfg->wszType) );
|
|
}
|
|
|
|
if( fl & DIJC_WDMGAMEPORT ) {
|
|
cfg.guidGameport = pcfg->guidGameport;
|
|
}
|
|
} else {
|
|
memcpy( &cfg, pcfg, sizeof(DIJOYCONFIG) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* use standard guidGameport if it is NULL.
|
|
*/
|
|
if( IsEqualGUID(&cfg.guidGameport, &GUID_NULL) )
|
|
{
|
|
memcpy( &cfg.guidGameport, &guidGameport, sizeof(GUID) );
|
|
}
|
|
|
|
if( IsEqualGUID(&cfg.guidInstance, &GUID_NULL) )
|
|
{
|
|
DWORD i;
|
|
DIJOYCONFIG cfg2;
|
|
|
|
hres = DIWdm_SetConfig(idJoy, &jwc, &cfg, fl );
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
// We can't set the correct id from above call, so we have to find
|
|
// which id we set and try again.
|
|
for( i=0; i<16; i++ ) {
|
|
hres = JoyReg_GetConfig( i, &cfg2, DIJC_GUIDINSTANCE | DIJC_REGHWCONFIGTYPE | DIJC_GAIN | DIJC_WDMGAMEPORT);
|
|
if( SUCCEEDED(hres) && (i != idJoy) ) {
|
|
if( lstrcmpW(cfg.wszType, cfg2.wszType) == 0 ) {
|
|
hres = DIWdm_SetJoyId(&cfg2.guidInstance, idJoy);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hres = JoyReg_SetConfig(idJoy, &jwc, &cfg, fl);
|
|
|
|
}
|
|
goto done;
|
|
} else
|
|
{
|
|
/*
|
|
* Since pcfg is not null, we set it here to avoid calling
|
|
* DIWdm_JoyHidMapping. Even if it fails, it doesn't hurt anything.
|
|
*/
|
|
hres = JoyReg_SetConfig(idJoy, &jwc, &cfg, fl);
|
|
hres = DIWdm_SetJoyId(&cfg.guidInstance, idJoy);
|
|
hres = JoyReg_SetConfig(idJoy, &jwc, &cfg, fl);
|
|
}
|
|
}
|
|
} else {
|
|
hres = JoyReg_SetConfig(idJoy, &jwc, pcfg, DIJC_UPDATEALIAS | fl);
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
#ifdef WINNT
|
|
PostMessage(HWND_BROADCAST, g_wmJoyChanged, idJoy+1, 0L);
|
|
#else
|
|
joyConfigChanged(0);
|
|
fVjoydDeviceNotExist = FALSE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
done:;
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | DeleteConfig |
|
|
*
|
|
* Delete configuration information about a joystick.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm UINT | idJoy |
|
|
*
|
|
* Zero-based joystick identification number.
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration has not been
|
|
* acquired. You must call <mf IDirectInputJoyConfig8::Acquire>
|
|
* before you can alter joystick configuration settings.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DIJOYCONFIG c_djcReset = {
|
|
cbX(c_djcReset), /* dwSize */
|
|
{ 0}, /* guidInstance */
|
|
{ 0}, /* hwc */
|
|
DI_FFNOMINALMAX, /* dwGain */
|
|
{ 0}, /* wszType */
|
|
{ 0}, /* wszCallout */
|
|
};
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_DeleteConfig(PDJC pdjc, UINT idJoy)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnterProcR(IDirectInputJoyConfig8::DeleteConfig, (_ "pu", pdjc, idJoy));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)))
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
|
|
if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))
|
|
{
|
|
|
|
HKEY hk;
|
|
TCHAR tsz[MAX_JOYSTRING];
|
|
DIJOYCONFIG dijcfg;
|
|
|
|
hres = DIWdm_DeleteConfig(idJoy);
|
|
|
|
#ifndef WINNT
|
|
if( hres == DIERR_DEVICENOTREG ) {
|
|
fVjoydDeviceNotExist = TRUE;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* To delete it, set everything to the Reset values and
|
|
* delete the configuration subkey.
|
|
*/
|
|
if( ( SUCCEEDED(hres) || hres == DIERR_DEVICENOTREG ) &&
|
|
SUCCEEDED(hres = JoyReg_SetConfig(idJoy, &c_djcReset.hwc,
|
|
&c_djcReset, DIJC_SETVALID)) &&
|
|
SUCCEEDED(hres = JoyReg_OpenConfigKey(idJoy, MAXIMUM_ALLOWED,
|
|
REG_OPTION_VOLATILE, &hk)))
|
|
{
|
|
|
|
wsprintf(tsz, TEXT("%u"), idJoy + 1);
|
|
|
|
// DIWinnt_RegDeleteKey:: name is a mismomer, the function
|
|
// recursively deletes the key and all subkeys.
|
|
DIWinnt_RegDeleteKey(hk, tsz);
|
|
|
|
RegCloseKey(hk);
|
|
|
|
#ifndef WINNT
|
|
joyConfigChanged(0);
|
|
#endif
|
|
|
|
hres = S_OK;
|
|
}
|
|
|
|
if( FAILED(hres) )
|
|
{
|
|
if( FAILED( JoyReg_GetConfig(idJoy, &dijcfg, DIJC_REGHWCONFIGTYPE | DIJC_GUIDINSTANCE) ) )
|
|
{
|
|
/* No config exists, so vacuous success on delete */
|
|
hres = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | GetUserValues |
|
|
*
|
|
* Obtain information about user settings for the joystick.
|
|
*
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm IN OUT LPDIJOYUSERVALUES | pjuv |
|
|
*
|
|
* Receives information about the user joystick configuration.
|
|
* The caller "must" initialize the <e DIJOYUSERVALUES.dwSize>
|
|
* field before calling this method.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Zero or more <c DIJU_*> flags specifying which parts
|
|
* of the <t DIJOYUSERVALUES> structure contain values
|
|
* which are to be retrieved.
|
|
*
|
|
* @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>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_GetUserValues(PDJC pdjc, LPDIJOYUSERVALUES pjuv, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::GetUserValues,
|
|
(_ "ppx", pdjc, pjuv, fl));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb(pjuv, DIJOYUSERVALUES, 2)) &&
|
|
SUCCEEDED(hres = hresFullValidFl(fl, DIJU_GETVALID, 3)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
hres = JoyReg_GetUserValues(pjuv, fl);
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | hresFullValidUVStr |
|
|
*
|
|
* Validate a string field in a <t DIJOYUSERVALUES>.
|
|
*
|
|
* @parm IN LPCWSTR | pwsz |
|
|
*
|
|
* String to be validated.
|
|
*
|
|
* @parm UINT | cwch |
|
|
*
|
|
* Maximum string length.
|
|
*
|
|
* @parm LPCSTR | pszName |
|
|
*
|
|
* Field name.
|
|
*
|
|
* @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>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef XDEBUG
|
|
|
|
#define hresFullValidUVStr_(pwsz, cwch, pszName, z, i) \
|
|
_hresFullValidUVStr_(pwsz, cwch) \
|
|
|
|
#endif
|
|
|
|
#define hresFullValidUVStr(pjuv, f, iarg) \
|
|
hresFullValidUVStr_(pjuv->f, cA(pjuv->f), #f, s_szProc,iarg)\
|
|
|
|
|
|
HRESULT INLINE
|
|
hresFullValidUVStr_(LPCWSTR pwsz, UINT cwch, LPCSTR pszName,
|
|
LPCSTR s_szProc, int iarg)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(SUCCEEDED(hres = hresFullValidReadStrW(pwsz, cwch, iarg)))
|
|
{
|
|
} else
|
|
{
|
|
#ifdef XDEBUG
|
|
RPF("%s: Invalid value for DIJOYUSERVALUES.%s", s_szProc, pszName);
|
|
#endif
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | SetUserValues |
|
|
*
|
|
* Set the user settings for the joystick.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm IN LPCDIJOYUSERVALUES | pjuv |
|
|
*
|
|
* Contains information about the new user joystick settings.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Zero or more <c DIJU_*> flags specifying which parts
|
|
* of the <t DIJOYUSERVALUES> structure contain values
|
|
* which are to be set.
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration has not been
|
|
* acquired. You must call <mf IDirectInputJoyConfig8::Acquire>
|
|
* before you can alter joystick configuration settings.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_SetUserValues(PDJC pdjc, LPCDIJOYUSERVALUES pjuv, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::SetUserValues,
|
|
(_ "pp", pdjc, pjuv, fl));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidReadPxCb(pjuv, DIJOYUSERVALUES, 2)) &&
|
|
fLimpFF(fl & DIJU_GLOBALDRIVER,
|
|
SUCCEEDED(hres = hresFullValidUVStr(pjuv,
|
|
wszGlobalDriver, 2))) &&
|
|
fLimpFF(fl & DIJU_GAMEPORTEMULATOR,
|
|
SUCCEEDED(hres = hresFullValidUVStr(pjuv,
|
|
wszGameportEmulator, 2))) &&
|
|
SUCCEEDED(hres = hresFullValidFl(fl, DIJU_SETVALID, 3)))
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
CJoyCfg_EnterCrit(this);
|
|
|
|
if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))
|
|
{
|
|
hres = JoyReg_SetUserValues(pjuv, fl);
|
|
}
|
|
|
|
CJoyCfg_LeaveCrit(this);
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | AddNewHardware |
|
|
*
|
|
* Displays the "Add New Hardware" dialog to
|
|
* guide the user through installing
|
|
* new game controller.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm HWND | hwndOwner |
|
|
*
|
|
* Window to act as owner window for UI.
|
|
*
|
|
* @parm REFGUID | rguidClass |
|
|
*
|
|
* <t GUID> which specifies the class of the hardware device
|
|
* to be added. DirectInput comes with the following
|
|
* class <t GUIDs> already defined:
|
|
*
|
|
* <c GUID_KeyboardClass>: Keyboard devices.
|
|
*
|
|
* <c GUID_MouseClass>: Mouse devices.
|
|
*
|
|
* <c GUID_MediaClass>: Media devices, including joysticks.
|
|
*
|
|
* <c GUID_HIDClass>: HID devices.
|
|
*
|
|
* @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>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c DIERR_INVALIDCLASSINSTALLER>: The "media" class installer
|
|
* could not be found or is invalid.
|
|
*
|
|
* <c DIERR_CANCELLED>: The user cancelled the operation.
|
|
*
|
|
* <c DIERR_BADINF>: The INF file for the device the user
|
|
* selected could not be found or is invalid or is damaged.
|
|
*
|
|
* <c S_FALSE>: DirectInput could not determine whether the
|
|
* operation completed successfully.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_AddNewHardware(PDJC pdjc, HWND hwnd, REFGUID rguid)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::AddNewHardware,
|
|
(_ "pxG", pdjc, hwnd, rguid));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidHwnd0(hwnd, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidGuid(rguid, 2)))
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
hres = AddNewHardware(hwnd, rguid);
|
|
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | OpenTypeKey |
|
|
*
|
|
* Open the registry key associated with a joystick type.
|
|
*
|
|
* Control panel applications can use this key to store
|
|
* per-type persistent information, such as global
|
|
* configuration parameters.
|
|
*
|
|
* Such private information should be kept in a subkey
|
|
* named "OEM"; do not store private information in the
|
|
* main type key.
|
|
*
|
|
* Control panel applications can also use this key to
|
|
* read configuration information, such as the strings
|
|
* to use for device calibration prompts.
|
|
*
|
|
* The application should use <f RegCloseKey> to close
|
|
* the registry key.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm LPCWSTR | pwszType |
|
|
*
|
|
* Points to the name of the type. The name of the type may
|
|
* not exceed <c MAX_PATH> characters, including the terminating
|
|
* null character.
|
|
*
|
|
* The name may not begin with
|
|
* a "#" character. Types beginning with "#" are reserved
|
|
* by DirectInput.
|
|
*
|
|
* @parm REGSAM | regsam |
|
|
*
|
|
* Registry security access mask. This can be any of the
|
|
* values permitted by the <f RegOpenKeyEx> function.
|
|
* If write access is requested, then joystick
|
|
* configuration must first have been acquired.
|
|
* If only read access is requested, then acquisition is
|
|
* not required.
|
|
*
|
|
* @parm PHKEY | phk |
|
|
*
|
|
* Receives the opened registry key on success.
|
|
*
|
|
* @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_NOTACQUIRED>: Joystick configuration has not been
|
|
* acquired. You must call <mf IDirectInputJoyConfig8::Acquire>
|
|
* before you can open a joystick type configuration key
|
|
* for writing.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ErrorCode)>:
|
|
* A Win32 error code if access to the key is denied by
|
|
* registry permissions or some other external factor.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_OpenTypeKey(PDJC pdjc, LPCWSTR pwszType, REGSAM sam, PHKEY phk)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::OpenTypeKey,
|
|
(_ "pWx", pdjc, pwszType, sam));
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrW(pwszType, MAX_JOYSTRING, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidPcbOut(phk, cbX(*phk), 3)))
|
|
{
|
|
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
if(pwszType[0] != TEXT('#'))
|
|
{
|
|
/*
|
|
* Attempting to get write access requires acquisition.
|
|
*/
|
|
if(fLimpFF(IsWriteSam(sam),
|
|
SUCCEEDED(hres = CJoyCfg_IsAcquired(this))))
|
|
{
|
|
hres = JoyReg_OpenTypeKey(pwszType, sam, REG_OPTION_NON_VOLATILE, phk);
|
|
}
|
|
} else
|
|
{
|
|
RPF("%s: Invalid pwszType (predefined)", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
}
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputJoyConfig8 | OpenAppStatusKey |
|
|
*
|
|
* Opens the root key of the application status registry keys.
|
|
*
|
|
* Hardware vendors can use the sub keys of this key to inspect the
|
|
* status of DirectInput applications with respect to the
|
|
* functionality they use. The key is opened with KEY_READ access.
|
|
*
|
|
* Vendors are cautioned against opening these keys directly (by
|
|
* finding the absolute path of the key rather than using this method)
|
|
* as the absolute registry path may vary on different Windows
|
|
* platforms or in future versions of DirectInput.
|
|
*
|
|
* The application should use <f RegCloseKey> to close
|
|
* the registry key.
|
|
*
|
|
* @cwrap LPDIRECTINPUTJOYCONFIG8 | LPDIRECTINPUTJOYCONFIG8
|
|
*
|
|
* @parm PHKEY | phk |
|
|
*
|
|
* Receives the opened registry key on success.
|
|
*
|
|
* @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>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c DIERR_NOTFOUND>: The key is missing on this system.
|
|
* Applications should proceed as if the key were empty.
|
|
*
|
|
* <c MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ErrorCode)>:
|
|
* A Win32 error code if access to the key is denied by
|
|
* registry permissions or some other external factor.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_OpenAppStatusKey(PDJC pdjc, PHKEY phk)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputJoyConfig8::OpenAppStatusKey,
|
|
(_ "pp", pdjc, phk));
|
|
|
|
|
|
if(SUCCEEDED(hres = hresPv(pdjc)) &&
|
|
SUCCEEDED(hres = hresFullValidPcbOut(phk, cbX(*phk), 1)))
|
|
{
|
|
PJC this = _thisPvNm(pdjc, djc);
|
|
|
|
hres = hresMumbleKeyEx( HKEY_CURRENT_USER, REGSTR_PATH_DINPUT,
|
|
KEY_READ, REG_OPTION_NON_VOLATILE, phk);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CJoyCfg_New (constructor)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CJoyCfg_New(PUNK punkOuter, RIID riid, PPV ppvObj)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcI(IDirectInputJoyConfig8::<constructor>,
|
|
(_ "p", ppvObj));
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 3)))
|
|
{
|
|
LPVOID pvTry = NULL;
|
|
hres = Common_NewRiid(CJoyCfg, punkOuter, riid, &pvTry);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
/* Must use _thisPv in case of aggregation */
|
|
PJC this = _thisPv(pvTry);
|
|
|
|
this->fCritInited = fInitializeCriticalSection(&this->crst);
|
|
if( this->fCritInited )
|
|
{
|
|
*ppvObj = pvTry;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
Common_Unhold(this);
|
|
*ppvObj = NULL;
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProcPpvR(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* The long-awaited vtbls and templates
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
#define CJoyCfg_Signature 0x6766434B /* "JCfg" */
|
|
|
|
Primary_Interface_Begin(CJoyCfg, IDirectInputJoyConfig8)
|
|
CJoyCfg_Acquire,
|
|
CJoyCfg_Unacquire,
|
|
CJoyCfg_SetCooperativeLevel,
|
|
CJoyCfg_SendNotify,
|
|
CJoyCfg_EnumTypes,
|
|
CJoyCfg_GetTypeInfo,
|
|
CJoyCfg_SetTypeInfo,
|
|
CJoyCfg_DeleteType,
|
|
CJoyCfg_GetConfig,
|
|
CJoyCfg_SetConfig,
|
|
CJoyCfg_DeleteConfig,
|
|
CJoyCfg_GetUserValues,
|
|
CJoyCfg_SetUserValues,
|
|
CJoyCfg_AddNewHardware,
|
|
CJoyCfg_OpenTypeKey,
|
|
CJoyCfg_OpenAppStatusKey,
|
|
Primary_Interface_End(CJoyCfg, IDirectInputJoyConfig8)
|
|
|