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.
2731 lines
80 KiB
2731 lines
80 KiB
/****************************************************************************
|
|
*
|
|
* Copyright (C) 2000, 2001 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Author: Tomislav Markoc, (tmarkoc), SDE
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <commctrl.h>//UI only
|
|
#include <shfusion.h>//UI only
|
|
#define _INC_MMSYSTEM
|
|
#define WINMMAPI DECLSPEC_IMPORT
|
|
typedef UINT MMRESULT; /* error return code, 0 means no error */
|
|
/* call as if(err=xxxx(...)) Error(err); else */
|
|
// end of hack to avoid including mmsystem.h!!!
|
|
#include <gameport.h>
|
|
#include <dinput.h>
|
|
#include <dinputd.h>
|
|
#include <list>
|
|
#include <exception>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <tchar.h>
|
|
#include <windowsx.h>
|
|
#include <regstr.h>
|
|
#include <new.h>
|
|
#include "resource.h"
|
|
#include "ifacesvr.h"
|
|
#include "joyarray.h"
|
|
|
|
using namespace std;
|
|
|
|
#define NUMJOYDEVS 16//Max joy id-s. Why is this hardcoded?
|
|
#define MAX_DEVICES 75
|
|
#define IDC_WHATSTHIS 400
|
|
|
|
#ifdef UNICODE
|
|
#define String wstring
|
|
#else
|
|
#define String string
|
|
#endif // !UNICODE
|
|
|
|
#ifdef _CHECKED
|
|
#define JOY_EXCEPTION(A) JoyException(_T(__FILE__),__LINE__,A)
|
|
#else
|
|
#define JOY_EXCEPTION(A) JoyException(A)
|
|
#endif
|
|
|
|
#define cA(a) (sizeof(a)/sizeof(a[0]))
|
|
|
|
class JoyException:public exception
|
|
{
|
|
HRESULT m_hRes;
|
|
#ifdef _CHECKED
|
|
String m_SourceFile;
|
|
DWORD m_dwLine;
|
|
#endif
|
|
public:
|
|
HRESULT GetResult(){return m_hRes;};
|
|
JoyException(
|
|
#ifdef _CHECKED
|
|
LPCTSTR lpSourceFile,DWORD dwLine,
|
|
#endif
|
|
HRESULT hRes);
|
|
};
|
|
|
|
JoyException::JoyException(
|
|
#ifdef _CHECKED
|
|
LPCTSTR lpSourceFile,DWORD dwLine,
|
|
#endif
|
|
HRESULT hRes)
|
|
{
|
|
m_hRes=hRes;
|
|
#ifdef _CHECKED
|
|
m_SourceFile=lpSourceFile;
|
|
m_dwLine=dwLine;
|
|
#endif
|
|
}
|
|
|
|
int __cdecl my_new_handler(size_t) {
|
|
throw JOY_EXCEPTION(E_OUTOFMEMORY);
|
|
return 0;
|
|
}
|
|
|
|
template <class p> class AutoRelease
|
|
{
|
|
p m_p;
|
|
public:
|
|
AutoRelease(){m_p=NULL;};
|
|
~AutoRelease(){Clear();};
|
|
void Clear(){
|
|
ULONG nRef=-1;
|
|
if(m_p)
|
|
{
|
|
nRef=m_p->Release();//If last ptr, nRef falls to 0.
|
|
};
|
|
m_p=NULL;};
|
|
|
|
AutoRelease(const AutoRelease<p> &R)
|
|
{m_p=R.m_p;m_p->AddRef();};
|
|
AutoRelease<p> &operator=(const AutoRelease<p> &R)
|
|
{m_p=R.m_p;m_p->AddRef();return *this;};
|
|
AutoRelease<p> &operator=(p ptr)
|
|
{m_p=ptr;if(m_p)m_p->AddRef();return *this;};
|
|
p operator->(){return m_p;};
|
|
operator p&(){return m_p;};
|
|
operator p*(){return &m_p;};
|
|
};
|
|
typedef AutoRelease<LPDIRECTINPUT8> LPDIRECTINPUT_AR;
|
|
typedef AutoRelease<LPDIRECTINPUTDEVICE8> LPDIRECTINPUTDEVICE_AR;
|
|
typedef AutoRelease<LPDIRECTINPUTJOYCONFIG8> LPDIRECTINPUTJOYCONFIG_AR;
|
|
|
|
template <class p> class AutoDeleteArray
|
|
{
|
|
p m_p;
|
|
public:
|
|
AutoDeleteArray(){m_p=NULL;};
|
|
AutoDeleteArray(const p P){m_p=P;};
|
|
~AutoDeleteArray(){delete[] m_p;};
|
|
p operator=(const p P){m_p=P;return m_p;};
|
|
p operator->(){return m_p;};
|
|
operator p&(){return m_p;};
|
|
operator p*(){return &m_p;};
|
|
};
|
|
|
|
enum EStatus{EConnected,ENotConnected,EUnknown};
|
|
|
|
//ISSUE-2001/03/29-timgill Should use predefined NULLGUID
|
|
const GUID NULLGUID;
|
|
|
|
class CCore;
|
|
class CDIDev
|
|
{
|
|
friend BOOL CALLBACK DIEnumDevicesProc(
|
|
const DIDEVICEINSTANCE * lpddi,LPVOID pvRef);
|
|
|
|
LPDIRECTINPUTDEVICE_AR m_pDID;
|
|
DIDEVICEINSTANCE m_DIDevInst;
|
|
DIDEVCAPS_DX3 m_DIDevCaps;
|
|
DIJOYCONFIG m_DIJoyCfg;
|
|
DWORD m_dwId;
|
|
|
|
bool m_bInitialized;
|
|
CCore *m_pCore;
|
|
public:
|
|
CDIDev(){m_bInitialized=false;};
|
|
DWORD Id(){return m_dwId;};
|
|
const GUID &InstGUID(){return m_DIDevInst.guidInstance;};
|
|
const GUID &PortGUID(){return m_DIJoyCfg.guidGameport;};
|
|
LPCTSTR InstName(){return m_DIDevInst.tszInstanceName;};
|
|
EStatus Status(){if(m_DIDevCaps.dwFlags&DIDC_ATTACHED)return EConnected;return ENotConnected;};
|
|
void Update(LPDIRECTINPUTJOYCONFIG8 pJoyCfg);
|
|
HRESULT Rename(LPCTSTR pName);
|
|
bool operator==(const GUID &G){if(InstGUID()==G)return true;return false;};
|
|
};
|
|
|
|
class CGprtDev
|
|
{
|
|
friend BOOL CALLBACK DIEnumJoyTypePr(LPCWSTR pwszTypeName,LPVOID pvRef);
|
|
friend class CCore;
|
|
String m_Name;
|
|
DIJOYTYPEINFO m_Info;
|
|
public:
|
|
LPCTSTR TypeName(){return m_Name.data();};
|
|
LPCTSTR Name(){return m_Info.wszDisplayName;};
|
|
bool operator==(LPCTSTR pTypeName){if(m_Name==pTypeName)return true;return false;};
|
|
bool Rudder(){if(m_Info.hws.dwFlags&JOY_HWS_HASR)return true;return false;};
|
|
};
|
|
|
|
typedef list<CDIDev> LISTDIDEV;
|
|
typedef list<String> LISTSTRING;
|
|
typedef list<CGprtDev> LISTGPRTDEV;
|
|
|
|
class CCore
|
|
{
|
|
friend HRESULT Properties(HMODULE hMod,HWND hWnd,CCore *pCore,DWORD dwId);
|
|
friend BOOL CALLBACK DIEnumJoyTypePr(LPCWSTR pwszTypeName,LPVOID pvRef);
|
|
friend class CDIDev;
|
|
friend BOOL CALLBACK DIEnumDevicesProc(const DIDEVICEINSTANCE *lpddi,LPVOID pvRef);
|
|
|
|
bool m_bAccess;
|
|
bool m_bInitialized;
|
|
|
|
virtual void UIUpdate(){};
|
|
LPDIRECTINPUT_AR m_pDI;
|
|
LPDIRECTINPUTJOYCONFIG_AR m_pDIJoyCfg;
|
|
|
|
int GetNextAvailableId();
|
|
bool FullJoyOemAccess();
|
|
public:
|
|
LISTDIDEV m_ListDIDev;
|
|
LISTSTRING m_GprtDrv;
|
|
LISTGPRTDEV m_GprtBus;
|
|
LISTGPRTDEV m_GprtDev;
|
|
|
|
CCore();
|
|
void Initialize(HWND hWnd);
|
|
void Update();
|
|
void UpdateType();
|
|
bool Access(){return m_bAccess;};
|
|
CDIDev *FindDIDev(GUID &G);
|
|
HRESULT Remove(GUID &G);
|
|
void Preferred(GUID &G);
|
|
HRESULT AddDevice
|
|
(LPCTSTR pTypeName,bool bRudder,LPCTSTR pGprtId,GUID &GOccupied);
|
|
bool IsAutoDetectGprt();
|
|
bool IsAvailableVIDPID(String &VIDPIDName);
|
|
bool DuplicateDeviceName(LPCTSTR pName);
|
|
void AddCustomDevice(bool bJoy,bool bPad,bool bYoke,bool bCar,
|
|
int nAxes,bool bZAxis,int nButtons,bool bHasPov,LPCTSTR pName,
|
|
LPCTSTR pVIDPIDName);
|
|
bool IsCustomDevice(LPCTSTR pTypeName);
|
|
bool IsDeviceActive(LPCTSTR pTypeName);
|
|
void DeleteType(LPCTSTR pTypeName);
|
|
CGprtDev *FindGprtDev(LPCTSTR pTypeName);
|
|
};
|
|
|
|
struct SEnumDev
|
|
{
|
|
LPDIRECTINPUT8 m_pDI;
|
|
CCore *m_pCore;
|
|
SEnumDev(LISTDIDEV &ListDIDev,LPDIRECTINPUT8 pDI,CCore *pCore)
|
|
{m_pDI=pDI;m_pCore=pCore;};
|
|
};
|
|
|
|
/******************************************************************************
|
|
End of header
|
|
******************************************************************************/
|
|
|
|
|
|
/******************************************************************************
|
|
CDIDev
|
|
******************************************************************************/
|
|
|
|
HRESULT CDIDev::Rename(LPCTSTR pName)
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
if(!m_pCore->Access())return DIERR_INSUFFICIENTPRIVS;
|
|
|
|
DIPROPSTRING DIPropString;
|
|
ZeroMemory(&DIPropString,sizeof(DIPROPSTRING));
|
|
DIPropString.diph.dwSize=sizeof(DIPROPSTRING);
|
|
DIPropString.diph.dwHeaderSize=sizeof(DIPROPHEADER);
|
|
DIPropString.diph.dwHow=DIPH_DEVICE;
|
|
wcsncpy(DIPropString.wsz,pName,MAX_PATH-1);
|
|
DIPropString.wsz[MAX_PATH-1]=0;
|
|
HRESULT hRes=m_pDID->SetProperty(DIPROP_INSTANCENAME,&DIPropString.diph);
|
|
Update(m_pCore->m_pDIJoyCfg);
|
|
return hRes;
|
|
}
|
|
|
|
void CDIDev::Update(LPDIRECTINPUTJOYCONFIG8 pJoyCfg)
|
|
{
|
|
HRESULT hRes=S_OK;
|
|
|
|
ZeroMemory(&m_DIDevInst,sizeof(m_DIDevInst));
|
|
m_DIDevInst.dwSize=sizeof(m_DIDevInst);
|
|
hRes=m_pDID->GetDeviceInfo(&m_DIDevInst);
|
|
if(FAILED(hRes))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
|
|
ZeroMemory(&m_DIDevCaps,sizeof(m_DIDevCaps));
|
|
m_DIDevCaps.dwSize=sizeof(m_DIDevCaps);
|
|
hRes=m_pDID->GetCapabilities((LPDIDEVCAPS)&m_DIDevCaps);
|
|
if(FAILED(hRes))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
|
|
//Get Id.
|
|
m_dwId=-1;
|
|
DIPROPDWORD DIPropDW;
|
|
ZeroMemory(&DIPropDW,sizeof(DIPropDW));
|
|
DIPropDW.diph.dwSize=sizeof(DIPROPDWORD);
|
|
DIPropDW.diph.dwHeaderSize=sizeof(DIPROPHEADER);
|
|
DIPropDW.diph.dwHow=DIPH_DEVICE;
|
|
hRes=m_pDID->GetProperty(DIPROP_JOYSTICKID,&DIPropDW.diph);
|
|
if(FAILED(hRes))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
m_dwId=DIPropDW.dwData;
|
|
|
|
//Get gameport.
|
|
ZeroMemory(&m_DIJoyCfg,sizeof(m_DIJoyCfg));
|
|
m_DIJoyCfg.dwSize=sizeof(m_DIJoyCfg);
|
|
hRes=pJoyCfg->GetConfig(m_dwId,&m_DIJoyCfg,DIJC_WDMGAMEPORT);
|
|
if(FAILED(hRes))
|
|
{
|
|
//throw JOY_EXCEPTION(hRes);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
CCore
|
|
******************************************************************************/
|
|
|
|
BOOL CALLBACK DIEnumDevicesProc(
|
|
const DIDEVICEINSTANCE *lpddi,LPVOID pvRef)
|
|
{
|
|
try
|
|
{
|
|
SEnumDev &ED=*(SEnumDev*)pvRef;
|
|
|
|
CDIDev Dev;
|
|
Dev.m_pCore=ED.m_pCore;
|
|
HRESULT hRes=ED.m_pDI->CreateDevice(lpddi->guidInstance,Dev.m_pDID,NULL);
|
|
if(FAILED(hRes))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
Dev.Update(ED.m_pCore->m_pDIJoyCfg);
|
|
Dev.m_bInitialized=true;
|
|
ED.m_pCore->m_ListDIDev.push_back(Dev);
|
|
}
|
|
catch(JoyException E)
|
|
{
|
|
}
|
|
catch(exception)
|
|
{
|
|
}
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
BOOL CALLBACK DIEnumJoyTypePr(LPCWSTR pwszTypeName,LPVOID pvRef)
|
|
{
|
|
try
|
|
{
|
|
HRESULT hRes=S_OK;
|
|
|
|
String TN=pwszTypeName;
|
|
|
|
SEnumDev &ED=*(SEnumDev*)pvRef;
|
|
|
|
DIJOYTYPEINFO JoyInfo;
|
|
ZeroMemory(&JoyInfo,sizeof(JoyInfo));
|
|
JoyInfo.dwSize=sizeof(JoyInfo);
|
|
|
|
switch(ED.m_pCore->m_pDIJoyCfg->GetTypeInfo(pwszTypeName,&JoyInfo,DITC_REGHWSETTINGS))
|
|
{
|
|
//Errors to continue with.
|
|
case DIERR_NOTFOUND:
|
|
return DIENUM_CONTINUE;
|
|
//Errors to stop with.
|
|
case DIERR_INVALIDPARAM:
|
|
case DIERR_NOMOREITEMS:
|
|
return DIENUM_STOP;
|
|
}
|
|
|
|
if(JoyInfo.hws.dwFlags&JOY_HWS_ISGAMEPORTBUS)
|
|
{
|
|
CGprtDev D;
|
|
D.m_Name=TN;
|
|
|
|
ZeroMemory(&D.m_Info,sizeof(D.m_Info));
|
|
D.m_Info.dwSize=sizeof(D.m_Info);
|
|
DWORD dwFlags=DITC_CLSIDCONFIG|DITC_DISPLAYNAME;
|
|
if(FAILED(ED.m_pCore->m_pDIJoyCfg->GetTypeInfo(D.m_Name.data(),&D.m_Info,dwFlags)))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
ED.m_pCore->m_GprtBus.push_back(D);
|
|
}
|
|
else if(!(JoyInfo.hws.dwFlags&JOY_HWS_AUTOLOAD))
|
|
{
|
|
CGprtDev D;
|
|
D.m_Name=TN;
|
|
|
|
ZeroMemory(&D.m_Info,sizeof(D.m_Info));
|
|
D.m_Info.dwSize=sizeof(D.m_Info);
|
|
DWORD dwFlags=DITC_REGHWSETTINGS|DITC_FLAGS1|DITC_HARDWAREID|DITC_CALLOUT|DITC_DISPLAYNAME;
|
|
if(FAILED(ED.m_pCore->m_pDIJoyCfg->GetTypeInfo(D.m_Name.data(),&D.m_Info,dwFlags)))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
ED.m_pCore->m_GprtDev.push_back(D);
|
|
}
|
|
}
|
|
catch(JoyException E)
|
|
{
|
|
}
|
|
catch(exception)
|
|
{
|
|
}
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
CCore::CCore()
|
|
{
|
|
m_bAccess=false;
|
|
m_bInitialized=false;
|
|
}
|
|
|
|
bool CCore::IsAutoDetectGprt()
|
|
{
|
|
if(!m_GprtDev.size())return false;
|
|
if(m_GprtDev.front().m_Info.dwFlags1&
|
|
JOYTYPE_NOAUTODETECTGAMEPORT)return false;
|
|
return true;
|
|
}
|
|
|
|
int CCore::GetNextAvailableId()
|
|
{
|
|
if(!m_bInitialized)return -1;
|
|
DIJOYCONFIG JoyCfg;
|
|
ZeroMemory(&JoyCfg,sizeof(JoyCfg));
|
|
JoyCfg.dwSize=sizeof(JoyCfg);
|
|
|
|
for(int i=0;i<NUMJOYDEVS;i++)
|
|
{
|
|
switch(m_pDIJoyCfg->GetConfig(i,&JoyCfg,DIJC_REGHWCONFIGTYPE))
|
|
{
|
|
case S_FALSE:
|
|
case DIERR_NOMOREITEMS:
|
|
case DIERR_NOTFOUND:
|
|
case E_FAIL:
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool CCore::FullJoyOemAccess()
|
|
{
|
|
LONG lRc;
|
|
HKEY hk;
|
|
bool bRc;
|
|
|
|
lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_JOYOEM,
|
|
0,
|
|
KEY_ALL_ACCESS & ~WRITE_DAC & ~WRITE_OWNER,
|
|
&hk);
|
|
|
|
if( lRc == ERROR_SUCCESS ) {
|
|
bRc = true;
|
|
RegCloseKey(hk);
|
|
} else {
|
|
bRc = false;
|
|
}
|
|
|
|
return bRc;
|
|
}
|
|
|
|
HRESULT CCore::AddDevice(LPCTSTR pTypeName,bool bRudder,LPCTSTR pGprtId,GUID &GOccupied)
|
|
{
|
|
HRESULT hRes=S_OK;
|
|
GOccupied=NULLGUID;
|
|
|
|
if(!m_bInitialized)return E_FAIL;
|
|
|
|
if(m_GprtDev.size()>=MAX_DEVICES)return E_FAIL;
|
|
|
|
LISTGPRTDEV::iterator It;
|
|
It=find(m_GprtDev.begin(),m_GprtDev.end(),pTypeName);
|
|
if(It==m_GprtDev.end())return E_FAIL;
|
|
CGprtDev *pGprtDev=&*It;
|
|
|
|
int nId=GetNextAvailableId();
|
|
if(nId==-1)return DIERR_NOTFOUND;
|
|
|
|
DIJOYCONFIG JoyCfg;
|
|
ZeroMemory(&JoyCfg,sizeof(JoyCfg));
|
|
JoyCfg.dwSize=sizeof(JoyCfg);
|
|
JoyCfg.hwc.hws=pGprtDev->m_Info.hws;
|
|
JoyCfg.hwc.hws.dwFlags|=JOY_HWS_ISANALOGPORTDRIVER;
|
|
if(bRudder)
|
|
{
|
|
JoyCfg.hwc.hws.dwFlags|=JOY_HWS_HASR;
|
|
JoyCfg.hwc.dwUsageSettings|=JOY_US_HASRUDDER;
|
|
}
|
|
JoyCfg.hwc.dwUsageSettings|=JOY_US_PRESENT;
|
|
//JoyCfg.hwc.dwType=nArrayID;WHY is this beeing set to index?????????????????????????????????????????????????????????????????????????????????
|
|
wcsncpy(JoyCfg.wszCallout,pGprtDev->m_Info.wszCallout,sizeof(JoyCfg.wszCallout)/sizeof(JoyCfg.wszCallout[0])-1);
|
|
wcsncpy(JoyCfg.wszType,pGprtDev->m_Name.data(),sizeof(JoyCfg.wszType)/sizeof(JoyCfg.wszType[0])-1);
|
|
|
|
if(SUCCEEDED(hRes=m_pDIJoyCfg->Acquire()))
|
|
{
|
|
if(m_GprtBus.size())
|
|
{
|
|
if(m_GprtBus.size()>1)
|
|
{
|
|
if(pGprtId)
|
|
{
|
|
String GId=pGprtId;
|
|
LISTGPRTDEV::iterator It;
|
|
It=find(m_GprtBus.begin(),m_GprtBus.end(),GId.data());
|
|
if(It!=m_GprtDev.end())
|
|
{
|
|
JoyCfg.guidGameport=It->m_Info.clsidConfig;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JoyCfg.guidGameport=m_GprtBus.front().m_Info.clsidConfig;
|
|
}
|
|
}
|
|
|
|
if(FAILED(hRes=m_pDIJoyCfg->SetConfig(nId,&JoyCfg,DIJC_REGHWCONFIGTYPE|DIJC_CALLOUT)))
|
|
{
|
|
m_pDIJoyCfg->Unacquire();
|
|
if(hRes==E_ACCESSDENIED)
|
|
GOccupied=JoyCfg.guidGameport;
|
|
return hRes;
|
|
}
|
|
else
|
|
{
|
|
//Fix #55524.
|
|
if(SUCCEEDED(m_pDIJoyCfg->GetConfig(nId,&JoyCfg,DIJC_REGHWCONFIGTYPE)))
|
|
{
|
|
if(!(JoyCfg.hwc.dwUsageSettings&JOY_US_PRESENT))
|
|
{
|
|
JoyCfg.hwc.dwUsageSettings|=JOY_US_PRESENT;
|
|
JoyCfg.hwc.hwv.dwCalFlags|=0x80000000;
|
|
JoyCfg.hwc.hws.dwFlags|=JOY_HWS_ISANALOGPORTDRIVER;
|
|
m_pDIJoyCfg->SetConfig(nId,&JoyCfg,DIJC_REGHWCONFIGTYPE);
|
|
}
|
|
}
|
|
//End of fix #55524.
|
|
}
|
|
m_pDIJoyCfg->Unacquire();
|
|
}
|
|
Update();
|
|
UpdateType();
|
|
return hRes;
|
|
}
|
|
|
|
void CCore::Initialize(HWND hWnd)
|
|
{
|
|
if((LPDIRECTINPUTJOYCONFIG8)m_pDIJoyCfg)
|
|
m_pDIJoyCfg->Release();
|
|
m_pDIJoyCfg=NULL;
|
|
|
|
if((LPDIRECTINPUT8)m_pDI)
|
|
m_pDI->Release();
|
|
m_pDI=NULL;
|
|
|
|
HMODULE hM=GetModuleHandle(NULL);
|
|
if(!hM)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
HRESULT hRes=DirectInput8Create(hM,DIRECTINPUT_VERSION,
|
|
IID_IDirectInput8,(LPVOID*)&m_pDI,NULL);
|
|
if(FAILED(hRes))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
hRes=m_pDI->QueryInterface(IID_IDirectInputJoyConfig8,(LPVOID*)&m_pDIJoyCfg);
|
|
if(hRes!=DI_OK)
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
hRes=m_pDIJoyCfg->SetCooperativeLevel(hWnd,DISCL_EXCLUSIVE|DISCL_BACKGROUND);
|
|
if(hRes!=DI_OK)
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
m_bAccess = FullJoyOemAccess();
|
|
m_bInitialized=true;
|
|
}
|
|
|
|
void CCore::Update()
|
|
{
|
|
HRESULT hRes;
|
|
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
m_ListDIDev.clear();
|
|
|
|
SEnumDev ED(m_ListDIDev,m_pDI,this);
|
|
hRes=m_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL,DIEnumDevicesProc,&ED,DIEDFL_ALLDEVICES);
|
|
if(FAILED(hRes))
|
|
{
|
|
//EnumDevices goes wrong
|
|
; //throw JOY_EXCEPTION(hRes);
|
|
}
|
|
|
|
UIUpdate();
|
|
}
|
|
|
|
void CCore::UpdateType()
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
m_GprtDrv.clear();
|
|
m_GprtBus.clear();
|
|
m_GprtDev.clear();
|
|
|
|
HRESULT hRes=S_OK;
|
|
|
|
SEnumDev ED(m_ListDIDev,m_pDI,this);
|
|
hRes=m_pDIJoyCfg->EnumTypes(DIEnumJoyTypePr,&ED);
|
|
if(FAILED(hRes))
|
|
{
|
|
throw JOY_EXCEPTION(hRes);
|
|
}
|
|
|
|
UIUpdate();
|
|
}
|
|
|
|
CDIDev *CCore::FindDIDev(GUID &Guid)
|
|
{
|
|
LISTDIDEV::iterator It;
|
|
It=find(m_ListDIDev.begin(),m_ListDIDev.end(),Guid);
|
|
if(It==m_ListDIDev.end())return NULL;
|
|
return &*It;
|
|
}
|
|
|
|
HRESULT CCore::Remove(GUID &Guid)
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
CDIDev *pDev=FindDIDev(Guid);
|
|
if(!pDev)return E_FAIL;
|
|
|
|
HRESULT hRes;
|
|
|
|
if(FAILED(hRes=m_pDIJoyCfg->Acquire()))return hRes;
|
|
if(FAILED(hRes=m_pDIJoyCfg->DeleteConfig(pDev->Id())))
|
|
{
|
|
m_pDIJoyCfg->Unacquire();
|
|
return hRes;
|
|
}
|
|
m_pDIJoyCfg->SendNotify();
|
|
m_pDIJoyCfg->Unacquire();
|
|
return S_OK;
|
|
}
|
|
|
|
#define DIJC_ALL DIJC_REGHWCONFIGTYPE|DIJC_CALLOUT|DIJC_WDMGAMEPORT|DIJC_GAIN|DIJC_GUIDINSTANCE
|
|
|
|
void CCore::Preferred(GUID &G)
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
//Set Id of preferred device to 0.
|
|
|
|
//Find Id of device to be set to 0.
|
|
CDIDev *pDev=FindDIDev(G);
|
|
if(!pDev)return;
|
|
int nId=pDev->Id();
|
|
if(nId==0)//Already preferred.
|
|
return;
|
|
|
|
if(SUCCEEDED(m_pDIJoyCfg->Acquire()))
|
|
{
|
|
//We could call SetConfig only once and DInput on NT or Whistler should
|
|
//swap Id-s of two devices. However, it will not work if gameport device
|
|
//is unplugged, so we still must swap by salling SetConfig twice.
|
|
|
|
DIJOYCONFIG OldId0;
|
|
ZeroMemory(&OldId0,sizeof(OldId0));
|
|
OldId0.dwSize=sizeof(OldId0);
|
|
bool bOldId0=true;
|
|
HRESULT hRes = m_pDIJoyCfg->GetConfig(0,&OldId0,DIJC_ALL);
|
|
if(hRes==DIERR_NOTFOUND||hRes==S_FALSE)
|
|
bOldId0=false;
|
|
|
|
DIJOYCONFIG NewId0;
|
|
ZeroMemory(&NewId0,sizeof(NewId0));
|
|
NewId0.dwSize=sizeof(NewId0);
|
|
bool bNewId0=true;
|
|
hRes=m_pDIJoyCfg->GetConfig(nId,&NewId0,DIJC_ALL);
|
|
if(hRes==DIERR_NOTFOUND||hRes==S_FALSE)
|
|
bNewId0=false;
|
|
|
|
if(bOldId0)
|
|
m_pDIJoyCfg->SetConfig(nId,&OldId0,DIJC_ALL);
|
|
else
|
|
//We must still delete because GetConfig could fail for other
|
|
//reasons than device with Id 0 not present.
|
|
m_pDIJoyCfg->DeleteConfig(0);
|
|
|
|
if(bNewId0)
|
|
m_pDIJoyCfg->SetConfig(0,&NewId0,DIJC_ALL);
|
|
|
|
m_pDIJoyCfg->SendNotify();
|
|
m_pDIJoyCfg->Unacquire();
|
|
}
|
|
Update();
|
|
}
|
|
|
|
//Partialy copied from old joy.cpl.
|
|
//I strongly suspect this is not documented anywhere.
|
|
bool CCore::IsAvailableVIDPID(String &VIDPIDName)
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
HRESULT hRes=m_pDIJoyCfg->Acquire();
|
|
if(FAILED(hRes))throw JOY_EXCEPTION(hRes);
|
|
|
|
//Make the VID/PID to compare from the following formula:
|
|
//VID_045e&PID_100+JOY_HW_LASTENTRY to 100+JOY_HW_LASTENTRY+0xf
|
|
|
|
TCHAR Type[18];
|
|
_tcsncpy(Type,_T("VID_045E&PID_0100"),18);
|
|
Type[17] = 0;
|
|
|
|
const WCHAR Lookup[]=_T("0123456789ABCDEF");
|
|
|
|
int i=JOY_HW_LASTENTRY;
|
|
do
|
|
{
|
|
if(i<0x10)
|
|
{
|
|
Type[16]=Lookup[i];
|
|
}
|
|
else
|
|
{
|
|
Type[15]=Lookup[1];
|
|
Type[16]=Lookup[i%0x10];
|
|
}
|
|
i++;
|
|
|
|
HKEY hKey;
|
|
if(FAILED(m_pDIJoyCfg->OpenTypeKey(Type,KEY_READ,&hKey)))
|
|
break;
|
|
RegCloseKey(hKey);
|
|
}
|
|
while(i<(JOY_HW_LASTENTRY+0x11));
|
|
|
|
m_pDIJoyCfg->Unacquire();
|
|
if(i<0x1d)
|
|
{
|
|
VIDPIDName=Type;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CGprtDev *CCore::FindGprtDev(LPCTSTR pTypeName)
|
|
{
|
|
if(!pTypeName)return NULL;
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
for(LISTGPRTDEV::iterator It=m_GprtDev.begin();
|
|
It!=m_GprtDev.end();It++)
|
|
{
|
|
if(It->m_Name==pTypeName)
|
|
return &(*It);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool CCore::IsCustomDevice(LPCTSTR pTypeName)
|
|
{
|
|
if(!pTypeName)return false;
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
if(pTypeName[0]==_T('#'))return false;//Standard type.
|
|
CGprtDev *pDevType=FindGprtDev(pTypeName);
|
|
if(!pDevType)
|
|
//This should never happend, but just in case.
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
if(!pDevType->m_Info.wszHardwareId[0])
|
|
{
|
|
if(!pDevType->m_Info.wszCallout[0])
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
TCHAR AnalogRoot[]=_T("gameport\\vid_045e&pid_01");
|
|
//Test if it is predefined custom. Do not delete.
|
|
TCHAR C=pDevType->m_Info.wszHardwareId[(sizeof(AnalogRoot)/
|
|
sizeof(AnalogRoot[0]))-1];
|
|
if((C==_T('f'))||(C==_T('F')))
|
|
return false;
|
|
//Now test if it is custom.
|
|
if(!_tcsnicmp(pDevType->m_Info.wszHardwareId,AnalogRoot,
|
|
(sizeof(AnalogRoot)/sizeof(AnalogRoot[0]))-1))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CCore::DeleteType(LPCTSTR pTypeName)
|
|
{
|
|
if(!pTypeName)return;
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
HRESULT hRes=m_pDIJoyCfg->Acquire();
|
|
if(FAILED(hRes))throw JOY_EXCEPTION(hRes);
|
|
m_pDIJoyCfg->DeleteType(pTypeName);
|
|
m_pDIJoyCfg->Unacquire();
|
|
UpdateType();
|
|
}
|
|
|
|
bool CCore::IsDeviceActive(LPCTSTR pTypeName)
|
|
{
|
|
if(!pTypeName)return false;
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
for(LISTDIDEV::iterator It=m_ListDIDev.begin();
|
|
It!=m_ListDIDev.end();It++)
|
|
{
|
|
DIJOYCONFIG JoyCfg;
|
|
ZeroMemory(&JoyCfg,sizeof(JoyCfg));
|
|
JoyCfg.dwSize=sizeof(JoyCfg);
|
|
if(SUCCEEDED(m_pDIJoyCfg->GetConfig(It->Id(),&JoyCfg,
|
|
DIJC_REGHWCONFIGTYPE)))
|
|
{
|
|
if(!_tcscmp(JoyCfg.wszType,pTypeName))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CCore::DuplicateDeviceName(LPCTSTR pName)
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
for(LISTGPRTDEV::iterator It=m_GprtDev.begin();
|
|
It!=m_GprtDev.end();It++)
|
|
{
|
|
if(!_tcsncmp(pName,It->Name(),
|
|
(sizeof(It->m_Info.wszDisplayName)/
|
|
sizeof(It->m_Info.wszDisplayName[0]))-1))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CCore::AddCustomDevice(bool bJoy,bool bPad,bool bYoke,bool bCar,
|
|
int nAxes,bool bZAxis,int nButtons,bool bHasPov,LPCTSTR pName,
|
|
LPCTSTR pVIDPIDName)
|
|
{
|
|
if(!m_bInitialized)
|
|
{
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
String VIDPIDName=_T("GamePort\\");
|
|
VIDPIDName+=pVIDPIDName;
|
|
|
|
HRESULT hRes=m_pDIJoyCfg->Acquire();
|
|
if(FAILED(hRes))throw JOY_EXCEPTION(hRes);
|
|
|
|
DIJOYTYPEINFO JTI;
|
|
ZeroMemory(&JTI,sizeof(JTI));
|
|
JTI.dwSize=sizeof(JTI);
|
|
int nCh=sizeof(JTI.wszDisplayName)/
|
|
sizeof(JTI.wszDisplayName[0]);
|
|
_tcsncpy(JTI.wszDisplayName,pName,nCh);
|
|
JTI.wszDisplayName[nCh-1]=0;
|
|
JTI.hws.dwNumButtons=nButtons;
|
|
if(nAxes==3)
|
|
{
|
|
if(bZAxis)
|
|
JTI.hws.dwFlags|=JOY_HWS_HASZ;
|
|
else
|
|
JTI.hws.dwFlags|=JOY_HWS_HASR;
|
|
}
|
|
else if(nAxes==4)
|
|
{
|
|
JTI.hws.dwFlags|=JOY_HWS_HASR|JOY_HWS_HASZ;
|
|
}
|
|
if(bHasPov)
|
|
JTI.hws.dwFlags|=JOY_HWS_HASPOV|JOY_HWS_POVISBUTTONCOMBOS;
|
|
if(!bJoy)
|
|
{
|
|
if(bPad)
|
|
{
|
|
JTI.hws.dwFlags|=JOY_HWS_ISGAMEPAD;
|
|
}
|
|
else if(bCar)
|
|
{
|
|
JTI.hws.dwFlags|=JOY_HWS_ISCARCTRL;
|
|
}
|
|
else
|
|
{
|
|
JTI.hws.dwFlags|=JOY_HWS_ISYOKE;
|
|
}
|
|
}
|
|
nCh=sizeof(JTI.wszHardwareId)/
|
|
sizeof(JTI.wszHardwareId[0]);
|
|
_tcsncpy(JTI.wszHardwareId,VIDPIDName.data(),nCh);
|
|
JTI.wszDisplayName[nCh-1]=0;
|
|
|
|
hRes=m_pDIJoyCfg->SetTypeInfo(pVIDPIDName,&JTI,
|
|
DITC_DISPLAYNAME|DITC_CLSIDCONFIG|
|
|
DITC_REGHWSETTINGS|DITC_HARDWAREID,NULL);
|
|
m_pDIJoyCfg->Unacquire();
|
|
if(FAILED(hRes))throw JOY_EXCEPTION(hRes);
|
|
|
|
UpdateType();
|
|
}
|
|
|
|
/******************************************************************************
|
|
UI
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
UI header
|
|
******************************************************************************/
|
|
|
|
class CDlgProcHandler//this is not in CDlg because we want to reuse for property sheets...
|
|
{
|
|
protected:
|
|
HWND m_hWnd;
|
|
HMODULE m_hModule;
|
|
|
|
static INT_PTR CALLBACK DialogProc
|
|
(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
virtual INT_PTR DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
virtual BOOL InitDialog(HWND hFocus,LPARAM lParam){return TRUE;};
|
|
virtual BOOL Timer(WPARAM wTimerID){return FALSE;};
|
|
virtual INT_PTR Command(WORD wNotifyCode,WORD wID,HWND hwndCtl);
|
|
virtual INT_PTR Notify(int idCtrl,LPNMHDR pnmh){return 0;};
|
|
HWND HDlgItem(int nIDDlgItem){return GetDlgItem(m_hWnd,nIDDlgItem);};
|
|
public:
|
|
CDlgProcHandler(){m_hWnd=NULL;m_hModule=NULL;};
|
|
};
|
|
|
|
class CDlg:public CDlgProcHandler
|
|
{
|
|
protected:
|
|
virtual INT_PTR DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
virtual INT_PTR Command(WORD wNotifyCode,WORD wID,HWND hwndCtl);
|
|
public:
|
|
int Dlg(WORD wID,HMODULE hModule,HWND hParent);
|
|
};
|
|
|
|
int CDlg::Dlg(WORD wID,HMODULE hModule,HWND hParent)
|
|
{
|
|
m_hModule=hModule;
|
|
return DialogBoxParam(hModule,MAKEINTRESOURCE(wID),hParent,
|
|
CDlgProcHandler::DialogProc,(LPARAM)this);
|
|
}
|
|
|
|
typedef list<GUID> LISTGUID;
|
|
class CMainDlg;
|
|
class CPreferredDlg:public CDlg
|
|
{
|
|
LISTGUID m_ListCtrl;
|
|
bool m_bBlockUpdate;
|
|
CCore *m_pCore;
|
|
CMainDlg *m_pMainDlg;
|
|
|
|
virtual BOOL InitDialog(HWND hFocus,LPARAM lParam);
|
|
virtual INT_PTR Command(WORD wNotifyCode,WORD wID,HWND hwndCtl);
|
|
void Preferred();
|
|
INT_PTR Notify(int idCtrl,LPNMHDR pnmh);
|
|
public:
|
|
CPreferredDlg(CMainDlg *pMainDlg,CCore *pCore)
|
|
{m_pMainDlg=pMainDlg;m_pCore=pCore;m_bBlockUpdate=false;};
|
|
void Update();
|
|
};
|
|
|
|
class CAddDlg:public CDlg
|
|
{
|
|
CCore *m_pCore;
|
|
// CMainDlg *m_pMainDlg;
|
|
LISTSTRING m_ListCtrl;
|
|
LISTSTRING m_GprtListCtrl;
|
|
bool m_bBlockUpdate;
|
|
|
|
BOOL InitDialog(HWND hFocus,LPARAM lParam);
|
|
INT_PTR Command(WORD wNotifyCode,WORD wID,HWND hwndCtl);
|
|
void AddDev();
|
|
INT_PTR DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
public:
|
|
CAddDlg(/*CMainDlg *pMainDlg,*/CCore *pCore)
|
|
{/*m_pMainDlg=pMainDlg;*/m_pCore=pCore;m_bBlockUpdate=false;};
|
|
void Update();
|
|
};
|
|
|
|
class CCustomDlg:public CDlg
|
|
{
|
|
CCore *m_pCore;
|
|
String m_VIDPIDName;
|
|
BOOL InitDialog(HWND hFocus,LPARAM lParam);
|
|
INT_PTR Command(WORD wNotifyCode,WORD wID,HWND hwndCtl);
|
|
public:
|
|
CCustomDlg(CCore *pCore){m_pCore=pCore;};
|
|
LPCTSTR GetVIDPIDName(){return m_VIDPIDName.data();};
|
|
};
|
|
|
|
class CMainDlg:public CDlg
|
|
{
|
|
LISTGUID m_ListCtrl;
|
|
bool m_bBlockUpdate;
|
|
CCore *m_pCore;
|
|
CPreferredDlg *m_pPrefDlg;
|
|
CAddDlg *m_pAddDlg;
|
|
bool m_bEditingName;
|
|
|
|
virtual BOOL InitDialog(HWND hFocus,LPARAM lParam);
|
|
virtual BOOL Timer(WPARAM wTimerID);
|
|
virtual INT_PTR Command(WORD wNotifyCode,WORD wID,HWND hwndCtl);
|
|
INT_PTR DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
INT_PTR Notify(int idCtrl,LPNMHDR pnmh);
|
|
void Remove();
|
|
void Prop();
|
|
protected:
|
|
CMainDlg(){m_pCore=NULL;m_bBlockUpdate=false;m_pPrefDlg=NULL;m_pAddDlg=NULL;m_bEditingName=false;};
|
|
void ConnectUI(CCore *pCore){m_pCore=pCore;};
|
|
void Update();
|
|
void CoreUpdate();
|
|
};
|
|
|
|
class CUpdate
|
|
{
|
|
bool *m_pbBlockUpdate;
|
|
public:
|
|
CUpdate(bool *pbBlockUpdate)
|
|
{m_pbBlockUpdate=pbBlockUpdate;*m_pbBlockUpdate=true;};
|
|
~CUpdate(){*m_pbBlockUpdate=false;};
|
|
};
|
|
|
|
/******************************************************************************
|
|
End of UI header
|
|
******************************************************************************/
|
|
|
|
#define DEVICE_COLUMN 0
|
|
#define STATUS_COLUMN 1
|
|
|
|
LPTSTR Insert1String(LPCTSTR pS,LPCTSTR pI)
|
|
{
|
|
LPTSTR pR=new TCHAR[_tcslen(pS)+_tcslen(pI)+1];
|
|
wsprintf(pR,pS,pI);
|
|
return pR;
|
|
}
|
|
|
|
LPTSTR Insert2Strings(LPCTSTR pS,LPCTSTR pI1,LPCTSTR pI2)
|
|
{
|
|
LPTSTR pR=new TCHAR[_tcslen(pS)+_tcslen(pI1)+_tcslen(pI2)+1];
|
|
wsprintf(pR,pS,pI1,pI2);
|
|
return pR;
|
|
}
|
|
|
|
void MessageBox(HWND hWnd,HINSTANCE hInstance,UINT uTitleID,UINT uMsgID)
|
|
{
|
|
TCHAR Title[128];
|
|
TCHAR Msg[256];
|
|
|
|
LoadString(hInstance,uTitleID, Title, cA(Title));
|
|
LoadString(hInstance,uMsgID, Msg, cA(Msg));
|
|
UINT uRTL = (GetWindowLongPtr(hWnd,GWL_EXSTYLE) & WS_EX_LAYOUTRTL) ? MB_RTLREADING : 0;
|
|
MessageBox(hWnd,Msg,Title,MB_ICONHAND|MB_OK|MB_APPLMODAL|uRTL);
|
|
}
|
|
|
|
void LVSetItem(HWND hCtrl,int nItem,int nSubItem, LPCTSTR lpStr)
|
|
{
|
|
LVITEM Item;
|
|
ZeroMemory(&Item,sizeof(Item));
|
|
Item.mask=LVIF_TEXT;
|
|
Item.iItem=nItem;
|
|
Item.iSubItem=nSubItem;
|
|
Item.cchTextMax=lstrlen(lpStr);
|
|
Item.pszText=(LPTSTR)lpStr;
|
|
|
|
SendMessage(hCtrl,LVM_SETITEM,0,(LPARAM)(const LPLVITEM)&Item);
|
|
}
|
|
|
|
void LVInsertItem(HWND hCtrl,int nItem,int nSubItem,LPCTSTR lpStr,LPARAM lData)
|
|
{
|
|
LVITEM Item;
|
|
ZeroMemory(&Item,sizeof(Item));
|
|
Item.mask=LVIF_TEXT|LVIF_PARAM;
|
|
Item.iItem=nItem;
|
|
Item.cchTextMax=lstrlen(lpStr);
|
|
Item.pszText=(LPTSTR)lpStr;
|
|
Item.lParam=lData;
|
|
|
|
SendMessage(hCtrl,LVM_INSERTITEM,0,(LPARAM)(const LPLVITEM)&Item);
|
|
}
|
|
|
|
void *LVGetItemDataPtr(HWND hCtrl,int nItem)
|
|
{
|
|
LVITEM Item;
|
|
ZeroMemory(&Item,sizeof(LVITEM));
|
|
Item.mask=LVIF_PARAM;
|
|
Item.iItem=nItem;
|
|
if(SendMessage(hCtrl,LVM_GETITEM,0,(LPARAM)(LPLVITEM)&Item))
|
|
return(void*)Item.lParam;
|
|
return NULL;
|
|
}
|
|
|
|
const GUID &LVGetItemGUID(HWND hCtrl,int nItem)
|
|
{
|
|
if(nItem<0)return NULLGUID;
|
|
GUID *pG=(GUID*)LVGetItemDataPtr(hCtrl,nItem);
|
|
if(pG)return *pG;
|
|
return NULLGUID;
|
|
}
|
|
|
|
int LVFindGUIDIndex(HWND hCtrl,GUID &G)
|
|
{
|
|
int nCnt=ListView_GetItemCount(hCtrl);
|
|
for(int i=0;i<nCnt;i++)
|
|
if(G==LVGetItemGUID(hCtrl,i))return i;
|
|
return -1;
|
|
}
|
|
|
|
int LVGetSel(HWND hCtrl)
|
|
{
|
|
return ListView_GetNextItem(hCtrl,-1,LVNI_SELECTED);
|
|
}
|
|
|
|
void LVSetSel(HWND hCtrl,int nItem,bool bSel=true)
|
|
{
|
|
if(bSel)
|
|
ListView_SetItemState(hCtrl,nItem,
|
|
LVIS_FOCUSED|LVIS_SELECTED,0x000F)
|
|
else
|
|
ListView_SetItemState(hCtrl,nItem,
|
|
0,0x000F);
|
|
}
|
|
|
|
void LVInsertColumn (HWND hCtrl,int nColumn,UINT uID,int nWidth,HINSTANCE hInstance)
|
|
{
|
|
LVCOLUMN Col;
|
|
ZeroMemory(&Col,sizeof(Col));
|
|
Col.mask=LVCF_FMT|LVCF_TEXT|LVCF_WIDTH;
|
|
Col.fmt=LVCFMT_CENTER;
|
|
Col.cx=nWidth;
|
|
|
|
TCHAR S[128];
|
|
LoadString(hInstance,uID, S, cA(S));
|
|
|
|
Col.pszText=(LPTSTR)S;
|
|
SendMessage(hCtrl,LVM_INSERTCOLUMN,(WPARAM)(int)nColumn,(LPARAM)(const LPLVCOLUMN)&Col);
|
|
}
|
|
|
|
INT_PTR CDlgProcHandler::DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return InitDialog((HWND)wParam,lParam);
|
|
case WM_TIMER:
|
|
return Timer(wParam);
|
|
case WM_COMMAND:
|
|
return Command(HIWORD(wParam),LOWORD(wParam),(HWND)lParam);
|
|
case WM_NOTIFY:
|
|
return Notify((int)wParam,(LPNMHDR)lParam);
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
TCHAR HelpFileName[128];
|
|
LoadString(m_hModule,IDS_HELPFILENAME, HelpFileName, cA(HelpFileName));
|
|
WinHelp((HWND)wParam,HelpFileName,HELP_CONTEXTMENU,(ULONG_PTR)gaHelpIDs);
|
|
}
|
|
//Undocumented in msdn but otherwise
|
|
//problem rightclicking title to close.
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
INT_PTR CDlgProcHandler::Command(WORD wNotifyCode,WORD wID,HWND hwndCtl)
|
|
{
|
|
switch(wID)
|
|
{
|
|
case IDC_WHATSTHIS:
|
|
{
|
|
TCHAR HelpFileName[128];
|
|
LoadString(m_hModule,IDS_HELPFILENAME, HelpFileName, cA(HelpFileName));
|
|
WinHelp(hwndCtl,HelpFileName,HELP_WM_HELP,(ULONG_PTR)gaHelpIDs);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
INT_PTR CALLBACK CDlgProcHandler::DialogProc
|
|
(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
CDlgProcHandler* pH=NULL;
|
|
|
|
try
|
|
{
|
|
if(uMsg==WM_INITDIALOG)
|
|
{
|
|
SetLastError(0);
|
|
LONG lRet=SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
|
|
if(GetLastError()&&!lRet) {
|
|
EndDialog(hwndDlg,E_FAIL);
|
|
}
|
|
}
|
|
|
|
pH=(CDlgProcHandler*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
|
|
if(pH && !IsBadReadPtr(pH, sizeof(CDlgProcHandler)))
|
|
{
|
|
if(uMsg==WM_INITDIALOG) {
|
|
pH->m_hWnd=hwndDlg;
|
|
}
|
|
|
|
if( pH->m_hWnd == hwndDlg ) {
|
|
return pH->DialogProc(uMsg,wParam,lParam);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
catch(JoyException E)
|
|
{
|
|
if(pH)
|
|
{
|
|
if(uMsg==WM_INITDIALOG) {
|
|
EndDialog(pH->m_hWnd,IDCANCEL);
|
|
}
|
|
}
|
|
}
|
|
|
|
catch(...)
|
|
{
|
|
if(pH && !IsBadReadPtr(pH, sizeof(CDlgProcHandler))) {
|
|
EndDialog(pH->m_hWnd, IDCANCEL);
|
|
}
|
|
//should report error here, and keep going.
|
|
}
|
|
|
|
if(uMsg==WM_INITDIALOG) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
CDlg
|
|
******************************************************************************/
|
|
|
|
INT_PTR CDlg::Command(WORD wNotifyCode,WORD wID,HWND hwndCtl)
|
|
{
|
|
switch(wID)
|
|
{
|
|
case IDOK:
|
|
if(!(wNotifyCode&~1))
|
|
EndDialog(m_hWnd,IDOK);
|
|
return 0;
|
|
case IDCANCEL:
|
|
if(!(wNotifyCode&~1))
|
|
EndDialog(m_hWnd,IDCANCEL);
|
|
return 0;
|
|
}
|
|
return CDlgProcHandler::Command(wNotifyCode,wID,hwndCtl);
|
|
};
|
|
|
|
INT_PTR CDlg::DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_CLOSE:
|
|
EndDialog(m_hWnd,0);
|
|
return 0;
|
|
}
|
|
return CDlgProcHandler::DialogProc(uMsg,wParam,lParam);
|
|
}
|
|
|
|
/******************************************************************************
|
|
Main dialog CMainDlg
|
|
******************************************************************************/
|
|
|
|
void CMainDlg::Remove()
|
|
{
|
|
{
|
|
if(!m_pCore->Access())
|
|
{
|
|
MessageBox(m_hWnd,m_hModule,IDS_USER_MODE_TITLE,IDS_USER_MODE);
|
|
return;
|
|
}
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(hListCtrl)
|
|
{
|
|
int nSelDev=LVGetSel(hListCtrl);
|
|
if(nSelDev<0)return;
|
|
GUID G=LVGetItemGUID(hListCtrl,nSelDev);
|
|
CDIDev *pDev=m_pCore->FindDIDev(G);
|
|
|
|
if(!pDev) {
|
|
return;
|
|
}
|
|
|
|
TCHAR AreSure[256];
|
|
LoadString(m_hModule,IDS_GEN_AREYOUSURE, AreSure, cA(AreSure));
|
|
LPTSTR pT = Insert1String(AreSure,pDev->InstName());
|
|
lstrcpy(AreSure, pT);
|
|
delete[] pT;
|
|
|
|
TCHAR Title[128];
|
|
LoadString(m_hModule,IDS_GEN_AREYOUSURE_TITLE, Title, cA(Title));
|
|
|
|
UINT uRTL = (GetWindowLongPtr(m_hWnd,GWL_EXSTYLE) & WS_EX_LAYOUTRTL) ? MB_RTLREADING : 0;
|
|
if(IDYES!=MessageBox(m_hWnd,AreSure,Title,MB_ICONQUESTION|MB_YESNO|MB_APPLMODAL|uRTL)) {
|
|
return;
|
|
}
|
|
if(m_pCore->Remove(G)==DIERR_UNSUPPORTED) {
|
|
MessageBox(m_hWnd,m_hModule,IDS_GEN_AREYOUSURE_TITLE,IDS_GEN_NO_REMOVE_USB);
|
|
}
|
|
}
|
|
}
|
|
CoreUpdate();
|
|
}
|
|
|
|
void OnHelp(LPHELPINFO pHelpInfo,HINSTANCE hInstance)
|
|
{
|
|
TCHAR FileName[256];
|
|
LoadString(hInstance,IDS_HELPFILENAME, FileName, cA(FileName));
|
|
if(pHelpInfo->iContextType==HELPINFO_WINDOW)
|
|
WinHelp((HWND)pHelpInfo->hItemHandle,FileName,HELP_WM_HELP,(ULONG_PTR)gaHelpIDs);
|
|
}
|
|
|
|
INT_PTR CMainDlg::DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_ACTIVATEAPP:
|
|
CoreUpdate();
|
|
return 0;
|
|
/* case WM_POWERBROADCAST:
|
|
switch( wParam )
|
|
{
|
|
return 0;
|
|
case PBT_APMSUSPEND:
|
|
// Suspend operation!
|
|
KillTimer(hDlg, ID_MYTIMER);
|
|
break;
|
|
|
|
case PBT_APMRESUMESUSPEND:
|
|
case PBT_APMRESUMECRITICAL:
|
|
// Resume operation!
|
|
SetActive(hDlg);
|
|
break;
|
|
}
|
|
break;return 0;*/
|
|
case WM_DEVICECHANGE:
|
|
CoreUpdate();
|
|
return 0;
|
|
case WM_HELP:
|
|
OnHelp((LPHELPINFO)lParam,m_hModule);
|
|
return 0;
|
|
/* nFlags &= ~ON_PAGE;
|
|
KillTimer(hDlg, ID_MYTIMER);
|
|
OnContextMenu(wParam, lParam);
|
|
nFlags |= ON_PAGE;
|
|
SetTimer(hDlg, ID_MYTIMER, POLLRATE, 0);
|
|
return(1); return 0;???
|
|
*/
|
|
case WM_SYSCOLORCHANGE:
|
|
{
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(hListCtrl)
|
|
{
|
|
SendMessage(hListCtrl,WM_SYSCOLORCHANGE,0,0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
return CDlg::DialogProc(uMsg,wParam,lParam);
|
|
}
|
|
|
|
void CMainDlg::Prop()
|
|
{
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(hListCtrl)
|
|
{
|
|
int nSelDev=LVGetSel(hListCtrl);
|
|
if(nSelDev<0)return;
|
|
GUID G=LVGetItemGUID(hListCtrl,nSelDev);
|
|
CDIDev *pDev=m_pCore->FindDIDev(G);
|
|
//ISSUE-2001/03/29-timgill internal error;SHOULD ASSERT HERE
|
|
if(!pDev)return;
|
|
//need to kill the timer before launching property sheet - see Whistler bug 260145 for details
|
|
KillTimer(m_hWnd,1);
|
|
switch(Properties(m_hModule,m_hWnd,m_pCore,pDev->Id()))
|
|
{
|
|
case E_NOINTERFACE:
|
|
MessageBox(m_hWnd,m_hModule,IDS_INTERNAL_ERROR,IDS_NO_DIJOYCONFIG);
|
|
break;
|
|
default://Not handled for now or ever?
|
|
break;
|
|
};
|
|
//now update and re-set the timer
|
|
m_pCore->Update();
|
|
SetTimer(m_hWnd,1,5000,NULL);
|
|
}
|
|
}
|
|
|
|
INT_PTR CMainDlg::Command(WORD wNotifyCode,WORD wID,HWND hwndCtl)
|
|
{
|
|
switch(wID)
|
|
{
|
|
case IDC_BTN_ADV:
|
|
if(!m_pCore->Access()) {
|
|
MessageBox(m_hWnd,m_hModule,IDS_USER_MODE_TITLE,IDS_USER_MODE);
|
|
} else {
|
|
if(!m_pPrefDlg)
|
|
{
|
|
CPreferredDlg PrefDlg(this,m_pCore);
|
|
m_pPrefDlg=&PrefDlg;
|
|
PrefDlg.Dlg(IDD_ADV_CHANGE,m_hModule,m_hWnd);
|
|
m_pPrefDlg=NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
case IDC_BTN_REMOVE:
|
|
Remove();
|
|
return 0;
|
|
case IDC_BTN_ADD:
|
|
if(!m_pCore->Access()) {
|
|
MessageBox(m_hWnd,m_hModule,IDS_USER_MODE_TITLE,IDS_USER_MODE);
|
|
} else {
|
|
if(!m_pAddDlg)
|
|
{
|
|
CAddDlg AddDlg(m_pCore);
|
|
m_pAddDlg=&AddDlg;
|
|
AddDlg.Dlg(IDD_ADD,m_hModule,m_hWnd);
|
|
m_pAddDlg=NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
case IDC_BTN_TSHOOT:
|
|
{
|
|
TCHAR ExeBuff[MAX_PATH];
|
|
|
|
if( GetWindowsDirectory(ExeBuff,MAX_PATH) ) {
|
|
TCHAR CmdBuff[256];
|
|
LoadString(m_hModule,IDS_TSHOOT_CMD, CmdBuff, cA(CmdBuff));
|
|
|
|
STARTUPINFO Si;
|
|
PROCESS_INFORMATION Pi;
|
|
ZeroMemory(&Si,sizeof(Si));
|
|
ZeroMemory(&Pi,sizeof(Pi));
|
|
Si.cb=sizeof(Si);
|
|
// ISSUE-2000/12/20-MarcAnd Quick Fix to use HSS
|
|
// In other places where HSS is used, STARTF_FORCEONFEEDBACK is not set
|
|
// Changed IDS_TSHOOT_CMD from: "hh.exe joy.chm"
|
|
// to "explorer.exe hcp://help/tshoot/tsInputDev.htm"
|
|
// Need to make this OS specific to allow backprop to Win2k (or further)
|
|
Si.dwFlags=STARTF_USESHOWWINDOW|STARTF_FORCEONFEEDBACK;
|
|
Si.wShowWindow=SW_NORMAL;
|
|
|
|
ExeBuff[MAX_PATH-1]=0;
|
|
String Exe=ExeBuff;
|
|
String Cmd=CmdBuff;
|
|
if(Exe[Exe.size()-1]!=_T('\\'))
|
|
{
|
|
Exe+=_T('\\');
|
|
}
|
|
Exe+=_T("explorer.exe");
|
|
Cmd=_T("\"")+Exe+_T("\"")+_T(" ")+Cmd;
|
|
|
|
if(CreateProcess(Exe.data(),(LPTSTR)Cmd.data(),0,0,0,0,0,0,&Si,&Pi))
|
|
{
|
|
CloseHandle(Pi.hThread);
|
|
CloseHandle(Pi.hProcess);
|
|
}
|
|
} else {
|
|
// something is wrong when calling GetWindowsDirectory
|
|
;
|
|
}
|
|
}
|
|
return 0;
|
|
case IDC_BTN_PROPERTIES:
|
|
Prop();
|
|
return 0;
|
|
}
|
|
return CDlg::Command(wNotifyCode,wID,hwndCtl);
|
|
};
|
|
|
|
INT_PTR CMainDlg::Notify(int idCtrl,LPNMHDR pnmh)
|
|
{
|
|
switch(pnmh->code )
|
|
{
|
|
/* Keeping this just in case someone changes his/her mind soon.
|
|
case LVN_BEGINLABELEDIT:
|
|
{
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(!hListCtrl)return TRUE;
|
|
if(!m_pCore->Access())return TRUE;
|
|
PostMessage((HWND)::SendMessage(hListCtrl,LVM_GETEDITCONTROL,0,0),EM_SETLIMITTEXT,MAX_PATH-1,0);
|
|
m_bEditingName=true;
|
|
return(FALSE);
|
|
}
|
|
case LVN_ENDLABELEDIT:
|
|
{
|
|
m_bEditingName=false;
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(!hListCtrl)
|
|
{
|
|
CoreUpdate();
|
|
return FALSE;
|
|
}
|
|
|
|
HWND hCtrl=(HWND)SendMessage(hListCtrl,LVM_GETEDITCONTROL,0,0);
|
|
if(hCtrl)
|
|
{
|
|
if(SendMessage(hCtrl,EM_GETMODIFY,0,0))
|
|
{
|
|
int nLen=lstrlen(((NMLVDISPINFO*)pnmh)->item.pszText);
|
|
if((nLen>(MAX_PATH-1))||(nLen==0))
|
|
MessageBeep(MB_ICONHAND);
|
|
//Make sure the name is usable.
|
|
else if(_tcschr(((NMLVDISPINFO*)pnmh)->item.pszText,TEXT('\\')))
|
|
MessageBox(m_hWnd,m_hModule,IDS_INVALID_NAME_TITLE,IDS_INVALID_NAME);
|
|
else
|
|
{
|
|
int nSelDev=LVGetSel(hListCtrl);
|
|
GUID SelGUID=LVGetItemGUID(hListCtrl,nSelDev);
|
|
CDIDev *pSelDev=m_pCore->FindDIDev(SelGUID);
|
|
|
|
if(SUCCEEDED(pSelDev->Rename(((NMLVDISPINFO *)pnmh)->item.pszText)))
|
|
{
|
|
CoreUpdate();
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_RENAME_TITLE,IDS_NO_RENAME);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CoreUpdate();
|
|
return FALSE;
|
|
}*/
|
|
case LVN_KEYDOWN:
|
|
switch(((LV_KEYDOWN*)pnmh)->wVKey)
|
|
{
|
|
case VK_DELETE:
|
|
Remove();
|
|
return 0;
|
|
|
|
case VK_F5:
|
|
CoreUpdate();
|
|
return 0;
|
|
}
|
|
return 0;
|
|
case LVN_ITEMCHANGED:
|
|
if(!(((LPNMLISTVIEW)pnmh)->uOldState&LVIS_SELECTED)&&
|
|
(((LPNMLISTVIEW)pnmh)->uNewState&LVIS_SELECTED)&&
|
|
(((LPNMLISTVIEW)pnmh)->uChanged&LVIF_STATE))
|
|
Update();
|
|
return 0;
|
|
case NM_DBLCLK:
|
|
switch(idCtrl)
|
|
{
|
|
case IDC_LIST_DEVICE:
|
|
Prop();
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CMainDlg::CoreUpdate()
|
|
{
|
|
m_pCore->Update();
|
|
//KillTimer so if UI is updated for some other reason than WM_TIMER timer will be reset.
|
|
//make sure nothing can fail between KillTimer and SetTimer.
|
|
KillTimer(m_hWnd,1);
|
|
SetTimer(m_hWnd,1,5000,NULL);
|
|
}
|
|
|
|
BOOL CMainDlg::InitDialog(HWND hFocus,LPARAM lParam)
|
|
{
|
|
SetTimer(m_hWnd,1,5000,NULL);
|
|
m_pCore->Initialize(m_hWnd);
|
|
//#wi315410. we need to decide...
|
|
// m_pCore->UpdateType();
|
|
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(hListCtrl)
|
|
{
|
|
SendMessage(hListCtrl,LVM_SETEXTENDEDLISTVIEWSTYLE,0,LVS_EX_FULLROWSELECT);
|
|
RECT R;
|
|
GetClientRect(hListCtrl,&R);
|
|
int nWidth=(R.right>>2)*3;
|
|
LVInsertColumn(hListCtrl,DEVICE_COLUMN,IDS_GEN_DEVICE_HEADING,nWidth,m_hModule);
|
|
LVInsertColumn(hListCtrl,STATUS_COLUMN,IDS_GEN_STATUS_HEADING,R.right-nWidth,m_hModule);
|
|
}
|
|
CoreUpdate();
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CMainDlg::Timer(WPARAM wTimerID)
|
|
{
|
|
CoreUpdate();
|
|
return FALSE;
|
|
}
|
|
|
|
void CMainDlg::Update()
|
|
{
|
|
if(m_pPrefDlg)m_pPrefDlg->Update();
|
|
|
|
if(m_bEditingName)return;//Do not update this dialog when editing name.
|
|
if(m_bBlockUpdate)return;//Some actions may send notify messages which then Update and overflow stack.
|
|
CUpdate U(&m_bBlockUpdate);
|
|
|
|
int nSelDev=-1;
|
|
GUID SelGUID=NULLGUID;
|
|
|
|
HWND hListCtrl=HDlgItem(IDC_LIST_DEVICE);
|
|
if(hListCtrl)
|
|
{
|
|
nSelDev=LVGetSel(hListCtrl);
|
|
SelGUID=LVGetItemGUID(hListCtrl,nSelDev);
|
|
|
|
SendMessage(hListCtrl,WM_SETREDRAW,(WPARAM)FALSE,0);
|
|
SendMessage(hListCtrl,LVM_DELETEALLITEMS,0,0);
|
|
m_ListCtrl.clear();//Must be behind LVM_DELETEALLITEMS
|
|
int nIndex=0;
|
|
for(LISTDIDEV::iterator It=m_pCore->m_ListDIDev.begin();
|
|
It!=m_pCore->m_ListDIDev.end();It++)
|
|
{
|
|
GUID G=It->InstGUID();
|
|
m_ListCtrl.push_back(G);
|
|
LVInsertItem(hListCtrl,nIndex,DEVICE_COLUMN,
|
|
It->InstName(),(LPARAM)&m_ListCtrl.back());
|
|
|
|
TCHAR Status[256];
|
|
if(It->Status()==ENotConnected) {
|
|
LoadString(m_hModule,IDS_GEN_STATUS_NOTCONNECTED, Status, cA(Status));
|
|
} else if(It->Status()==EConnected) {
|
|
LoadString(m_hModule,IDS_GEN_STATUS_OK, Status, cA(Status));
|
|
} else {
|
|
LoadString(m_hModule,IDS_GEN_STATUS_UNKNOWN, Status, cA(Status));
|
|
}
|
|
LVSetItem(hListCtrl,nIndex,STATUS_COLUMN,Status);
|
|
nIndex++;
|
|
}
|
|
|
|
nSelDev=LVFindGUIDIndex(hListCtrl,SelGUID);
|
|
if(nSelDev>=0)
|
|
{
|
|
LVSetSel(hListCtrl,nSelDev);
|
|
}
|
|
else
|
|
LVSetSel(hListCtrl,0);
|
|
nSelDev=LVGetSel(hListCtrl);
|
|
|
|
SendMessage(hListCtrl,WM_SETREDRAW,(WPARAM)TRUE,0);
|
|
InvalidateRect(hListCtrl,NULL,TRUE);
|
|
SelGUID=LVGetItemGUID(hListCtrl,nSelDev);
|
|
}
|
|
CDIDev *pSelDev=m_pCore->FindDIDev(SelGUID);
|
|
|
|
//#wi315410. we need to decide...
|
|
// HWND hAddBtn=HDlgItem(IDC_BTN_ADD);
|
|
// if(hAddBtn)
|
|
// {
|
|
// BOOL bE=(m_pCore->m_GprtBus.size()>0)?TRUE:FALSE;
|
|
// EnableWindow(hAddBtn,bE);
|
|
// }
|
|
HWND hRemBtn=HDlgItem(IDC_BTN_REMOVE);
|
|
if(hRemBtn)
|
|
{
|
|
BOOL bE=(nSelDev>=0)?TRUE:FALSE;
|
|
EnableWindow(hRemBtn,bE);
|
|
}
|
|
HWND hPropBtn=HDlgItem(IDC_BTN_PROPERTIES);
|
|
if(hPropBtn)
|
|
{
|
|
BOOL bE=FALSE;
|
|
if((nSelDev>=0)&&pSelDev)
|
|
if(pSelDev->Status()==EConnected)
|
|
bE=TRUE;
|
|
EnableWindow(hPropBtn,bE);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
Add dialog
|
|
******************************************************************************/
|
|
|
|
int CBGetCurSel(HWND hCtrl)
|
|
{
|
|
int i=ComboBox_GetCurSel(hCtrl);
|
|
if(i==CB_ERR)
|
|
i=-1;
|
|
return i;
|
|
}
|
|
|
|
int CBGetCnt(HWND hCtrl)
|
|
{
|
|
int i=ComboBox_GetCount(hCtrl);
|
|
if(i==CB_ERR)
|
|
i=0;
|
|
return i;
|
|
}
|
|
|
|
const GUID &CBGetItemGUID(HWND hCtrl,int iIndex)
|
|
{
|
|
LRESULT p=ComboBox_GetItemData(hCtrl,iIndex);
|
|
if(p==CB_ERR)return NULLGUID;
|
|
if(p)
|
|
return *(GUID*)p;
|
|
return NULLGUID;
|
|
}
|
|
|
|
int CBFindGUIDIndex(HWND hCtrl,const GUID &G)
|
|
{
|
|
int nCnt=ComboBox_GetCount(hCtrl);
|
|
if(nCnt==CB_ERR)return -1;
|
|
for(int i=0;i<nCnt;i++)
|
|
{
|
|
if(CBGetItemGUID(hCtrl,i)==G)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
LPCTSTR CBGetItemTypeName(HWND hCtrl,int iIndex)
|
|
{
|
|
LRESULT p=ComboBox_GetItemData(hCtrl,iIndex);
|
|
if(p==CB_ERR)return NULL;
|
|
if(p)
|
|
return ((String*)p)->data();
|
|
return NULL;
|
|
}
|
|
|
|
int CBFindTypeNameIndex(HWND hCtrl,LPCTSTR pTypeName)
|
|
{
|
|
int nCnt=ComboBox_GetCount(hCtrl);
|
|
if(nCnt==CB_ERR)return -1;
|
|
String TN;
|
|
if(pTypeName)
|
|
TN=pTypeName;
|
|
for(int i=0;i<nCnt;i++)
|
|
{
|
|
if(CBGetItemTypeName(hCtrl,i)==TN)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int LBGetCurSel(HWND hCtrl)
|
|
{
|
|
int i=ListBox_GetCurSel(hCtrl);
|
|
if(i==LB_ERR)return -1;
|
|
return i;
|
|
}
|
|
|
|
LPCTSTR LBGetItemTypeName(HWND hCtrl,int iIndex)
|
|
{
|
|
LRESULT p=ListBox_GetItemData(hCtrl,iIndex);
|
|
if(p==LB_ERR)return NULL;
|
|
if(p)
|
|
return ((String*)p)->data();
|
|
return NULL;
|
|
}
|
|
|
|
int LBFindTypeNameIndex(HWND hCtrl,LPCTSTR pTypeName)
|
|
{
|
|
int nCnt=ListBox_GetCount(hCtrl);
|
|
if(nCnt==LB_ERR)return -1;
|
|
String TN;
|
|
if(pTypeName)
|
|
TN=pTypeName;
|
|
for(int i=0;i<nCnt;i++)
|
|
{
|
|
if(LBGetItemTypeName(hCtrl,i)==TN)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
BOOL CAddDlg::InitDialog(HWND hFocus,LPARAM lParam)
|
|
{
|
|
m_pCore->UpdateType();
|
|
Update();
|
|
return TRUE;
|
|
}
|
|
|
|
void CAddDlg::Update()
|
|
{
|
|
if(m_bBlockUpdate)return;//Some actions may send notify messages which then Update and overflow stack.
|
|
CUpdate U(&m_bBlockUpdate);
|
|
|
|
if(!m_hWnd)return;
|
|
//Update device list.
|
|
int nSelDev=-1;
|
|
String TypeName;
|
|
|
|
HWND hListCtrl=HDlgItem(IDC_DEVICE_LIST);
|
|
if(hListCtrl)
|
|
{
|
|
int nTopIndex=ListBox_GetTopIndex(hListCtrl);
|
|
nSelDev=LBGetCurSel(hListCtrl);
|
|
LPCTSTR pTypeName=LBGetItemTypeName(hListCtrl,nSelDev);
|
|
if(pTypeName)TypeName=pTypeName;
|
|
|
|
SetWindowRedraw(hListCtrl,FALSE);
|
|
ListBox_ResetContent(hListCtrl);
|
|
m_ListCtrl.clear();//Must be behind ListBox_ResetContent
|
|
for(LISTGPRTDEV::iterator It=m_pCore->m_GprtDev.begin();
|
|
It!=m_pCore->m_GprtDev.end();It++)
|
|
{
|
|
String S=It->TypeName();
|
|
m_ListCtrl.push_back(S);
|
|
int nIndex=ListBox_AddString(hListCtrl,It->Name());
|
|
ListBox_SetItemData(hListCtrl,nIndex,&m_ListCtrl.back());
|
|
}
|
|
|
|
nSelDev=LBFindTypeNameIndex(hListCtrl,TypeName.data());
|
|
if(nSelDev>=0)
|
|
ListBox_SetCurSel(hListCtrl,nSelDev);
|
|
else
|
|
ListBox_SetCurSel(hListCtrl,0);
|
|
|
|
ListBox_SetTopIndex(hListCtrl,nTopIndex);
|
|
SetWindowRedraw(hListCtrl,TRUE);
|
|
InvalidateRect(hListCtrl,NULL,TRUE);
|
|
|
|
nSelDev=LBGetCurSel(hListCtrl);
|
|
}
|
|
|
|
HWND hRudder=HDlgItem(IDC_JOY1HASRUDDER);
|
|
if(hRudder)
|
|
{
|
|
BOOL bE=FALSE;
|
|
if(nSelDev>=0)
|
|
{
|
|
LPCTSTR pTypeName=LBGetItemTypeName(hListCtrl,nSelDev);
|
|
if(pTypeName)
|
|
{
|
|
LISTGPRTDEV::iterator It;
|
|
It=find(m_pCore->m_GprtDev.begin(),m_pCore->m_GprtDev.end(),pTypeName);
|
|
if(It!=m_pCore->m_GprtDev.end())
|
|
bE=It->Rudder()?FALSE:TRUE;
|
|
}
|
|
}
|
|
EnableWindow(hRudder,bE);
|
|
}
|
|
|
|
//Update gameport list.
|
|
nSelDev=-1;
|
|
TypeName;
|
|
|
|
HWND hListCtrlTitle=HDlgItem(IDC_GAMEPORT);
|
|
hListCtrl=HDlgItem(IDC_GAMEPORTLIST);
|
|
if(hListCtrl&&hListCtrlTitle)
|
|
{
|
|
SetWindowRedraw(hListCtrl,FALSE);
|
|
SetWindowPos(hListCtrl,NULL,NULL,NULL,NULL,NULL,
|
|
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW);
|
|
|
|
//How many gameports. Gameport list only if more than 1.
|
|
if(m_pCore->m_GprtBus.size()>1)
|
|
{
|
|
ShowWindow(hListCtrlTitle,SW_SHOWNA);
|
|
|
|
nSelDev=CBGetCurSel(hListCtrl);
|
|
LPCTSTR pTypeName=CBGetItemTypeName(hListCtrl,nSelDev);
|
|
if(pTypeName)TypeName=pTypeName;
|
|
|
|
ComboBox_ResetContent(hListCtrl);
|
|
m_GprtListCtrl.clear();//Must be behind ComboBox_ResetContent
|
|
for(LISTGPRTDEV::iterator It=m_pCore->m_GprtBus.begin();
|
|
It!=m_pCore->m_GprtBus.end();It++)
|
|
{
|
|
String S=It->TypeName();
|
|
m_GprtListCtrl.push_back(S);
|
|
int nIndex=ComboBox_AddString(hListCtrl,It->Name());
|
|
ComboBox_SetItemData(hListCtrl,nIndex,&m_GprtListCtrl.back());
|
|
}
|
|
|
|
nSelDev=CBFindTypeNameIndex(hListCtrl,TypeName.data());
|
|
if(nSelDev>=0)
|
|
ComboBox_SetCurSel(hListCtrl,nSelDev);
|
|
else
|
|
ComboBox_SetCurSel(hListCtrl,0);
|
|
SetWindowPos(hListCtrl,NULL,NULL,NULL,NULL,NULL,
|
|
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW);
|
|
}
|
|
else
|
|
{
|
|
ShowWindow(hListCtrlTitle,SW_HIDE);
|
|
}
|
|
SetWindowRedraw(hListCtrl,TRUE);
|
|
InvalidateRect(hListCtrl,NULL,TRUE);
|
|
}
|
|
|
|
HWND hCustomBtn=HDlgItem(IDC_CUSTOM);
|
|
if(hCustomBtn)
|
|
{
|
|
BOOL bE=(m_pCore->m_GprtDev.size()<MAX_DEVICES)?TRUE:FALSE;
|
|
EnableWindow(hCustomBtn,bE);
|
|
}
|
|
}
|
|
|
|
void CAddDlg::AddDev()
|
|
{
|
|
HWND hListCtrl=HDlgItem(IDC_DEVICE_LIST);
|
|
HWND hRudder=HDlgItem(IDC_JOY1HASRUDDER);
|
|
if(hListCtrl&&hRudder)
|
|
{
|
|
int nSelDev=LBGetCurSel(hListCtrl);
|
|
LPCTSTR pTypeName=LBGetItemTypeName(hListCtrl,nSelDev);
|
|
|
|
HWND hListCtrlGprt=HDlgItem(IDC_GAMEPORTLIST);
|
|
nSelDev=CBGetCurSel(hListCtrlGprt);
|
|
LPCTSTR pGprtTypeName=CBGetItemTypeName(hListCtrlGprt,nSelDev);
|
|
if(!pGprtTypeName)
|
|
{
|
|
pGprtTypeName=m_pCore->m_GprtBus.front().TypeName();
|
|
}
|
|
if(pTypeName&&pGprtTypeName)
|
|
{
|
|
GUID GOccupied=NULLGUID;
|
|
HRESULT hRes=m_pCore->AddDevice(pTypeName,Button_GetCheck(hRudder)?true:false,pGprtTypeName,GOccupied);
|
|
if(!SUCCEEDED(hRes))
|
|
switch(hRes)
|
|
{
|
|
case E_FAIL:
|
|
break;
|
|
case E_ACCESSDENIED:
|
|
if(GOccupied!=NULLGUID)
|
|
{
|
|
//Find device which occupies the port.
|
|
for(LISTDIDEV::iterator It=m_pCore->m_ListDIDev.begin();It!=m_pCore->m_ListDIDev.end();It++)
|
|
{
|
|
if(It->PortGUID()==GOccupied)
|
|
{
|
|
TCHAR Title[128];
|
|
LoadString(m_hModule,IDS_ADD_PORT_OCCUPIED, Title, cA(Title));
|
|
|
|
TCHAR Msg[256];
|
|
LoadString(m_hModule,IDS_ADD_PORT_MSGFORMAT, Msg, cA(Msg));
|
|
//Get gameport name.
|
|
LPCTSTR pBus=_T(" ");
|
|
LISTGPRTDEV::iterator It;
|
|
It=find(m_pCore->m_GprtBus.begin(),m_pCore->m_GprtBus.end(),pGprtTypeName);
|
|
if(It!=m_pCore->m_GprtBus.end())
|
|
pBus=It->Name();
|
|
//Get device name.
|
|
LPCTSTR pDev=_T(" ");
|
|
LISTDIDEV::iterator ItDN;
|
|
for(ItDN=m_pCore->m_ListDIDev.begin();ItDN!=m_pCore->m_ListDIDev.end();ItDN++)
|
|
if(ItDN->PortGUID()==GOccupied)break;
|
|
if(ItDN!=m_pCore->m_ListDIDev.end())
|
|
pDev=ItDN->InstName();
|
|
|
|
LPTSTR pT = Insert2Strings(Msg,pDev,pBus);
|
|
lstrcpy(Msg, pT);
|
|
delete[] pT;
|
|
|
|
UINT uRTL = (GetWindowLongPtr(m_hWnd,GWL_EXSTYLE) & WS_EX_LAYOUTRTL) ? MB_RTLREADING : 0;
|
|
MessageBox(m_hWnd,Msg,Title,MB_ICONHAND|MB_OK|MB_APPLMODAL|uRTL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case DIERR_DEVICEFULL:
|
|
MessageBox(m_hWnd,m_hModule,IDS_GAMEPORT_OCCUPIED_TITLE,IDS_GAMEPORT_OCCUPIED);
|
|
break;
|
|
case DIERR_NOTFOUND:
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_IDS_TITLE,IDS_NO_IDS);
|
|
break;
|
|
case DIERR_DEVICENOTREG:
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_GAMENUM_TITLE,IDS_NO_GAMENUM);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
EndDialog(m_hWnd,IDOK);
|
|
}
|
|
|
|
INT_PTR CAddDlg::Command(WORD wNotifyCode,WORD wID,HWND hwndCtl)
|
|
{
|
|
switch(wID)
|
|
{
|
|
case IDOK:
|
|
AddDev();
|
|
return 0;
|
|
case IDC_CUSTOM:
|
|
{
|
|
CCustomDlg CustomDlg(m_pCore);
|
|
if(CustomDlg.Dlg(IDD_CUSTOM,m_hModule,m_hWnd)==IDOK)
|
|
{
|
|
Update();
|
|
//Now select new custom device in this dialog box.
|
|
HWND hListCtrl=HDlgItem(IDC_DEVICE_LIST);
|
|
if(hListCtrl)
|
|
{
|
|
int nSelDev=LBFindTypeNameIndex(hListCtrl,CustomDlg.GetVIDPIDName());
|
|
if(nSelDev>=0)
|
|
{
|
|
ListBox_SetCurSel(hListCtrl,nSelDev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
case IDC_DEVICE_LIST:
|
|
switch(wNotifyCode)
|
|
{
|
|
case LBN_DBLCLK:
|
|
AddDev();
|
|
return 0;
|
|
case LBN_SELCHANGE:
|
|
Update();
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
return CDlg::Command(wNotifyCode,wID,hwndCtl);
|
|
}
|
|
|
|
INT_PTR CAddDlg::DialogProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_VKEYTOITEM:
|
|
if(LOWORD(wParam)==VK_DELETE)
|
|
{
|
|
HWND hListCtrl=HDlgItem(IDC_DEVICE_LIST);
|
|
if(hListCtrl)
|
|
{
|
|
int nSelDev=LBGetCurSel(hListCtrl);
|
|
LPCTSTR pTypeName=LBGetItemTypeName(hListCtrl,nSelDev);
|
|
if(pTypeName)
|
|
{
|
|
if(m_pCore->IsCustomDevice(pTypeName))
|
|
{
|
|
if(!m_pCore->IsDeviceActive(pTypeName))
|
|
{
|
|
CGprtDev *pDev=m_pCore->FindGprtDev(pTypeName);
|
|
if(!pDev) {
|
|
//This should never happend, but just in case.
|
|
throw JOY_EXCEPTION(E_FAIL);
|
|
}
|
|
|
|
TCHAR S[128];
|
|
LoadString(m_hModule,IDS_GEN_AREYOUSURE, S, cA(S));
|
|
LPTSTR pT = Insert1String(S,pDev->Name());
|
|
lstrcpy(S, pT);
|
|
delete[] pT;
|
|
|
|
TCHAR Title[128];
|
|
LoadString(m_hModule,IDS_GEN_AREYOUSURE_TITLE, Title, cA(Title));
|
|
|
|
UINT uRTL = (GetWindowLongPtr(m_hWnd,GWL_EXSTYLE) & WS_EX_LAYOUTRTL) ? MB_RTLREADING : 0;
|
|
if(MessageBox(m_hWnd,S,Title,
|
|
MB_ICONQUESTION|MB_YESNO|MB_APPLMODAL|uRTL)==IDYES)
|
|
{
|
|
m_pCore->DeleteType(pTypeName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageBox(m_hWnd,m_hModule,IDS_GEN_AREYOUSURE_TITLE,IDS_NO_REMOVE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else return -1;
|
|
return 0;
|
|
}
|
|
return CDlg::DialogProc(uMsg,wParam,lParam);
|
|
}
|
|
|
|
/******************************************************************************
|
|
Custom dialog
|
|
******************************************************************************/
|
|
|
|
#define MAX_ANALOG_BUTTONS 4
|
|
#define MIN_ANALOG_AXIS 2
|
|
#define MAX_ANALOG_AXIS 4
|
|
#define MAX_STR_LEN 255
|
|
|
|
BOOL CCustomDlg::InitDialog(HWND hFocus,LPARAM lParam)
|
|
{
|
|
if(!m_pCore->IsAvailableVIDPID(m_VIDPIDName))
|
|
{
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_NAME_TITLE,IDS_NOAVAILABLEVIDPID);
|
|
EndDialog(m_hWnd,IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
HWND hButtons=HDlgItem(IDC_COMBO_BUTTONS);
|
|
if(hButtons)
|
|
{
|
|
for(int i=0;i<=MAX_ANALOG_BUTTONS;i++)
|
|
{
|
|
TCHAR Str[32];
|
|
_sntprintf(Str,32,_T("%d"),i);
|
|
Str[31]=0;
|
|
ComboBox_InsertString(hButtons,i,Str);
|
|
}
|
|
ComboBox_SetCurSel(hButtons,MAX_ANALOG_BUTTONS);
|
|
}
|
|
HWND hAxis=HDlgItem(IDC_COMBO_AXIS);
|
|
if(hAxis)
|
|
{
|
|
for(int i=MIN_ANALOG_AXIS;i<=MAX_ANALOG_AXIS;i++)
|
|
{
|
|
TCHAR Str[32];
|
|
_sntprintf(Str,32,_T("%d"),i);
|
|
Str[31]=0;
|
|
ComboBox_InsertString(hAxis,i-MIN_ANALOG_AXIS,Str);
|
|
}
|
|
ComboBox_SetCurSel(hAxis,0);
|
|
}
|
|
HWND hSpecJoy=HDlgItem(IDC_SPECIAL_JOYSTICK);
|
|
if(hSpecJoy)
|
|
Button_SetCheck(hSpecJoy,BST_CHECKED);
|
|
HWND hEdit=HDlgItem(IDC_EDIT_NAME);
|
|
if(hEdit)
|
|
Edit_LimitText(hEdit,MAX_STR_LEN);
|
|
HWND hHasZAxis=HDlgItem(IDC_HASZAXIS);
|
|
if(hHasZAxis)
|
|
Button_SetCheck(hHasZAxis,BST_CHECKED);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR CCustomDlg::Command(WORD wNotifyCode,WORD wID,HWND hwndCtl)
|
|
{
|
|
switch(wID)
|
|
{
|
|
case IDOK:
|
|
{
|
|
HWND hJoy=HDlgItem(IDC_SPECIAL_JOYSTICK);
|
|
HWND hYoke=HDlgItem(IDC_SPECIAL_YOKE);
|
|
HWND hPad=HDlgItem(IDC_SPECIAL_PAD);
|
|
HWND hCar=HDlgItem(IDC_SPECIAL_AUTO);
|
|
HWND hAxis=HDlgItem(IDC_COMBO_AXIS);
|
|
HWND hRudder=HDlgItem(IDC_HASRUDDER);
|
|
HWND hZAxis=HDlgItem(IDC_HASZAXIS);
|
|
HWND hButtons=HDlgItem(IDC_COMBO_BUTTONS);
|
|
HWND hPov=HDlgItem(IDS_CUSTOM_HASPOV);
|
|
HWND hEdit=HDlgItem(IDC_EDIT_NAME);
|
|
if(hJoy&&
|
|
hYoke&&
|
|
hPad&&
|
|
hCar&&
|
|
hAxis&&
|
|
hRudder&&
|
|
hZAxis&&
|
|
hButtons&&
|
|
hPov&&
|
|
hEdit)//Possible internal error.
|
|
{
|
|
TCHAR *pStr=NULL;
|
|
bool bErr=false;
|
|
|
|
int nLen=Edit_LineLength(hEdit,0);//Possible internal error.
|
|
if(!nLen)
|
|
{
|
|
bErr=true;
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_NAME_TITLE,IDS_NO_NAME);
|
|
}
|
|
else
|
|
{
|
|
pStr=new TCHAR[nLen+1];
|
|
if (pStr == NULL)
|
|
return 0; //Internal error
|
|
if(GetDlgItemText(m_hWnd,IDC_EDIT_NAME,pStr,nLen+1)!=nLen)
|
|
return 0;//Internal error.
|
|
if(_tcschr(pStr,_T('\\')))
|
|
{
|
|
bErr=true;
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_NAME_TITLE,IDS_INVALID_NAME);
|
|
}
|
|
else
|
|
{
|
|
if(m_pCore->DuplicateDeviceName(pStr))
|
|
{
|
|
bErr=true;
|
|
MessageBox(m_hWnd,m_hModule,IDS_NO_NAME_TITLE,IDS_DUPLICATE_TYPE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bErr)//User entered invalid text for name.
|
|
{
|
|
SetFocus(m_hWnd);
|
|
SetFocus(hEdit);
|
|
Edit_SetSel(hEdit,0,-1);
|
|
return 0;
|
|
}
|
|
|
|
m_pCore->AddCustomDevice(
|
|
(Button_GetCheck(hJoy)==BST_CHECKED)?true:false,
|
|
(Button_GetCheck(hPad)==BST_CHECKED)?true:false,
|
|
(Button_GetCheck(hYoke)==BST_CHECKED)?true:false,
|
|
(Button_GetCheck(hCar)==BST_CHECKED)?true:false,
|
|
ComboBox_GetCurSel(hAxis)+MIN_ANALOG_AXIS,
|
|
(Button_GetCheck(hZAxis)==BST_CHECKED)?true:false,
|
|
ComboBox_GetCurSel(hButtons),
|
|
(Button_GetCheck(hPov)==BST_CHECKED)?true:false,
|
|
pStr,
|
|
m_VIDPIDName.data());
|
|
|
|
if( pStr ) {
|
|
delete[] pStr;
|
|
}
|
|
}
|
|
}
|
|
EndDialog(m_hWnd,IDOK);
|
|
return 0;
|
|
case IDC_COMBO_AXIS:
|
|
if(wNotifyCode==CBN_SELCHANGE)
|
|
{
|
|
HWND hAxis=HDlgItem(IDC_COMBO_AXIS);
|
|
if(hAxis)
|
|
{
|
|
UINT uShow=SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER;
|
|
if((ComboBox_GetCurSel(hAxis)+MIN_ANALOG_AXIS)==3)//If 3 axis selected.
|
|
uShow|=SWP_SHOWWINDOW;
|
|
else
|
|
uShow|=SWP_HIDEWINDOW;
|
|
|
|
HWND hHasZAxis=HDlgItem(IDC_HASZAXIS);
|
|
if(hHasZAxis)
|
|
{
|
|
SetWindowPos(hHasZAxis,NULL,NULL,NULL,NULL,NULL,uShow);
|
|
}
|
|
HWND hHasRudder=HDlgItem(IDC_HASRUDDER);
|
|
if(hHasRudder)
|
|
{
|
|
SetWindowPos(hHasRudder,NULL,NULL,NULL,NULL,NULL,uShow);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
return CDlg::Command(wNotifyCode,wID,hwndCtl);
|
|
}
|
|
|
|
/******************************************************************************
|
|
Preferred dialog
|
|
******************************************************************************/
|
|
|
|
void CPreferredDlg::Preferred()
|
|
{
|
|
HWND hListCtrl=HDlgItem(IDC_CHANGE_LIST);
|
|
if(hListCtrl)
|
|
{
|
|
int nSelDev=CBGetCurSel(hListCtrl);
|
|
GUID SelGUID=CBGetItemGUID(hListCtrl,nSelDev);
|
|
if(SelGUID!=NULLGUID)
|
|
m_pCore->Preferred(SelGUID);
|
|
}
|
|
EndDialog(m_hWnd,IDOK);
|
|
}
|
|
|
|
INT_PTR CPreferredDlg::Command(WORD wNotifyCode,WORD wID,HWND hwndCtl)
|
|
{
|
|
switch(wID)
|
|
{
|
|
case IDOK:
|
|
Preferred();
|
|
return 0;
|
|
}
|
|
if(wNotifyCode==CBN_CLOSEUP)
|
|
{
|
|
Update();
|
|
return 0;
|
|
}
|
|
return CDlg::Command(wNotifyCode,wID,hwndCtl);
|
|
}
|
|
|
|
BOOL CPreferredDlg::InitDialog(HWND hFocus,LPARAM lParam)
|
|
{
|
|
Update();
|
|
HWND hListCtrl=HDlgItem(IDC_CHANGE_LIST);
|
|
if(hListCtrl)
|
|
{
|
|
for(LISTDIDEV::iterator It=m_pCore->m_ListDIDev.begin();
|
|
It!=m_pCore->m_ListDIDev.end();It++)
|
|
{
|
|
if(It->Id()==0)
|
|
{
|
|
int nSelDev=CBFindGUIDIndex(hListCtrl,It->InstGUID());
|
|
if(nSelDev>=0)
|
|
ComboBox_SetCurSel(hListCtrl,nSelDev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CPreferredDlg::Update()
|
|
{
|
|
if(m_bBlockUpdate)return;//Some actions may send notify messages which then Update and overflow stack.
|
|
CUpdate U(&m_bBlockUpdate);
|
|
|
|
if(!m_hWnd)return;
|
|
|
|
int nSelDev=-1;
|
|
GUID SelGUID=NULLGUID;
|
|
|
|
HWND hListCtrl=HDlgItem(IDC_CHANGE_LIST);
|
|
if(hListCtrl)
|
|
{
|
|
if(ComboBox_GetDroppedState(hListCtrl))return;//No update when selecting preferred.
|
|
|
|
int nCount=CBGetCnt(hListCtrl);
|
|
nSelDev=CBGetCurSel(hListCtrl);
|
|
SelGUID=CBGetItemGUID(hListCtrl,nSelDev);
|
|
|
|
SetWindowRedraw(hListCtrl,FALSE);
|
|
ComboBox_ResetContent(hListCtrl);
|
|
m_ListCtrl.clear();//Must be behind ComboBox_ResetContent.
|
|
int nId0Index=-1;//Index of preferred device.
|
|
for(LISTDIDEV::iterator It=m_pCore->m_ListDIDev.begin();
|
|
It!=m_pCore->m_ListDIDev.end();It++)
|
|
{
|
|
GUID G=It->InstGUID();
|
|
m_ListCtrl.push_back(G);
|
|
int nIndex=ComboBox_AddString(hListCtrl,It->InstName());
|
|
ComboBox_SetItemData(hListCtrl,nIndex,&m_ListCtrl.back());
|
|
if(It->Id()==0)
|
|
nId0Index=nIndex;
|
|
}
|
|
|
|
int nNoneIndex=-1;
|
|
if(nId0Index<0)//Only if there is no preferred device.
|
|
{
|
|
TCHAR None[256];
|
|
LoadString(m_hModule,IDS_NONE, None, cA(None));
|
|
nNoneIndex=ComboBox_AddString(hListCtrl,None);
|
|
}
|
|
|
|
if((!nCount)||//First update during init.
|
|
(SelGUID==NULLGUID))//Or none is selected.
|
|
{
|
|
if(nId0Index>=0)//Preferred device was added.
|
|
ComboBox_SetCurSel(hListCtrl,nId0Index);
|
|
else//There is no preferred device.
|
|
ComboBox_SetCurSel(hListCtrl,nNoneIndex);
|
|
}
|
|
else//List was not empty before update. Select same thing.
|
|
{
|
|
nSelDev=CBFindGUIDIndex(hListCtrl,SelGUID);
|
|
if(nSelDev>=0)
|
|
ComboBox_SetCurSel(hListCtrl,nSelDev);
|
|
else//Selected device removed.
|
|
{
|
|
if(nId0Index>=0)//Then select original preferred device.
|
|
ComboBox_SetCurSel(hListCtrl,nId0Index);
|
|
else//Select none.
|
|
ComboBox_SetCurSel(hListCtrl,nNoneIndex);
|
|
}
|
|
}
|
|
|
|
SetWindowRedraw(hListCtrl,TRUE);
|
|
InvalidateRect(hListCtrl,NULL,TRUE);
|
|
}
|
|
}
|
|
|
|
INT_PTR CPreferredDlg::Notify(int idCtrl,LPNMHDR pnmh)
|
|
{
|
|
// switch(pnmh->code)
|
|
// {
|
|
/*case LVN_KEYDOWN:
|
|
switch(((LV_KEYDOWN*)pnmh)->wVKey)
|
|
{
|
|
case VK_F5:
|
|
CoreUpdate();
|
|
return 0;
|
|
}
|
|
return 0;
|
|
case LVN_ITEMCHANGED:
|
|
Update();
|
|
return 0;*/
|
|
/* case NM_DBLCLK:
|
|
switch(pnmh->idFrom)
|
|
{
|
|
case IDC_CHANGE_LIST:
|
|
Preferred();
|
|
return 0;
|
|
}
|
|
return 0;*/
|
|
// }
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
Connect UI and core
|
|
******************************************************************************/
|
|
|
|
class CCP:public CCore,public CMainDlg
|
|
{
|
|
virtual void UIUpdate(){CMainDlg::Update();};
|
|
public:
|
|
CCP(){ConnectUI((CCore*)this);};
|
|
};
|
|
|
|
/******************************************************************************
|
|
Entry point.
|
|
******************************************************************************/
|
|
|
|
#define MUTEX_NAME _T("$$$MS_GameControllers_Cpl$$$")
|
|
|
|
void Core(HANDLE hModule,HWND hWnd)
|
|
{
|
|
static HWND hPrevHwnd=NULL;
|
|
static HANDLE hMutex=CreateMutex(NULL,TRUE,MUTEX_NAME);
|
|
|
|
if(GetLastError()==ERROR_ALREADY_EXISTS)
|
|
{
|
|
SetForegroundWindow(hPrevHwnd);
|
|
}
|
|
else
|
|
{
|
|
hPrevHwnd=hWnd;
|
|
|
|
_PNH _old_handler;
|
|
_old_handler = _set_new_handler(my_new_handler);
|
|
|
|
if( SHFusionInitializeFromModuleID((HMODULE)hModule,124) )
|
|
{
|
|
// While not initializing would only cause theming to be inactive,
|
|
// a failure to initialize indicates a much worse problem so since
|
|
// PREFIX warns (656863) that the return needs to be tested, abort
|
|
// if the initialization fails.
|
|
try
|
|
{
|
|
CCP CP;
|
|
CP.Dlg(IDD_CPANEL,(HMODULE)hModule,hWnd);
|
|
}
|
|
catch(JoyException E)
|
|
{
|
|
}
|
|
catch(exception)
|
|
{
|
|
}
|
|
SHFusionUninitialize();
|
|
}
|
|
|
|
_set_new_handler(_old_handler);
|
|
|
|
ReleaseMutex(hMutex);
|
|
CloseHandle(hMutex);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
Propertiy
|
|
******************************************************************************/
|
|
|
|
class CoInit
|
|
{
|
|
HRESULT m_hRes;
|
|
public:
|
|
~CoInit()
|
|
{
|
|
if(SUCCEEDED(m_hRes))
|
|
{
|
|
CoFreeUnusedLibraries();//Free gcdef.dll.
|
|
CoUninitialize();
|
|
}
|
|
};
|
|
CoInit(){m_hRes=CoInitialize(NULL);};
|
|
operator HRESULT(){return m_hRes;};
|
|
};
|
|
typedef HPROPSHEETPAGE *LPHPROPSHEETPAGE;
|
|
typedef AutoDeleteArray<LPHPROPSHEETPAGE> LPHPROPSHEETPAGE_ADAR;
|
|
|
|
//FUN! FUN! FUN! FUN!
|
|
//This is certain funny code written by some other, quite funny people,
|
|
//so I will put it in the funny code section at the end of the otherwise
|
|
//serious file.
|
|
|
|
//tmarkoc cleaned it up. No more memory/interface/freelib leaks, alloc failures handled, no unneccessary allocations.
|
|
|
|
/*
|
|
#ifndef PPVOID
|
|
typedef LPVOID* PPVOID;
|
|
#endif
|
|
//WHAT IS THIS FOR????????????????????????????????????????????????????????
|
|
class CDIGameCntrlPropSheet : public IDIGameCntrlPropSheet
|
|
{
|
|
private:
|
|
DWORD m_cProperty_refcount;
|
|
|
|
public:
|
|
CDIGameCntrlPropSheet(void);
|
|
~CDIGameCntrlPropSheet(void);
|
|
|
|
// IUnknown methods
|
|
STDMETHODIMP QueryInterface(REFIID, PPVOID);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// CImpIServerProperty methods
|
|
STDMETHODIMP GetSheetInfo(LPDIGCSHEETINFO *lpSheetInfo);
|
|
STDMETHODIMP GetPageInfo (LPDIGCPAGEINFO *lpPageInfo );
|
|
STDMETHODIMP SetID(USHORT nID);
|
|
STDMETHODIMP_(USHORT) GetID(void);
|
|
};*/
|
|
//typedef CDIGameCntrlPropSheet *LPCDIGAMECNTRLPROPSHEET;
|
|
typedef IDIGameCntrlPropSheet *LPCDIGAMECNTRLPROPSHEET;
|
|
|
|
typedef AutoRelease<LPCDIGAMECNTRLPROPSHEET> LPCDIGAMECNTRLPROPSHEET_AR;
|
|
|
|
HRESULT Properties(HMODULE hMod,HWND hWnd,CCore *pCore,DWORD dwId)
|
|
{
|
|
// ASSERT(IsWindow(hWnd));
|
|
|
|
CLSID clsidPropSheet=CLSID_LegacyServer;
|
|
|
|
//Get type name.
|
|
DIJOYCONFIG DIJoyCfg;
|
|
ZeroMemory(&DIJoyCfg,sizeof(DIJoyCfg));
|
|
DIJoyCfg.dwSize=sizeof(DIJoyCfg);
|
|
if(SUCCEEDED(pCore->m_pDIJoyCfg->GetConfig(dwId,&DIJoyCfg,DIJC_REGHWCONFIGTYPE|DIJC_CALLOUT)))
|
|
{
|
|
//Get the clsidConfig.
|
|
DIJOYTYPEINFO DIJoyType;
|
|
ZeroMemory(&DIJoyType,sizeof(DIJoyType));
|
|
DIJoyType.dwSize=sizeof(DIJoyType);
|
|
if(SUCCEEDED(pCore->m_pDIJoyCfg->GetTypeInfo(DIJoyCfg.wszType,&DIJoyType,DITC_CLSIDCONFIG|DITC_REGHWSETTINGS|DITC_FLAGS1)))
|
|
{
|
|
if((DIJoyType.clsidConfig!=GUID_NULL)&&
|
|
!(DIJoyType.dwFlags1&JOYTYPE_DEFAULTPROPSHEET))
|
|
clsidPropSheet=DIJoyType.clsidConfig;
|
|
}
|
|
}
|
|
int nStartPage=(clsidPropSheet==CLSID_LegacyServer)?1:0;
|
|
|
|
if(nStartPage>MAX_PAGES)
|
|
return(DIGCERR_STARTPAGETOOLARGE);
|
|
|
|
CoInit CI;//CoInitialize(NULL); Auto CoFreeUnusedLibraries();CoUninitialize();.
|
|
LPCDIGAMECNTRLPROPSHEET_AR fnInterface;
|
|
if(SUCCEEDED(CI))
|
|
{
|
|
IClassFactory* pCF;
|
|
if(SUCCEEDED(CoGetClassObject(clsidPropSheet,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(LPVOID*)&pCF)))
|
|
{
|
|
pCF->CreateInstance(NULL,IID_IDIGameCntrlPropSheet,(LPVOID*)&fnInterface);
|
|
pCF->Release();
|
|
}
|
|
else
|
|
{
|
|
//reset to legacy server
|
|
clsidPropSheet=CLSID_LegacyServer;
|
|
nStartPage=1;
|
|
if(SUCCEEDED(CoGetClassObject(clsidPropSheet,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(LPVOID*)&pCF)))
|
|
{
|
|
pCF->CreateInstance(NULL,IID_IDIGameCntrlPropSheet,(LPVOID*)&fnInterface);
|
|
pCF->Release();
|
|
}
|
|
}
|
|
}
|
|
if(*((PVOID *)&fnInterface) == NULL)
|
|
{
|
|
return(E_NOINTERFACE);
|
|
}
|
|
//Send device Id to the property sheet.
|
|
fnInterface->SetID(dwId);
|
|
|
|
LPDIGCSHEETINFO pServerSheet=NULL;
|
|
//Get the property sheet info from the server.
|
|
if(FAILED(fnInterface->GetSheetInfo(&pServerSheet)))
|
|
{
|
|
return(E_FAIL);
|
|
}
|
|
//Verify data from server.
|
|
if(pServerSheet->nNumPages==0)
|
|
return(DIGCERR_NUMPAGESZERO);
|
|
else if((pServerSheet->nNumPages>MAX_PAGES)||(pServerSheet->nNumPages<nStartPage))
|
|
return(DIGCERR_NUMPAGESTOOLARGE);
|
|
|
|
LPDIGCPAGEINFO pServerPage=NULL;
|
|
//Get the information for all the pages from the server.
|
|
if(FAILED(fnInterface->GetPageInfo(&pServerPage)))
|
|
{
|
|
return(E_FAIL);
|
|
}
|
|
|
|
// Allocate memory for the pages.
|
|
LPHPROPSHEETPAGE_ADAR pPages=new HPROPSHEETPAGE[pServerSheet->nNumPages];
|
|
if(*((PVOID *)&pPages) == NULL) return(E_OUTOFMEMORY);
|
|
ZeroMemory((LPHPROPSHEETPAGE)pPages,sizeof(HPROPSHEETPAGE)*pServerSheet->nNumPages);
|
|
|
|
// Allocate memory for the header!
|
|
PROPSHEETHEADER SH;
|
|
ZeroMemory(&SH,sizeof(SH));
|
|
SH.dwSize=sizeof(SH);
|
|
SH.hwndParent= hWnd;
|
|
SH.hInstance=pServerPage[0].hInstance;
|
|
if(pServerSheet->fSheetIconFlag)
|
|
{
|
|
if(pServerSheet->lpwszSheetIcon)
|
|
{
|
|
//Check to see if you are an INT or a WSTR.
|
|
if(HIWORD((INT_PTR)pServerSheet->lpwszSheetIcon))
|
|
{
|
|
//You are a string.
|
|
SH.pszIcon=pServerSheet->lpwszSheetIcon;
|
|
}
|
|
else
|
|
SH.pszIcon=(LPCTSTR)(pServerSheet->lpwszSheetIcon);
|
|
SH.dwFlags=PSH_USEICONID;
|
|
}
|
|
else return(DIGCERR_NOICON);
|
|
}
|
|
|
|
//Do we have a sheet caption?
|
|
if(pServerSheet->lpwszSheetCaption)
|
|
{
|
|
SH.pszCaption=pServerSheet->lpwszSheetCaption;
|
|
SH.dwFlags|=PSH_PROPTITLE;
|
|
}
|
|
|
|
SH.nPages=pServerSheet->nNumPages;
|
|
SH.nStartPage=nStartPage;
|
|
|
|
//Set the property pages inofrmation into the header.
|
|
SH.phpage=(LPHPROPSHEETPAGE)pPages;
|
|
|
|
|
|
//Sheet stuff is done.Now do the pages.
|
|
PROPSHEETPAGE PropPage;
|
|
ZeroMemory(&PropPage,sizeof(PropPage));
|
|
PropPage.dwSize=sizeof(PropPage);
|
|
|
|
//Fill up each page.
|
|
int nIndex=0;
|
|
do
|
|
{
|
|
//Assign the things that there are not questionable.
|
|
PropPage.lParam=pServerPage[nIndex].lParam;
|
|
PropPage.hInstance=pServerPage[nIndex].hInstance;
|
|
|
|
// Add the title.
|
|
if(pServerPage[nIndex].lpwszPageTitle)
|
|
{
|
|
PropPage.dwFlags=PSP_USETITLE;
|
|
//Check to see if you are a string.
|
|
if(HIWORD((INT_PTR)pServerPage[nIndex].lpwszPageTitle))
|
|
{
|
|
PropPage.pszTitle=pServerPage[nIndex].lpwszPageTitle;
|
|
}
|
|
else
|
|
PropPage.pszTitle=(LPTSTR)pServerPage[nIndex].lpwszPageTitle;
|
|
}
|
|
else PropPage.pszTitle=NULL;
|
|
|
|
//If icon is required go ahead and add it.
|
|
if(pServerPage[nIndex].fIconFlag)
|
|
{
|
|
PropPage.dwFlags|=PSP_USEICONID;
|
|
//Check to see if you are an INT or a String.
|
|
if(HIWORD((INT_PTR)pServerPage[nIndex].lpwszPageIcon))
|
|
{
|
|
//You're a string.
|
|
PropPage.pszIcon=pServerPage[nIndex].lpwszPageIcon;
|
|
}
|
|
else
|
|
PropPage.pszIcon=(LPCTSTR)(pServerPage[nIndex].lpwszPageIcon);
|
|
}
|
|
|
|
//If a pre - post processing call back proc is required go ahead and add it.
|
|
if(pServerPage[nIndex].fProcFlag)
|
|
{
|
|
if(pServerPage[nIndex].fpPrePostProc)
|
|
{
|
|
PropPage.dwFlags|=PSP_USECALLBACK;
|
|
PropPage.pfnCallback=(LPFNPSPCALLBACK)pServerPage[nIndex].fpPrePostProc;
|
|
}
|
|
else
|
|
return(DIGCERR_NOPREPOSTPROC);
|
|
}
|
|
|
|
//And the essential "dialog" proc.
|
|
if(pServerPage[nIndex].fpPageProc)
|
|
PropPage.pfnDlgProc=pServerPage[nIndex].fpPageProc;
|
|
else
|
|
return(DIGCERR_NODLGPROC);
|
|
|
|
//Assign the dialog template.
|
|
if(HIWORD((INT_PTR)pServerPage[nIndex].lpwszTemplate))
|
|
{
|
|
PropPage.pszTemplate=pServerPage[nIndex].lpwszTemplate;
|
|
}
|
|
else
|
|
PropPage.pszTemplate=(LPTSTR)pServerPage[nIndex].lpwszTemplate;
|
|
|
|
if(clsidPropSheet!=CLSID_LegacyServer)//If third party software do not enforce theme.
|
|
((LPHPROPSHEETPAGE)pPages)[nIndex++]=SHNoFusionCreatePropertySheetPageW(&PropPage);
|
|
else
|
|
((LPHPROPSHEETPAGE)pPages)[nIndex++]=CreatePropertySheetPage(&PropPage);
|
|
}
|
|
while(nIndex<pServerSheet->nNumPages);
|
|
|
|
//Launch modal property sheet dialog.
|
|
PropertySheet(&SH);
|
|
|
|
pCore->Update();
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
#undef cA
|