// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdmisc.h"
#include "nel/misc/di_event_emitter.h"
#include "nel/misc/events.h"
#include "nel/misc/win_event_emitter.h"
//
#include "di_mouse_device.h"
#include "di_keyboard_device.h"
#include "di_game_device.h"
#ifdef NL_OS_WINDOWS
namespace NLMISC
{
static const char DirectInputLibName[] = "dinput8.dll";
/////////////////////////////////
// CDIEventEmitter statics //
/////////////////////////////////
HMODULE CDIEventEmitter::_DirectInputLibHandle = 0;
CDIEventEmitter::TPDirectInput8Create CDIEventEmitter::_PDirectInput8Create = NULL;
uint CDIEventEmitter::_NumCreatedInterfaces = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////
// CDIEventEmitter implementation //
////////////////////////////////////
//======================================================
CDIEventEmitter::CDIEventEmitter(HWND hwnd, CWinEventEmitter *we)
:
_hWnd(hwnd),
_WE(we),
_DInput8(NULL),
_Keyboard(NULL),
_Mouse(NULL),
_ButtonsFlags(noButton)
{
}
//======================================================
CDIEventEmitter::~CDIEventEmitter()
{
releaseMouse();
releaseKeyboard();
// release all devices
while (_DeviceServer.getNumDevices() != 0)
{
IInputDevice *dev = _DeviceServer.getDevice(0);
_DeviceServer.removeDevice(dev);
delete dev;
}
if (_DInput8) _DInput8->Release();
-- _NumCreatedInterfaces;
if (_NumCreatedInterfaces == 0) unloadLib();
}
//======================================================
CDIEventEmitter *CDIEventEmitter::create(HINSTANCE hinst, HWND hwnd, CWinEventEmitter *we) throw(EDirectInput)
{
if (!loadLib()) throw EDirectInputLibNotFound();
std::auto_ptr dxee(new CDIEventEmitter(hwnd, we));
HRESULT result = _PDirectInput8Create(hinst,
DIRECTINPUT_VERSION,
IID_IDirectInput8A,
(void **) &dxee->_DInput8,
NULL);
if (result != DI_OK) throw EDirectInputInitFailed();
// ok, everything's fine, commit changes
++_NumCreatedInterfaces;
return dxee.release();
}
//======================================================
bool CDIEventEmitter::loadLib()
{
if (_DirectInputLibHandle != 0) return true; // library already loaded ?
HMODULE handle = ::LoadLibrary(DirectInputLibName);
if (handle == 0) return false;
// try to get the creation function
TPDirectInput8Create cf = (TPDirectInput8Create) ::GetProcAddress(handle, "DirectInput8Create");
if (!cf)
{
::FreeLibrary(handle);
return false;
}
// commit changes
_DirectInputLibHandle = handle;
_PDirectInput8Create = cf;
return true;
}
//======================================================
void CDIEventEmitter::unloadLib()
{
nlassert(_DirectInputLibHandle != 0);
::FreeLibrary(_DirectInputLibHandle);
_DirectInputLibHandle = 0;
_PDirectInput8Create = NULL;
}
//======================================================
void CDIEventEmitter::poll(CEventServer *server)
{
if (_WE) _ButtonsFlags = _WE->buildFlags();
if (!server)
server=&_InternalServer;
_DeviceServer.poll(server);
}
//======================================================
TMouseButton CDIEventEmitter::buildButtonsFlags() const
{
uint mouseFlags;
uint keybFlags;
//
if (_Mouse) // takes the flags from the direct input mouse
{
mouseFlags = (_Mouse->getButton(0) ? leftButton : 0)
| (_Mouse->getButton(1) ? rightButton : 0)
| (_Mouse->getButton(2) ? middleButton : 0);
}
else // takes the flags from the system mouse
{
mouseFlags = _ButtonsFlags & (leftButton | rightButton | middleButton);
}
//
if (_Keyboard) // takes the flags from the direct input keyboard
{
keybFlags = (_Keyboard->ShiftPressed ? shiftButton : 0)
| (_Keyboard->AltPressed ? altButton : 0)
| (_Keyboard->CtrlPressed ? ctrlButton : 0);
}
else // takes the flags from the system keyboard
{
keybFlags = _ButtonsFlags & (shiftButton | altButton | ctrlButton);
}
return (TMouseButton) (keybFlags | mouseFlags);
}
//======================================================
IMouseDevice *CDIEventEmitter::getMouseDevice(bool hardware) throw(EInputDevice)
{
if (_Mouse) return _Mouse; // already created ?
try
{
// Create a mouse
std::auto_ptr mouse(CDIMouse::createMouseDevice(_DInput8, _hWnd, this, hardware, _WE));
// register to the device server
_DeviceServer.registerDevice(mouse.get());
_Mouse = mouse.get();
return mouse.release();
}
catch (...)
{
if (_WE) _WE->enableMouseEvents(true);
throw;
}
}
//======================================================
void CDIEventEmitter::releaseMouse()
{
if (!_Mouse) return;
// reupdate the system keyboard flags
if (_WE)
{
_WE->resetButtonFlagState();
_WE->enableMouseEvents(true);
}
// remove the device
_DeviceServer.removeDevice(_Mouse);
delete _Mouse;
_Mouse = NULL;
}
//===========================================================================
IKeyboardDevice *CDIEventEmitter::getKeyboardDevice() throw(EInputDevice)
{
if (_Keyboard) return _Keyboard;
try
{
// create a keyboard
std::auto_ptr keyboard(CDIKeyboard::createKeyboardDevice(_DInput8, _hWnd, this, _WE));
// register to the device server
_DeviceServer.registerDevice(keyboard.get());
_Keyboard = keyboard.get();
return keyboard.release();
}
catch (...)
{
if (_WE) _WE->enableKeyboardEvents(true);
throw;
}
}
//==========================================================================
void CDIEventEmitter::releaseKeyboard()
{
if (!_Keyboard) return;
// reupdate the system keyboard flags
if (_WE)
{
_WE->resetButtonFlagState();
_WE->enableKeyboardEvents(true);
}
//
_DeviceServer.removeDevice(_Keyboard);
delete _Keyboard;
_Keyboard = NULL;
}
//==========================================================================
void CDIEventEmitter::submitEvents(CEventServer &server, bool allWindows)
{
_InternalServer.setServer(&server);
_InternalServer.pump(allWindows);
}
//==========================================================================
void CDIEventEmitter::emulateMouseRawMode(bool enable)
{
nlerror("no raw mode emulation on windows, the CDIMouse has a real raw mode");
}
//==========================================================================
/// Tool fct to retrieve the game devices.
static BOOL CALLBACK DIEnumDevicesDescCallback
(
LPCDIDEVICEINSTANCE lpddi,
LPVOID pvRef
)
{
CGameDeviceDesc desc;
desc.InstanceName = lpddi->tszInstanceName;
desc.ProductName = lpddi->tszProductName;
switch (lpddi->wUsage & 0xff)
{
case DI8DEVTYPE_JOYSTICK: desc.DevType = CGameDeviceDesc::Joystick; break;
case DI8DEVTYPE_GAMEPAD: desc.DevType = CGameDeviceDesc::GamePad; break;
default: desc.DevType = CGameDeviceDesc::DontKnow; break;
}
TDeviceDescVect *dv = (TDeviceDescVect *) pvRef;
dv->push_back(desc);
return DIENUM_CONTINUE;
}
//==========================================================================
/// Tool fct to retrieve the game devices GUID
static BOOL CALLBACK DIEnumDevicesGUIDCallback
(
LPCDIDEVICEINSTANCE lpddi,
LPVOID pvRef
)
{
std::vector *gv = (std::vector *) pvRef;
gv->push_back(lpddi->guidInstance);
return DIENUM_CONTINUE;
}
//==========================================================================
void CDIEventEmitter::enumerateGameDevice(TDeviceDescVect &descs) throw(EInputDevice)
{
uint k;
nlassert(_DInput8);
descs.clear();
// enum all devices of interest
_DInput8->EnumDevices(DI8DEVCLASS_GAMECTRL, &DIEnumDevicesDescCallback, (LPVOID) &descs, DIEDFL_ALLDEVICES);
for (k = 0; k < descs.size(); ++k) descs[k].Connected = false;
// enum all connected devices
static TDeviceDescVect connecteds;
_DInput8->EnumDevices(DI8DEVCLASS_GAMECTRL, &DIEnumDevicesDescCallback, (LPVOID) &connecteds, DIEDFL_ATTACHEDONLY);
// see which devices are connected
for (k = 0; k < connecteds.size(); ++k)
{
TDeviceDescVect::iterator it = std::find(descs.begin(), descs.end(), connecteds[k]);
it->Connected = true;
}
}
//==========================================================================
IGameDevice *CDIEventEmitter::createGameDevice(const std::string &instanceName) throw(EInputDevice)
{
static TDeviceDescVect deviceDescs;
static std::vector deviceGUID;
nlassert(_DInput8);
enumerateGameDevice(deviceDescs);
// get the ID for each device
deviceGUID.clear();
HRESULT r = _DInput8->EnumDevices(DI8DEVCLASS_GAMECTRL, &DIEnumDevicesGUIDCallback, (LPVOID) &deviceGUID, DIEDFL_ALLDEVICES);
nlassert(r == DI_OK);
nlassert(deviceDescs.size() == deviceGUID.size());
// search the device that match the instance name
for (uint k = 0; k < deviceDescs.size(); ++k)
{
if (deviceDescs[k].InstanceName == instanceName)
{
std::auto_ptr gd(CDIGameDevice::createGameDevice(_DInput8, _hWnd, this, deviceDescs[k], deviceGUID[k]));
// insert in the device server
_DeviceServer.registerDevice(gd.get());
return gd.release();
}
}
return NULL;
}
//==========================================================================
void CDIEventEmitter::releaseGameDevice(IGameDevice *gd)
{
nlassert(gd);
CDIGameDevice *digd = safe_cast(gd);
_DeviceServer.removeDevice(digd);
delete gd;
}
} // NLMISC
#endif // NL_OS_WINDOWS