dolphin/Source/Plugins/Plugin_PadDX9/Src/multidi.cpp

1058 lines
37 KiB
C++
Raw Normal View History

//-----------------------------------------------------------------------------
// File: MultiDI.cpp
//
// Desc: DirectInput framework class using semantic mapping with multiplayer
// device ownership. Feel free to use this class as a starting point
// for adding extra functionality.
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//-----------------------------------------------------------------------------
#define STRICT
#define DIRECTINPUT_VERSION 0x0800
#include "stdafx.h"
#include <basetsd.h>
#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <dxerr9.h>
#include <d3d9types.h> // included to get the D3DCOLOR_RGBA macro.
#include "MultiDI.h"
#include <assert.h>
//-----------------------------------------------------------------------------
// Name: CMultiplayerInputDeviceManager
// Desc: Constructor
// Args: strRegKey - A location in the registry where device ownership
// information should be stored
//-----------------------------------------------------------------------------
CMultiplayerInputDeviceManager::CMultiplayerInputDeviceManager( TCHAR* strRegKey )
{
HRESULT hr = CoInitialize(NULL);
m_bCleanupCOM = SUCCEEDED(hr);
LONG nResult;
// Initialize members
m_pDI = NULL;
m_hWnd = NULL;
m_pdiaf = NULL;
m_pUsers = NULL;
m_pDeviceList = NULL;
m_AddDeviceCallback = NULL;
m_AddDeviceCallbackParam = NULL;
m_hKey = NULL;
m_dwNumDevices = 0;
m_dwMaxDevices = 0;
// Duplicate the registry location string since we'll need this again later
m_strKey = _tcsdup( strRegKey );
if( m_strKey == NULL )
return;
// Create a reg key to store device ownership data
nResult = RegCreateKeyEx( HKEY_CURRENT_USER, strRegKey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL,
&m_hKey, NULL );
if(nResult != ERROR_SUCCESS)
m_hKey = NULL;
}
//-----------------------------------------------------------------------------
// Name: ~CMultiplayerInputDeviceManager
// Desc: Destructor
//-----------------------------------------------------------------------------
CMultiplayerInputDeviceManager::~CMultiplayerInputDeviceManager()
{
Cleanup();
if( m_bCleanupCOM )
CoUninitialize();
RegCloseKey( m_hKey );
free( m_strKey );
}
//-----------------------------------------------------------------------------
// Name: Create
// Desc: Initializes the class, and enums the devices. See MultiMapper sample
// for how to use this class.
// It might fail if there are too many players for the
// number of devices availible, or if one player owns too many
// devices preventing others from having a device. Its up the app
// to prevent this or respond to this.
// Note: strUserName should be a array of sz strings
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::Create( HWND hWnd,
TCHAR* strUserNames[],
DWORD dwNumUsers,
DIACTIONFORMAT* pdiaf,
LPDIMANAGERCALLBACK AddDeviceCallback,
LPVOID pCallbackParam,
BOOL bResetOwnership,
BOOL bResetMappings )
{
HRESULT hr;
if( strUserNames == NULL || dwNumUsers == 0 )
return E_INVALIDARG;
Cleanup();
// Store data
m_hWnd = hWnd;
// Create and init the m_pUsers array
m_dwNumUsers = dwNumUsers;
m_pUsers = new PlayerInfo*[dwNumUsers];
for( DWORD i=0; i<dwNumUsers; i++ )
{
m_pUsers[i] = new PlayerInfo;
ZeroMemory( m_pUsers[i], sizeof(PlayerInfo) );
m_pUsers[i]->dwPlayerIndex = i; // set the 0-based player index (for easy referencing)
lstrcpyn( m_pUsers[i]->strPlayerName, strUserNames[i], MAX_PATH-1 );
}
m_AddDeviceCallback = AddDeviceCallback;
m_AddDeviceCallbackParam = pCallbackParam;
// Create the base DirectInput object
if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (VOID**)&m_pDI, NULL ) ) )
return DXTRACE_ERR( TEXT("DirectInput8Create"), hr );
if( FAILED( hr = SetActionFormat( pdiaf, TRUE, bResetOwnership, bResetMappings ) ) )
return DXTRACE_ERR( TEXT("SetActionFormat"), hr );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: SetActionFormat
// Desc: Sets a new action format.
// It re-enumerates the devices if bReenumerate
// It resets the ownership of the devices if bResetOwnership
// It resets the mapper actions of the devices if bResetMappings
// This function may fail if there are too many players for the
// number of devices availible, or if one player owns too many
// devices preventing others from having a device. Its up the app
// to prevent this or respond to this.
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::SetActionFormat( DIACTIONFORMAT* pdiaf,
BOOL bReenumerate,
BOOL bResetOwnership,
BOOL bResetMappings )
{
HRESULT hr;
DWORD iPlayer;
DWORD iDevice;
// Store the new action format
m_pdiaf = pdiaf;
// Only destroy and re-enumerate devices if the caller explicitly wants to.
// This isn't thread safe, so be sure not to have any other threads using
// this data unless you redesign this class
if( bReenumerate )
{
// Set all players to not have a device yet
for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
if( bResetOwnership )
{
// Set all devices as not assigned to a player
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
m_pDeviceList[iDevice].pPlayerInfo = NULL;
// Delete the device ownership keys
DeleteDeviceOwnershipKeys();
}
// Devices must be unacquired to have a new action map set.
UnacquireDevices();
// Enumerate all available devices that map the pri 1 actions,
// and store them in the m_pDeviceList array
if( FAILED( hr = BuildDeviceList() ) )
return DXTRACE_ERR( TEXT("BuildDeviceList"), hr );
// Assign devices to any players that don't have devices
// Some games may want to change this functionality.
if( FAILED( hr = AssignDevices() ) )
return DXTRACE_ERR( TEXT("AssignDevices"), hr );
// Report an error if there are too many players for the
// number of devices availible, or if one player owns too many
// devices preventing others from having a device
if( FAILED( hr = VerifyAssignment() ) )
return hr;
// Now that every player has at least one device, build and set
// the action map, and use callback into the app to tell the
// app of the device assignment.
if( FAILED( hr = AddAssignedDevices( bResetMappings ) ) )
return DXTRACE_ERR( TEXT("AddAssignedDevices"), hr );
// For every device that's assigned to a player, save its device key
// and assigned player to registry, so the app remembers which devices
// each player prefers
if( FAILED( hr = SaveDeviceOwnershipKeys() ) )
return DXTRACE_ERR( TEXT("SaveDeviceOwnershipKeys"), hr );
}
else
{
// Just apply the new maps for each device owned by each user
// Devices must be unacquired to have a new action map set.
UnacquireDevices();
// Apply the new action map to the current devices.
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;
if( FAILED( hr = pdidDevice->BuildActionMap( m_pdiaf, pPlayerInfo->strPlayerName, DIDBAM_DEFAULT ) ) )
return DXTRACE_ERR( TEXT("BuildActionMap"), hr );
if( FAILED( hr = pdidDevice->SetActionMap( m_pdiaf, pPlayerInfo->strPlayerName, DIDSAM_DEFAULT ) ) )
return DXTRACE_ERR( TEXT("SetActionMap"), hr );
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: BuildDeviceList
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::BuildDeviceList()
{
// Cleanup any previously enumerated devices
CleanupDeviceList();
// Build a simple list of all devices currently attached to the machine. This
// array will be used to reassign devices to each user.
m_dwMaxDevices = 5;
m_dwNumDevices = 0;
m_pDeviceList = NULL;
DeviceInfo* pListNew = NULL;
pListNew = (DeviceInfo*) realloc( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
// Verify allocation
if( NULL == pListNew )
return DXTRACE_ERR( TEXT("BuildDeviceList"), E_OUTOFMEMORY );
else
m_pDeviceList = pListNew;
ZeroMemory( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
// Enumerate available devices for any user.
HRESULT rs = m_pDI->EnumDevicesBySemantics( NULL, m_pdiaf, StaticEnumSuitableDevicesCB,
this, DIEDBSFL_ATTACHEDONLY );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: EnumSuitableDevicesCB
// Desc: DirectInput device enumeratation callback. Calls AddDevice()
// on each device enumerated.
//-----------------------------------------------------------------------------
BOOL CALLBACK CMultiplayerInputDeviceManager::StaticEnumSuitableDevicesCB( LPCDIDEVICEINSTANCE pdidi,
LPDIRECTINPUTDEVICE8 pdidDevice,
DWORD dwFlags, DWORD dwDeviceRemaining,
VOID* pContext )
{
// Add the device to the device manager's internal list
CMultiplayerInputDeviceManager* pInputDeviceManager = (CMultiplayerInputDeviceManager*)pContext;
return pInputDeviceManager->EnumDevice( pdidi, pdidDevice,
dwFlags, dwDeviceRemaining );
}
//-----------------------------------------------------------------------------
// Name: EnumDevice
// Desc: Enums each device to see if its suitable to add
//-----------------------------------------------------------------------------
BOOL CMultiplayerInputDeviceManager::EnumDevice( const DIDEVICEINSTANCE* pdidi,
const LPDIRECTINPUTDEVICE8 pdidDevice,
DWORD dwFlags, DWORD dwRemainingDevices )
{
TCHAR strPlayerName[MAX_PATH];
TCHAR strDeviceGuid[40];
// Devices of type DI8DEVTYPE_DEVICECTRL are specialized devices not generally
// considered appropriate to control game actions. We just ignore these.
if( GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_DEVICECTRL )
{
// We're only interested in devices that map the pri 1 actions
if( dwFlags & DIEDBS_MAPPEDPRI1 )
{
// Add new pdidDevice struct to array, and resize array if needed
m_dwNumDevices++;
if( m_dwNumDevices > m_dwMaxDevices )
{
m_dwMaxDevices += 5;
DeviceInfo* pListNew = NULL;
pListNew = (DeviceInfo*) realloc( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
// Verify allocation
if( NULL == pListNew )
{
DXTRACE_ERR( TEXT("EnumDevice"), E_OUTOFMEMORY );
return DIENUM_STOP;
}
else
m_pDeviceList = pListNew;
ZeroMemory( m_pDeviceList + m_dwMaxDevices - 5, 5*sizeof(DeviceInfo) );
}
DXUtil_ConvertGUIDToStringCch( &pdidi->guidInstance, strDeviceGuid, 40 );
DXUtil_ReadStringRegKeyCch( m_hKey, strDeviceGuid, strPlayerName, MAX_PATH, TEXT("") );
// Add the device to the array m_pDeviceList
DWORD dwCurrentDevice = m_dwNumDevices-1;
ZeroMemory( &m_pDeviceList[dwCurrentDevice], sizeof(DeviceInfo) );
m_pDeviceList[dwCurrentDevice].didi = *pdidi;
m_pDeviceList[dwCurrentDevice].pdidDevice = pdidDevice;
m_pDeviceList[dwCurrentDevice].pdidDevice->AddRef();
m_pDeviceList[dwCurrentDevice].bMapsPri1Actions = ((dwFlags & DIEDBS_MAPPEDPRI1) == DIEDBS_MAPPEDPRI1);
m_pDeviceList[dwCurrentDevice].bMapsPri2Actions = ((dwFlags & DIEDBS_MAPPEDPRI2) == DIEDBS_MAPPEDPRI2);
if( lstrcmp( strPlayerName, TEXT("") ) != 0 )
{
m_pDeviceList[dwCurrentDevice].pPlayerInfo = LookupPlayer( strPlayerName );
if( m_pDeviceList[dwCurrentDevice].pPlayerInfo )
m_pDeviceList[dwCurrentDevice].pPlayerInfo->bFoundDeviceForPlayer = TRUE;
}
}
}
// Continue enumerating
return DIENUM_CONTINUE;
}
//-----------------------------------------------------------------------------
// Name: AssignDevices
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::AssignDevices()
{
DWORD iDevice;
DWORD iPlayer;
// For any device that doesn't have a user assigned to it,
// then assign it to the first user that needs a device
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
if( m_pDeviceList[iDevice].pPlayerInfo == NULL )
{
for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
{
if( !m_pUsers[iPlayer]->bFoundDeviceForPlayer )
{
m_pDeviceList[iDevice].pPlayerInfo = m_pUsers[iPlayer];
m_pUsers[iPlayer]->bFoundDeviceForPlayer = TRUE;
break;
}
}
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: VerifyAssignment
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::VerifyAssignment()
{
DWORD iPlayer;
DWORD iDevice;
// For each player, make sure that a device was found for this
// player, otherwise return failure.
for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
{
if( !m_pUsers[iPlayer]->bFoundDeviceForPlayer )
{
if( GetNumDevices() < m_dwNumUsers )
return E_DIUTILERR_TOOMANYUSERS;
else
{
// Check to see if there's a device that isn't already
// assigned to a player. If there is return
// E_DIUTILERR_PLAYERWITHOUTDEVICE otherwise return
// E_DIUTILERR_DEVICESTAKEN
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
if( m_pDeviceList[iDevice].pPlayerInfo == NULL )
return E_DIUTILERR_PLAYERWITHOUTDEVICE;
}
return E_DIUTILERR_DEVICESTAKEN;
}
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: AddAssignedDevices
// Desc: For every device that's assigned to a player, set it action map
// and add it to the game
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::AddAssignedDevices( BOOL bResetMappings )
{
DWORD iDevice;
DWORD iAction;
HRESULT hr = S_OK;
DWORD *pdwAppFixed = NULL;
// If flagged, we'll be remapping the actions to hardware defaults, and in
// the process DirectInput will clear the DIA_APPFIXED flag, so we need
// to make a copy in order to reapply the flag later.
if( bResetMappings )
{
pdwAppFixed = new DWORD[m_pdiaf->dwNumActions];
// Verify memory allocation and collect DIA_APPFIXED settings
if( pdwAppFixed )
{
for( iAction=0; iAction < m_pdiaf->dwNumActions; iAction++ )
pdwAppFixed[iAction] = m_pdiaf->rgoAction[iAction].dwFlags & DIA_APPFIXED;
}
}
// For every device that's assigned to a player,
// set it action map, and add it to the game
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;
if( pPlayerInfo != NULL )
{
// Set the device's coop level
hr = pdidDevice->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND );
if( FAILED(hr) )
break;
// Build and set the action map on this device. This will also remove
// it from DirectInput's internal list of available devices.
DWORD dwBuildFlags = bResetMappings ? DIDBAM_HWDEFAULTS : DIDBAM_DEFAULT;
DWORD dwSetFlags = bResetMappings ? DIDSAM_FORCESAVE : DIDSAM_DEFAULT;
hr = pdidDevice->BuildActionMap( m_pdiaf, pPlayerInfo->strPlayerName, dwBuildFlags );
if( FAILED( hr ) )
{
// just print out a debug message and keep going
DXTRACE_ERR( TEXT("BuildActionMap"), hr );
hr = S_OK;
continue;
}
hr = pdidDevice->SetActionMap( m_pdiaf, pPlayerInfo->strPlayerName, dwSetFlags );
if( FAILED( hr ) )
{
// just print out a debug message and keep going
DXTRACE_ERR( TEXT("SetActionMap"), hr );
hr = S_OK;
continue;
}
// Callback into the app so it can adjust the device and set
// the m_pDeviceList[iDevice].pParam field with a device state struct
if( m_AddDeviceCallback )
m_AddDeviceCallback( pPlayerInfo, &m_pDeviceList[iDevice],
&m_pDeviceList[iDevice].didi, m_AddDeviceCallbackParam );
// Check to see if the device is using relative axis -- sometimes app code
// might want to know this.
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = 0;
pdidDevice->GetProperty( DIPROP_AXISMODE, &dipdw.diph );
if( dipdw.dwData == DIPROPAXISMODE_REL )
m_pDeviceList[iDevice].bRelativeAxis = TRUE;
// We made it through this iteration without breaking out do to errors
hr = S_OK;
}
else
{
if( FAILED( hr = pdidDevice->BuildActionMap( m_pdiaf, NULL, DIDBAM_DEFAULT ) ) )
{
DXTRACE_ERR( TEXT("BuildActionMap"), hr );
break;
}
if( FAILED( hr = pdidDevice->SetActionMap( m_pdiaf, NULL, DIDSAM_NOUSER ) ) )
{
DXTRACE_ERR( TEXT("SetActionMap"), hr );
break;
}
}
}
// If we stored DIA_APPFIXED flags earlier, we need to reapply those flags and
// free the allocated memory
if( bResetMappings && pdwAppFixed )
{
for( iAction=0; iAction < m_pdiaf->dwNumActions; iAction++ )
m_pdiaf->rgoAction[iAction].dwFlags |= pdwAppFixed[iAction];
delete [] pdwAppFixed;
}
return hr;
}
//-----------------------------------------------------------------------------
// Name: ConfigureDevices
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::ConfigureDevices( HWND hWnd, IUnknown* pSurface,
VOID* ConfigureDevicesCB,
DWORD dwFlags, LPVOID pvCBParam )
{
HRESULT hr;
DWORD iPlayer;
// Determine how large of a string we'll need to hold all user names
DWORD dwNamesSize = 0;
for( iPlayer=0; iPlayer < m_dwNumUsers; iPlayer++ )
dwNamesSize += lstrlen( m_pUsers[iPlayer]->strPlayerName ) +1;
// Build multi-sz list of user names
TCHAR* strUserNames = new TCHAR[dwNamesSize+1];
// Verify allocation and cycle through user names
if( strUserNames )
{
TCHAR* strTemp = strUserNames;
for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
{
lstrcpy( strTemp, m_pUsers[iPlayer]->strPlayerName );
strTemp += lstrlen(strTemp) + 1;
}
lstrcpy( strTemp, TEXT("\0") );
}
// Fill in all the params
DICONFIGUREDEVICESPARAMS dicdp;
ZeroMemory(&dicdp, sizeof(dicdp));
dicdp.dwSize = sizeof(dicdp);
dicdp.dwcFormats = 1;
dicdp.lprgFormats = m_pdiaf;
dicdp.hwnd = hWnd;
dicdp.lpUnkDDSTarget = pSurface;
dicdp.dwcUsers = m_dwNumUsers;
dicdp.lptszUserNames = strUserNames;
// Initialize all the colors here
DICOLORSET dics;
ZeroMemory(&dics, sizeof(DICOLORSET));
dics.dwSize = sizeof(DICOLORSET);
// Set UI color scheme (if not specified it uses defaults)
dicdp.dics.dwSize = sizeof(dics);
dicdp.dics.cTextFore = D3DCOLOR_RGBA(255,255,255,255);
dicdp.dics.cTextHighlight = D3DCOLOR_RGBA(204,204,255,255);
dicdp.dics.cCalloutLine = D3DCOLOR_RGBA(255,255,255,128); //
dicdp.dics.cCalloutHighlight= D3DCOLOR_RGBA(204,204,255,255);
dicdp.dics.cBorder = D3DCOLOR_RGBA(153,153,204,128);
dicdp.dics.cControlFill = D3DCOLOR_RGBA( 51, 51, 102, 128); //
dicdp.dics.cHighlightFill = D3DCOLOR_RGBA(0,0,0,128);
dicdp.dics.cAreaFill = D3DCOLOR_RGBA(0,0,50,128);
if( dwFlags & DICD_EDIT )
{
// Re-enum so we can catch any new devices that have been recently attached
for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
if( FAILED( hr = BuildDeviceList() ) )
{
DXTRACE_ERR( TEXT("BuildDeviceList"), hr );
goto LCleanup;
}
}
// Unacquire the devices so that mouse doesn't
// control the game while in control panel
UnacquireDevices();
if( FAILED( hr = m_pDI->ConfigureDevices( (LPDICONFIGUREDEVICESCALLBACK)ConfigureDevicesCB,
&dicdp, dwFlags, pvCBParam ) ) )
{
DXTRACE_ERR( TEXT("ConfigureDevices"), hr );
goto LCleanup;
}
if( dwFlags & DICD_EDIT )
{
// Update the device ownership
if( FAILED( hr = UpdateDeviceOwnership() ) )
{
DXTRACE_ERR( TEXT("UpdateDeviceOwnership"), hr );
goto LCleanup;
}
// Now save the device keys that are assigned players to registry,
if( FAILED( hr = SaveDeviceOwnershipKeys() ) )
{
DXTRACE_ERR( TEXT("SaveDeviceOwnershipKeys"), hr );
goto LCleanup;
}
// Report an error if there is a player that doesn't not
// have a device assigned
if( FAILED( hr = VerifyAssignment() ) )
goto LCleanup;
// Now that every player has at least one device, build and set
// the action map, and use callback into the app to tell the
// app of the device assignment.
if( FAILED( hr = AddAssignedDevices( FALSE ) ) )
{
DXTRACE_ERR( TEXT("AddAssignedDevices"), hr );
goto LCleanup;
}
}
hr = S_OK;
LCleanup:
if( strUserNames )
delete [] strUserNames;
return hr;
}
//-----------------------------------------------------------------------------
// Name: UpdateDeviceOwnership
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::UpdateDeviceOwnership()
{
DWORD iPlayer;
DWORD iDevice;
// Set all players to not have a device yet
for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
// Set all devices as not assigned to a player
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
m_pDeviceList[iDevice].pPlayerInfo = NULL;
UnacquireDevices();
// Update the device ownership by quering the DIPROP_USERNAME property
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
TCHAR strPlayerName[MAX_PATH];
DIPROPSTRING dips;
dips.diph.dwSize = sizeof(DIPROPSTRING);
dips.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dips.diph.dwObj = 0; // device property
dips.diph.dwHow = DIPH_DEVICE;
pdidDevice->GetProperty( DIPROP_USERNAME, &dips.diph );
DXUtil_ConvertWideStringToGenericCch( strPlayerName, dips.wsz, MAX_PATH );
if( lstrcmp( strPlayerName, TEXT("") ) != 0 )
{
m_pDeviceList[iDevice].pPlayerInfo = LookupPlayer( strPlayerName );
if( m_pDeviceList[iDevice].pPlayerInfo )
m_pDeviceList[iDevice].pPlayerInfo->bFoundDeviceForPlayer = TRUE;
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: UnacquireDevices
// Desc:
//-----------------------------------------------------------------------------
VOID CMultiplayerInputDeviceManager::UnacquireDevices()
{
// Unacquire every device
if( m_pDeviceList )
{
// All devices have been assigned a to a user in
// the new array, so clean up the local array
for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
// Set the device's coop level
pdidDevice->Unacquire();
}
}
}
//-----------------------------------------------------------------------------
// Name: SetFocus
// Desc: Sets the DirectInput focus to a new HWND
//-----------------------------------------------------------------------------
VOID CMultiplayerInputDeviceManager::SetFocus( HWND hWnd )
{
m_hWnd = hWnd;
if( m_pDeviceList )
{
// All devices have been assigned a to a user in
// the new array, so clean up the local array
for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
// Set the device's coop level
pdidDevice->Unacquire();
pdidDevice->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND );
}
}
}
//-----------------------------------------------------------------------------
// Name: GetDevices
// Desc: returns an array of DeviceInfo*'s
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::GetDevices( DeviceInfo** ppDeviceInfo,
DWORD* pdwCount )
{
if( NULL==ppDeviceInfo || NULL==pdwCount )
return E_INVALIDARG;
(*ppDeviceInfo) = m_pDeviceList;
(*pdwCount) = m_dwNumDevices;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: LookupPlayer
// Desc: searchs m_pUsers by player name
//-----------------------------------------------------------------------------
CMultiplayerInputDeviceManager::PlayerInfo* CMultiplayerInputDeviceManager::LookupPlayer( TCHAR* strPlayerName )
{
for( DWORD iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
{
PlayerInfo* pCurPlayer = m_pUsers[iPlayer];
if( lstrcmp( pCurPlayer->strPlayerName, strPlayerName ) == 0 )
return pCurPlayer;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Name: SaveDeviceOwnershipKeys
// Desc: For every device that's assigned to a player, save its device key
// and assigned player to registry.
//-----------------------------------------------------------------------------
HRESULT CMultiplayerInputDeviceManager::SaveDeviceOwnershipKeys()
{
TCHAR strDeviceGuid[40];
DWORD iDevice;
for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
{
PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;
DXUtil_ConvertGUIDToStringCch( &m_pDeviceList[iDevice].didi.guidInstance, strDeviceGuid, 40 );
if( pPlayerInfo != NULL )
DXUtil_WriteStringRegKey( m_hKey, strDeviceGuid, pPlayerInfo->strPlayerName );
else
RegDeleteValue( m_hKey, strDeviceGuid );
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: DeleteDeviceOwnershipKeys
// Desc: Delete all the ownership keys
//-----------------------------------------------------------------------------
VOID CMultiplayerInputDeviceManager::DeleteDeviceOwnershipKeys()
{
HKEY hKey;
TCHAR *strRegKey;
// Prepare strings to delete the key
strRegKey = _tcsdup( m_strKey );
if( strRegKey == NULL )
return;
TCHAR* strTemp = _tcsrchr( strRegKey, TEXT('\\') );
// Unless the registry path string was malformed, we're ready to delete
// and recreate the key
if( strTemp )
{
*strTemp = 0;
strTemp++;
RegCloseKey( m_hKey );
// Delete the reg key
RegOpenKey( HKEY_CURRENT_USER, strRegKey, &hKey );
RegDeleteKey( hKey, strTemp );
RegCloseKey( hKey );
// Create the key again now that all the subkeys have been deleted
RegCreateKeyEx( HKEY_CURRENT_USER, m_strKey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL,
&m_hKey, NULL );
}
// Clean up memory allocation
free( strRegKey );
}
//-----------------------------------------------------------------------------
// Name: Cleanup
// Desc:
//-----------------------------------------------------------------------------
VOID CMultiplayerInputDeviceManager::Cleanup()
{
CleanupDeviceList();
if( m_pUsers )
{
for( DWORD iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
SAFE_DELETE( m_pUsers[iPlayer] );
SAFE_DELETE( m_pUsers );
}
// Release() base object
SAFE_RELEASE( m_pDI );
}
//-----------------------------------------------------------------------------
// Name: CleanupDeviceList
// Desc: Clean up the device array
//-----------------------------------------------------------------------------
VOID CMultiplayerInputDeviceManager::CleanupDeviceList()
{
for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
SAFE_RELEASE( m_pDeviceList[iDevice].pdidDevice );
free( m_pDeviceList );
m_pDeviceList = NULL;
m_dwMaxDevices = 0;
m_dwNumDevices = 0;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGUIDToStringCch()
// Desc: Converts a GUID to a string
// cchDestChar is the size in TCHARs of strDest. Be careful not to
// pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGUIDToStringCch( const GUID* pGuidSrc, TCHAR* strDest, int cchDestChar )
{
int nResult = _sntprintf( strDest, cchDestChar, TEXT("{%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}"),
pGuidSrc->Data1, pGuidSrc->Data2, pGuidSrc->Data3,
pGuidSrc->Data4[0], pGuidSrc->Data4[1],
pGuidSrc->Data4[2], pGuidSrc->Data4[3],
pGuidSrc->Data4[4], pGuidSrc->Data4[5],
pGuidSrc->Data4[6], pGuidSrc->Data4[7] );
if( nResult < 0 )
return E_FAIL;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertWideStringToAnsi()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
// CHAR string.
// cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertWideStringToAnsiCch( CHAR* strDestination, const WCHAR* wstrSource,
int cchDestChar )
{
if( strDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
return E_INVALIDARG;
int nResult = WideCharToMultiByte( CP_ACP, 0, wstrSource, -1, strDestination,
cchDestChar*sizeof(CHAR), NULL, NULL );
strDestination[cchDestChar-1] = 0;
if( nResult == 0 )
return E_FAIL;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToGeneric()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
// TCHAR string.
// cchDestChar is the size in TCHARs of tstrDestination. Be careful not to
// pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertWideStringToGenericCch( TCHAR* tstrDestination, const WCHAR* wstrSource,
int cchDestChar )
{
if( tstrDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
return E_INVALIDARG;
#ifdef _UNICODE
wcsncpy( tstrDestination, wstrSource, cchDestChar );
tstrDestination[cchDestChar-1] = L'\0';
return S_OK;
#else
return DXUtil_ConvertWideStringToAnsiCch( tstrDestination, wstrSource, cchDestChar );
#endif
}
//-----------------------------------------------------------------------------
// Name: DXUtil_ReadStringRegKeyCch()
// Desc: Helper function to read a registry key string
// cchDest is the size in TCHARs of strDest. Be careful not to
// pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadStringRegKeyCch( HKEY hKey, LPCTSTR strRegName, TCHAR* strDest,
DWORD cchDest, LPCTSTR strDefault )
{
DWORD dwType;
DWORD cbDest = cchDest * sizeof(TCHAR);
if( ERROR_SUCCESS != RegQueryValueEx( hKey, strRegName, 0, &dwType,
(BYTE*)strDest, &cbDest ) )
{
_tcsncpy( strDest, strDefault, cchDest );
strDest[cchDest-1] = 0;
return S_FALSE;
}
else
{
if( dwType != REG_SZ )
{
_tcsncpy( strDest, strDefault, cchDest );
strDest[cchDest-1] = 0;
return S_FALSE;
}
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: DXUtil_WriteStringRegKey()
// Desc: Helper function to write a registry key string
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteStringRegKey( HKEY hKey, LPCTSTR strRegName,
LPCTSTR strValue )
{
if( NULL == strValue )
return E_INVALIDARG;
DWORD cbValue = ((DWORD)_tcslen(strValue)+1) * sizeof(TCHAR);
if( ERROR_SUCCESS != RegSetValueEx( hKey, strRegName, 0, REG_SZ,
(BYTE*)strValue, cbValue ) )
return E_FAIL;
return S_OK;
}