2566 lines
87 KiB
C++
Executable File
2566 lines
87 KiB
C++
Executable File
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "IrrCompileConfig.h"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
|
|
#include "CIrrDeviceWin32.h"
|
|
#include "IGUIEditBox.h"
|
|
#include "IGUIEnvironment.h"
|
|
#include "IGUIFont.h"
|
|
#include "IGUISkin.h"
|
|
#include "IEventReceiver.h"
|
|
#include "irrList.h"
|
|
#include "os.h"
|
|
|
|
#include "utils/string_utils.hpp"
|
|
#include "utils/utf8.h"
|
|
|
|
#include "CTimer.h"
|
|
#include "irrString.h"
|
|
#include "COSOperator.h"
|
|
#include "dimension2d.h"
|
|
#include "IGUISpriteBank.h"
|
|
#include <mmsystem.h>
|
|
#include <regstr.h>
|
|
#include <winuser.h>
|
|
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
|
|
// This define will switch to use XInput 9.1, which does not
|
|
// require an installer and works on most windows platforms.
|
|
// See https://blogs.msdn.microsoft.com/chuckw/2012/04/25/xinput-and-windows-8/
|
|
#undef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0601
|
|
// Don't change the case of xinput.h, otherwise cygwin compilation fails.
|
|
#include <xinput.h>
|
|
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
|
|
#define DIRECTINPUT_VERSION 0x0800
|
|
#include <dinput.h>
|
|
#include <wbemidl.h>
|
|
#include <oleauto.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "dinput8.lib")
|
|
#pragma comment(lib, "dxguid.lib")
|
|
#endif
|
|
#else
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "winmm.lib")
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#include <imm.h>
|
|
#pragma comment(lib, "imm32.lib")
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
|
|
IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, HWND window);
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
|
|
IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, HWND window);
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceWin32* device);
|
|
#endif
|
|
}
|
|
} // end namespace irr
|
|
|
|
namespace irr
|
|
{
|
|
struct SJoystickWin32Control
|
|
{
|
|
CIrrDeviceWin32* Device;
|
|
|
|
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_)
|
|
IDirectInput8* DirectInputDevice;
|
|
#endif
|
|
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
|
|
struct JoystickInfo
|
|
{
|
|
u32 Index;
|
|
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
|
|
core::stringc Name;
|
|
GUID guid;
|
|
LPDIRECTINPUTDEVICE8 lpdijoy;
|
|
DIDEVCAPS devcaps;
|
|
u8 axisValid[8];
|
|
#else
|
|
JOYCAPS Caps;
|
|
#endif
|
|
/** For xbox-360 controller. */
|
|
bool m_use_xinput;
|
|
};
|
|
core::array<JoystickInfo> ActiveJoysticks;
|
|
#endif
|
|
|
|
SJoystickWin32Control(CIrrDeviceWin32* dev) : Device(dev)
|
|
{
|
|
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_)
|
|
DirectInputDevice=0;
|
|
if (DI_OK != (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInputDevice, NULL)))
|
|
{
|
|
os::Printer::log("Could not create DirectInput8 Object", ELL_WARNING);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
~SJoystickWin32Control()
|
|
{
|
|
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_)
|
|
for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
|
|
{
|
|
LPDIRECTINPUTDEVICE8 dev = ActiveJoysticks[joystick].lpdijoy;
|
|
if (dev)
|
|
{
|
|
dev->Unacquire();
|
|
}
|
|
dev->Release();
|
|
}
|
|
|
|
if (DirectInputDevice)
|
|
DirectInputDevice->Release();
|
|
#endif
|
|
}
|
|
|
|
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_)
|
|
static BOOL CALLBACK EnumJoysticks(LPCDIDEVICEINSTANCE lpddi, LPVOID cp)
|
|
{
|
|
SJoystickWin32Control* p=(SJoystickWin32Control*)cp;
|
|
p->directInputAddJoystick(lpddi);
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
bool IsXInputDevice(const GUID* pGuidProductFromDirectInput);
|
|
|
|
void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi)
|
|
{
|
|
//Get the GUID of the joystuck
|
|
const GUID guid = lpddi->guidInstance;
|
|
JoystickInfo activeJoystick;
|
|
activeJoystick.m_use_xinput = isXInputDevice(&lpddi->guidProduct);
|
|
|
|
activeJoystick.Index=ActiveJoysticks.size();
|
|
activeJoystick.guid=guid;
|
|
activeJoystick.Name=StringUtils::wideToUtf8(lpddi->tszProductName).c_str();
|
|
if (FAILED(DirectInputDevice->CreateDevice(guid, &activeJoystick.lpdijoy, NULL)))
|
|
{
|
|
os::Printer::log("Could not create DirectInput device", ELL_WARNING);
|
|
return;
|
|
}
|
|
|
|
activeJoystick.devcaps.dwSize=sizeof(activeJoystick.devcaps);
|
|
if (FAILED(activeJoystick.lpdijoy->GetCapabilities(&activeJoystick.devcaps)))
|
|
{
|
|
os::Printer::log("Could not create DirectInput device", ELL_WARNING);
|
|
return;
|
|
}
|
|
|
|
if (FAILED(activeJoystick.lpdijoy->SetCooperativeLevel(Device->HWnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE)))
|
|
{
|
|
os::Printer::log("Could not set DirectInput device cooperative level", ELL_WARNING);
|
|
return;
|
|
}
|
|
|
|
if (FAILED(activeJoystick.lpdijoy->SetDataFormat(&c_dfDIJoystick2)))
|
|
{
|
|
os::Printer::log("Could not set DirectInput device data format", ELL_WARNING);
|
|
return;
|
|
}
|
|
|
|
if (FAILED(activeJoystick.lpdijoy->Acquire()))
|
|
{
|
|
os::Printer::log("Could not set DirectInput cooperative level", ELL_WARNING);
|
|
return;
|
|
}
|
|
|
|
DIJOYSTATE2 info;
|
|
if (FAILED(activeJoystick.lpdijoy->GetDeviceState(sizeof(info),&info)))
|
|
{
|
|
os::Printer::log("Could not read DirectInput device state", ELL_WARNING);
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(activeJoystick.axisValid,sizeof(activeJoystick.axisValid));
|
|
activeJoystick.axisValid[0]= (info.lX!=0) ? 1 : 0;
|
|
activeJoystick.axisValid[1]= (info.lY!=0) ? 1 : 0;
|
|
activeJoystick.axisValid[2]= (info.lZ!=0) ? 1 : 0;
|
|
activeJoystick.axisValid[3]= (info.lRx!=0) ? 1 : 0;
|
|
activeJoystick.axisValid[4]= (info.lRy!=0) ? 1 : 0;
|
|
activeJoystick.axisValid[5]= (info.lRz!=0) ? 1 : 0;
|
|
|
|
int caxis=0;
|
|
for (u8 i=0; i<6; i++)
|
|
{
|
|
if (activeJoystick.axisValid[i])
|
|
caxis++;
|
|
}
|
|
|
|
for (u8 i=0; i<(activeJoystick.devcaps.dwAxes)-caxis; i++)
|
|
{
|
|
if (i+caxis < 8)
|
|
activeJoystick.axisValid[i+caxis]=1;
|
|
}
|
|
|
|
// On Xbox 360 devices left and right trigger are reported as two
|
|
// different axes (instead of 1 with DirectInput), so we need one
|
|
// additional axis:
|
|
if (activeJoystick.m_use_xinput && activeJoystick.devcaps.dwAxes == 5)
|
|
activeJoystick.devcaps.dwAxes = 6;
|
|
ActiveJoysticks.push_back(activeJoystick);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Taken from:
|
|
* https://msdn.microsoft.com/en-us/library/windows/desktop/dd940435(v=vs.85).aspx
|
|
*/
|
|
template <class T> void SAFE_RELEASE(T **ppT)
|
|
{
|
|
if (*ppT)
|
|
{
|
|
(*ppT)->Release();
|
|
*ppT = NULL;
|
|
}
|
|
}
|
|
|
|
/** Taken from:
|
|
* https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
// Enum each PNP device using WMI and check each device ID to see if it contains
|
|
// "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device
|
|
// Unfortunately this information can not be found by just using DirectInput
|
|
//-----------------------------------------------------------------------------
|
|
bool isXInputDevice(const GUID* pGuidProductFromDirectInput)
|
|
{
|
|
IWbemLocator* pIWbemLocator = NULL;
|
|
IEnumWbemClassObject* pEnumDevices = NULL;
|
|
IWbemClassObject* pDevices[20] = { 0 };
|
|
IWbemServices* pIWbemServices = NULL;
|
|
BSTR bstrNamespace = NULL;
|
|
BSTR bstrDeviceID = NULL;
|
|
BSTR bstrClassName = NULL;
|
|
DWORD uReturned = 0;
|
|
bool bIsXinputDevice = false;
|
|
UINT iDevice = 0;
|
|
VARIANT var;
|
|
HRESULT hr;
|
|
|
|
// CoInit if needed
|
|
hr = CoInitialize(NULL);
|
|
bool bCleanupCOM = SUCCEEDED(hr);
|
|
|
|
// Create WMI
|
|
hr = CoCreateInstance(__uuidof(WbemLocator),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IWbemLocator),
|
|
(LPVOID*)&pIWbemLocator);
|
|
if (FAILED(hr) || pIWbemLocator == NULL)
|
|
goto LCleanup;
|
|
|
|
bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) goto LCleanup;
|
|
bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == NULL) goto LCleanup;
|
|
bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == NULL) goto LCleanup;
|
|
|
|
// Connect to WMI
|
|
hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L,
|
|
0L, NULL, NULL, &pIWbemServices);
|
|
if (FAILED(hr) || pIWbemServices == NULL)
|
|
goto LCleanup;
|
|
|
|
// Switch security level to IMPERSONATE.
|
|
CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
|
|
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
|
|
|
|
hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices);
|
|
if (FAILED(hr) || pEnumDevices == NULL)
|
|
goto LCleanup;
|
|
|
|
// Loop over all devices
|
|
for (;; )
|
|
{
|
|
// Get 20 at a time
|
|
hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned);
|
|
if (FAILED(hr))
|
|
goto LCleanup;
|
|
if (uReturned == 0)
|
|
break;
|
|
|
|
for (iDevice = 0; iDevice<uReturned; iDevice++)
|
|
{
|
|
// For each device, get its device ID
|
|
hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL);
|
|
if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL)
|
|
{
|
|
// Check if the device ID contains "IG_". If it does, then it's an XInput device
|
|
// This information can not be found from DirectInput
|
|
if (wcsstr(var.bstrVal, L"IG_"))
|
|
{
|
|
// If it does, then get the VID/PID from var.bstrVal
|
|
DWORD dwPid = 0, dwVid = 0;
|
|
WCHAR* strVid = wcsstr(var.bstrVal, L"VID_");
|
|
if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1)
|
|
dwVid = 0;
|
|
WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
|
|
if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1)
|
|
dwPid = 0;
|
|
|
|
// Compare the VID/PID to the DInput device
|
|
DWORD dwVidPid = MAKELONG(dwVid, dwPid);
|
|
if (dwVidPid == pGuidProductFromDirectInput->Data1)
|
|
{
|
|
bIsXinputDevice = true;
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
}
|
|
SAFE_RELEASE(&pDevices[iDevice]);
|
|
}
|
|
}
|
|
|
|
LCleanup:
|
|
if (bstrNamespace)
|
|
SysFreeString(bstrNamespace);
|
|
if (bstrDeviceID)
|
|
SysFreeString(bstrDeviceID);
|
|
if (bstrClassName)
|
|
SysFreeString(bstrClassName);
|
|
for (iDevice = 0; iDevice<20; iDevice++)
|
|
SAFE_RELEASE(&pDevices[iDevice]);
|
|
SAFE_RELEASE(&pEnumDevices);
|
|
SAFE_RELEASE(&pIWbemLocator);
|
|
SAFE_RELEASE(&pIWbemServices);
|
|
|
|
if (bCleanupCOM)
|
|
CoUninitialize();
|
|
|
|
return bIsXinputDevice;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
#endif
|
|
|
|
void pollJoysticks()
|
|
{
|
|
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
|
|
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
|
|
if (0 == ActiveJoysticks.size())
|
|
return;
|
|
|
|
u32 joystick;
|
|
DIJOYSTATE2 info;
|
|
|
|
for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
|
|
{
|
|
SEvent event;
|
|
|
|
event.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
|
|
event.JoystickEvent.Joystick = (u8)joystick;
|
|
|
|
if (ActiveJoysticks[joystick].m_use_xinput)
|
|
{
|
|
XINPUT_STATE state;
|
|
memset(&state, 0, sizeof(state));
|
|
DWORD result = XInputGetState(ActiveJoysticks[joystick].Index, &state);
|
|
// XInput reports the buttons in a different order. So to keep
|
|
// old configs to work as expected, remap the buttons.
|
|
// Map to 0x001 - 0x008
|
|
int abxy = (state.Gamepad.wButtons & (XINPUT_GAMEPAD_A |
|
|
XINPUT_GAMEPAD_B |
|
|
XINPUT_GAMEPAD_X |
|
|
XINPUT_GAMEPAD_Y )
|
|
) >> 12;
|
|
// Map to 0x010 - 0x020
|
|
int shoulder = (state.Gamepad.wButtons & (XINPUT_GAMEPAD_LEFT_SHOULDER|
|
|
XINPUT_GAMEPAD_RIGHT_SHOULDER)
|
|
) >> 4;
|
|
// Map to 0x040
|
|
int start = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) << 3;
|
|
// Map to 0x080
|
|
int back = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK ) << 1;
|
|
// Map to 0x100 to 0x200
|
|
int stick_buttons = (state.Gamepad.wButtons & (XINPUT_GAMEPAD_LEFT_THUMB |
|
|
XINPUT_GAMEPAD_RIGHT_THUMB)
|
|
) << 2;
|
|
|
|
event.JoystickEvent.ButtonStates = abxy | shoulder | start | back | stick_buttons;
|
|
int angle = 65535;
|
|
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
|
|
{
|
|
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) angle = 22500;
|
|
else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) angle = 13500;
|
|
else angle = 18000;
|
|
}
|
|
else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
|
|
{
|
|
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) angle = 31500;
|
|
else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) angle = 4500;
|
|
else angle = 0;
|
|
}
|
|
else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) angle = 9000;
|
|
else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) angle = 27000;
|
|
event.JoystickEvent.POV = angle;
|
|
// Map the axis as they were previously, so existing configs
|
|
// still work as expected. The Y axis needs to be reversed:
|
|
// -32768 --> 32767, ..., 32767 --> -32768
|
|
// Inverting the bits with ~ does that (-sThumbLY would map -32768 to -32768!!)
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = state.Gamepad.sThumbLX;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = ~state.Gamepad.sThumbLY;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = state.Gamepad.bLeftTrigger * 128;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = ~state.Gamepad.sThumbRY;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = state.Gamepad.sThumbRX;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = state.Gamepad.bRightTrigger * 128;
|
|
(void)Device->postEventFromUser(event);
|
|
|
|
continue;
|
|
}
|
|
// needs to be reset for each joystick
|
|
// request ALL values and POV as continuous if possible
|
|
|
|
const DIDEVCAPS & caps = ActiveJoysticks[joystick].devcaps;
|
|
// if no POV is available don't ask for POV values
|
|
|
|
if (!FAILED(ActiveJoysticks[joystick].lpdijoy->GetDeviceState(sizeof(info),&info)))
|
|
{
|
|
event.JoystickEvent.POV = (u16)info.rgdwPOV[0];
|
|
// set to undefined if no POV value was returned or the value
|
|
// is out of range
|
|
if ((caps.dwPOVs==0) || (event.JoystickEvent.POV > 35900))
|
|
event.JoystickEvent.POV = 65535;
|
|
|
|
for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis)
|
|
event.JoystickEvent.Axis[axis] = 0;
|
|
|
|
u16 dxAxis=0;
|
|
u16 irrAxis=0;
|
|
|
|
while (dxAxis < 6 && irrAxis <caps.dwAxes)
|
|
{
|
|
bool axisFound=0;
|
|
s32 axisValue=0;
|
|
|
|
switch (dxAxis)
|
|
{
|
|
case 0:
|
|
axisValue=info.lX;
|
|
break;
|
|
case 1:
|
|
axisValue=info.lY;
|
|
break;
|
|
case 2:
|
|
axisValue=info.lZ;
|
|
break;
|
|
case 3:
|
|
axisValue=info.lRx;
|
|
break;
|
|
case 4:
|
|
axisValue=info.lRy;
|
|
break;
|
|
case 5:
|
|
axisValue=info.lRz;
|
|
break;
|
|
case 6:
|
|
axisValue=info.rglSlider[0];
|
|
break;
|
|
case 7:
|
|
axisValue=info.rglSlider[1];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ActiveJoysticks[joystick].axisValid[dxAxis]>0)
|
|
axisFound=1;
|
|
|
|
if (axisFound)
|
|
{
|
|
s32 val=axisValue - 32768;
|
|
|
|
if (val <-32767) val=-32767;
|
|
if (val > 32767) val=32767;
|
|
event.JoystickEvent.Axis[irrAxis]=(s16)(val);
|
|
irrAxis++;
|
|
}
|
|
|
|
dxAxis++;
|
|
}
|
|
|
|
u32 buttons=0;
|
|
BYTE* bytebuttons=info.rgbButtons;
|
|
for (u16 i=0; i<32; i++)
|
|
{
|
|
if (bytebuttons[i] >0)
|
|
{
|
|
buttons |= (1 << i);
|
|
}
|
|
}
|
|
event.JoystickEvent.ButtonStates = buttons;
|
|
|
|
(void)Device->postEventFromUser(event);
|
|
}
|
|
}
|
|
#else
|
|
u32 joystick;
|
|
|
|
for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
|
|
{
|
|
SEvent event;
|
|
|
|
event.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
|
|
event.JoystickEvent.Joystick = (u8)joystick;
|
|
for (int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis)
|
|
event.JoystickEvent.Axis[axis] = 0;
|
|
|
|
if (ActiveJoysticks[joystick].m_use_xinput)
|
|
{
|
|
XINPUT_STATE state;
|
|
memset(&state, 0, sizeof(state));
|
|
DWORD result = XInputGetState(ActiveJoysticks[joystick].Index, &state);
|
|
event.JoystickEvent.ButtonStates = state.Gamepad.wButtons;
|
|
// Thumb values are in [-32768, 32767]
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = state.Gamepad.sThumbRX;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = state.Gamepad.sThumbRY;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = state.Gamepad.sThumbLX;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = state.Gamepad.sThumbLY;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = state.Gamepad.bLeftTrigger*128;
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = state.Gamepad.bRightTrigger*128;
|
|
}
|
|
else // old API
|
|
{
|
|
JOYINFOEX info;
|
|
// needs to be reset for each joystick
|
|
// request ALL values and POV as continuous if possible
|
|
info.dwSize = sizeof(info);
|
|
info.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS;
|
|
const JOYCAPS & caps = ActiveJoysticks[joystick].Caps;
|
|
// if no POV is available don't ask for POV values
|
|
if (!(caps.wCaps & JOYCAPS_HASPOV))
|
|
info.dwFlags &= ~(JOY_RETURNPOV | JOY_RETURNPOVCTS);
|
|
if (joyGetPosEx(ActiveJoysticks[joystick].Index, &info) != JOYERR_NOERROR)
|
|
continue;
|
|
|
|
|
|
event.JoystickEvent.POV = (u16)info.dwPOV;
|
|
// set to undefined if no POV value was returned or the value
|
|
// is out of range
|
|
if (!(info.dwFlags & JOY_RETURNPOV) || (event.JoystickEvent.POV > 35900))
|
|
event.JoystickEvent.POV = 65535;
|
|
|
|
event.JoystickEvent.ButtonStates = info.dwButtons;
|
|
|
|
switch(caps.wNumAxes)
|
|
{
|
|
default:
|
|
case 6:
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] =
|
|
(s16)((65535 * (info.dwVpos - caps.wVmin)) / (caps.wVmax - caps.wVmin) - 32768);
|
|
|
|
case 5:
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] =
|
|
(s16)((65535 * (info.dwUpos - caps.wUmin)) / (caps.wUmax - caps.wUmin) - 32768);
|
|
|
|
case 4:
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] =
|
|
(s16)((65535 * (info.dwRpos - caps.wRmin)) / (caps.wRmax - caps.wRmin) - 32768);
|
|
|
|
case 3:
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] =
|
|
(s16)((65535 * (info.dwZpos - caps.wZmin)) / (caps.wZmax - caps.wZmin) - 32768);
|
|
|
|
case 2:
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] =
|
|
(s16)((65535 * (info.dwYpos - caps.wYmin)) / (caps.wYmax - caps.wYmin) - 32768);
|
|
|
|
case 1:
|
|
event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] =
|
|
(s16)((65535 * (info.dwXpos - caps.wXmin)) / (caps.wXmax - caps.wXmin) - 32768);
|
|
} // switch
|
|
} // if XInput ... else
|
|
(void)Device->postEventFromUser(event);
|
|
} // for all joysticks
|
|
#endif
|
|
#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
|
|
}
|
|
|
|
/** This function is ported from SDL and released under zlip:
|
|
* Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
|
|
*/
|
|
void setJoystickName(int index, const JOYCAPS &caps, SJoystickInfo *joystick)
|
|
{
|
|
// As a default use the name given in the joystick structure
|
|
// - though that is always the same name, independent of joystick :(
|
|
joystick->Name = StringUtils::wideToUtf8(caps.szPname).c_str();
|
|
joystick->HasGenericName = true;
|
|
|
|
core::stringw key = core::stringw(REGSTR_PATH_JOYCONFIG)+"\\"+caps.szRegKey
|
|
+ "\\"+REGSTR_KEY_JOYCURR;
|
|
HKEY hTopKey = HKEY_LOCAL_MACHINE;
|
|
HKEY hKey;
|
|
long regresult = RegOpenKeyEx(hTopKey, key.c_str(), 0, KEY_READ, &hKey);
|
|
if (regresult != ERROR_SUCCESS)
|
|
{
|
|
hTopKey = HKEY_CURRENT_USER;
|
|
regresult = RegOpenKeyEx(hTopKey, key.c_str(), 0, KEY_READ, &hKey);
|
|
}
|
|
if (regresult != ERROR_SUCCESS) return;
|
|
|
|
/* find the registry key name for the joystick's properties */
|
|
wchar_t regname[256];
|
|
DWORD regsize = sizeof(regname);
|
|
core::stringw regvalue = core::stringw(L"Joystick")+core::stringw(index+1)
|
|
+ REGSTR_VAL_JOYOEMNAME;
|
|
regresult = RegQueryValueEx(hKey, regvalue.c_str(), 0, 0,
|
|
(LPBYTE)regname, ®size);
|
|
RegCloseKey(hKey);
|
|
if (regresult != ERROR_SUCCESS) return;
|
|
|
|
/* open that registry key */
|
|
core::stringw regkey = core::stringw(REGSTR_PATH_JOYOEM)+"\\"+regname;
|
|
regresult = RegOpenKeyEx(hTopKey, regkey.c_str(), 0, KEY_READ, &hKey);
|
|
if (regresult != ERROR_SUCCESS) return;
|
|
|
|
/* find the size for the OEM name text */
|
|
regsize = sizeof(regvalue);
|
|
regresult = RegQueryValueEx(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0,
|
|
NULL, ®size);
|
|
if (regresult == ERROR_SUCCESS)
|
|
{
|
|
char *name;
|
|
/* allocate enough memory for the OEM name text ... */
|
|
name = new char[regsize];
|
|
if (name)
|
|
{
|
|
/* ... and read it from the registry */
|
|
regresult = RegQueryValueEx(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0,
|
|
(LPBYTE)name, ®size );
|
|
joystick->Name = name;
|
|
joystick->HasGenericName = false;
|
|
} // if name
|
|
} // if SUCCESS
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
|
|
bool activateJoysticks(core::array<SJoystickInfo> & joystickInfo)
|
|
{
|
|
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
|
|
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
|
|
if (!DirectInputDevice || (DirectInputDevice->EnumDevices(DI8DEVCLASS_GAMECTRL, SJoystickWin32Control::EnumJoysticks, this, DIEDFL_ATTACHEDONLY )))
|
|
{
|
|
os::Printer::log("Could not enum DirectInput8 controllers", ELL_WARNING);
|
|
return false;
|
|
}
|
|
|
|
for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
|
|
{
|
|
JoystickInfo& activeJoystick = ActiveJoysticks[joystick];
|
|
SJoystickInfo info;
|
|
info.Axes=activeJoystick.devcaps.dwAxes;
|
|
info.Buttons=activeJoystick.devcaps.dwButtons;
|
|
info.Name=activeJoystick.Name;
|
|
info.PovHat = (activeJoystick.devcaps.dwPOVs != 0)
|
|
? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT;
|
|
joystickInfo.push_back(info);
|
|
}
|
|
return true;
|
|
#else
|
|
joystickInfo.clear();
|
|
ActiveJoysticks.clear();
|
|
|
|
// This number includes XInput and legacy devices
|
|
const u32 numberOfJoysticks = ::joyGetNumDevs();
|
|
JOYINFOEX info;
|
|
info.dwSize = sizeof(info);
|
|
info.dwFlags = JOY_RETURNALL;
|
|
|
|
JoystickInfo activeJoystick;
|
|
SJoystickInfo returnInfo;
|
|
|
|
joystickInfo.reallocate(numberOfJoysticks);
|
|
ActiveJoysticks.reallocate(numberOfJoysticks);
|
|
|
|
// First discover all Xbox 360 controllers, which need to use
|
|
// XInput to get the state (otherwise the two triggers can not
|
|
// be used at the same time).
|
|
u32 joystick = 0;
|
|
for (int i = 0; i < XUSER_MAX_COUNT; i++)
|
|
{
|
|
XINPUT_STATE state;
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
DWORD result = XInputGetState(i, &state);
|
|
// Not connected or not an XInput device
|
|
if (result != ERROR_SUCCESS) continue;
|
|
|
|
activeJoystick.Index = i;
|
|
activeJoystick.m_use_xinput = true;
|
|
ActiveJoysticks.push_back(activeJoystick);
|
|
|
|
// This information is returned to the calling program
|
|
SJoystickInfo joy_info;
|
|
// 2 sticks with 2 directions each plus two triggers
|
|
joy_info.Axes = 6;
|
|
joy_info.Buttons = 30;
|
|
joy_info.Name = "XBOX 360 Gamepad";
|
|
joy_info.HasGenericName = false;
|
|
joy_info.PovHat = SJoystickInfo::POV_HAT_PRESENT;
|
|
joystickInfo.push_back(joy_info);
|
|
} // for i < XUSER_MAX_COUNT
|
|
|
|
for(; joystick < numberOfJoysticks; ++joystick)
|
|
{
|
|
if(JOYERR_NOERROR == joyGetPosEx(joystick, &info)
|
|
&&
|
|
JOYERR_NOERROR == joyGetDevCaps(joystick,
|
|
&activeJoystick.Caps,
|
|
sizeof(activeJoystick.Caps)))
|
|
{
|
|
setJoystickName(joystick, activeJoystick.Caps, &returnInfo);
|
|
core::stringc low_name = returnInfo.Name.make_lower();
|
|
// Ignore xbox controller, which are handled using XInput above
|
|
if (low_name.find("xbox") != -1) continue;
|
|
|
|
activeJoystick.Index = joystick;
|
|
activeJoystick.m_use_xinput = false;
|
|
ActiveJoysticks.push_back(activeJoystick);
|
|
returnInfo.Joystick = (u8)joystick;
|
|
returnInfo.Axes = activeJoystick.Caps.wNumAxes;
|
|
returnInfo.Buttons = activeJoystick.Caps.wNumButtons;
|
|
returnInfo.PovHat = ((activeJoystick.Caps.wCaps & JOYCAPS_HASPOV) == JOYCAPS_HASPOV)
|
|
? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT;
|
|
|
|
joystickInfo.push_back(returnInfo);
|
|
}
|
|
}
|
|
|
|
for(joystick = 0; joystick < joystickInfo.size(); ++joystick)
|
|
{
|
|
char logString[256];
|
|
(void)sprintf(logString, "Found joystick %d, %d axes, %d buttons '%s'",
|
|
joystick, joystickInfo[joystick].Axes,
|
|
joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str());
|
|
os::Printer::log(logString, ELL_INFORMATION);
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
#else
|
|
return false;
|
|
#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
|
|
}
|
|
};
|
|
} // end namespace irr
|
|
|
|
// Get the codepage from the locale language id
|
|
// Based on the table from http://www.science.co.il/Language/Locale-Codes.asp?s=decimal
|
|
static unsigned int LocaleIdToCodepage(unsigned int lcid)
|
|
{
|
|
switch ( lcid )
|
|
{
|
|
case 1098: // Telugu
|
|
case 1095: // Gujarati
|
|
case 1094: // Punjabi
|
|
case 1103: // Sanskrit
|
|
case 1111: // Konkani
|
|
case 1114: // Syriac
|
|
case 1099: // Kannada
|
|
case 1102: // Marathi
|
|
case 1125: // Divehi
|
|
case 1067: // Armenian
|
|
case 1081: // Hindi
|
|
case 1079: // Georgian
|
|
case 1097: // Tamil
|
|
return 0;
|
|
case 1054: // Thai
|
|
return 874;
|
|
case 1041: // Japanese
|
|
return 932;
|
|
case 2052: // Chinese (PRC)
|
|
case 4100: // Chinese (Singapore)
|
|
return 936;
|
|
case 1042: // Korean
|
|
return 949;
|
|
case 5124: // Chinese (Macau S.A.R.)
|
|
case 3076: // Chinese (Hong Kong S.A.R.)
|
|
case 1028: // Chinese (Taiwan)
|
|
return 950;
|
|
case 1048: // Romanian
|
|
case 1060: // Slovenian
|
|
case 1038: // Hungarian
|
|
case 1051: // Slovak
|
|
case 1045: // Polish
|
|
case 1052: // Albanian
|
|
case 2074: // Serbian (Latin)
|
|
case 1050: // Croatian
|
|
case 1029: // Czech
|
|
return 1250;
|
|
case 1104: // Mongolian (Cyrillic)
|
|
case 1071: // FYRO Macedonian
|
|
case 2115: // Uzbek (Cyrillic)
|
|
case 1058: // Ukrainian
|
|
case 2092: // Azeri (Cyrillic)
|
|
case 1092: // Tatar
|
|
case 1087: // Kazakh
|
|
case 1059: // Belarusian
|
|
case 1088: // Kyrgyz (Cyrillic)
|
|
case 1026: // Bulgarian
|
|
case 3098: // Serbian (Cyrillic)
|
|
case 1049: // Russian
|
|
return 1251;
|
|
case 8201: // English (Jamaica)
|
|
case 3084: // French (Canada)
|
|
case 1036: // French (France)
|
|
case 5132: // French (Luxembourg)
|
|
case 5129: // English (New Zealand)
|
|
case 6153: // English (Ireland)
|
|
case 1043: // Dutch (Netherlands)
|
|
case 9225: // English (Caribbean)
|
|
case 4108: // French (Switzerland)
|
|
case 4105: // English (Canada)
|
|
case 1110: // Galician
|
|
case 10249: // English (Belize)
|
|
case 3079: // German (Austria)
|
|
case 6156: // French (Monaco)
|
|
case 12297: // English (Zimbabwe)
|
|
case 1069: // Basque
|
|
case 2067: // Dutch (Belgium)
|
|
case 2060: // French (Belgium)
|
|
case 1035: // Finnish
|
|
case 1080: // Faroese
|
|
case 1031: // German (Germany)
|
|
case 3081: // English (Australia)
|
|
case 1033: // English (United States)
|
|
case 2057: // English (United Kingdom)
|
|
case 1027: // Catalan
|
|
case 11273: // English (Trinidad)
|
|
case 7177: // English (South Africa)
|
|
case 1030: // Danish
|
|
case 13321: // English (Philippines)
|
|
case 15370: // Spanish (Paraguay)
|
|
case 9226: // Spanish (Colombia)
|
|
case 5130: // Spanish (Costa Rica)
|
|
case 7178: // Spanish (Dominican Republic)
|
|
case 12298: // Spanish (Ecuador)
|
|
case 17418: // Spanish (El Salvador)
|
|
case 4106: // Spanish (Guatemala)
|
|
case 18442: // Spanish (Honduras)
|
|
case 3082: // Spanish (International Sort)
|
|
case 13322: // Spanish (Chile)
|
|
case 19466: // Spanish (Nicaragua)
|
|
case 2058: // Spanish (Mexico)
|
|
case 10250: // Spanish (Peru)
|
|
case 20490: // Spanish (Puerto Rico)
|
|
case 1034: // Spanish (Traditional Sort)
|
|
case 14346: // Spanish (Uruguay)
|
|
case 8202: // Spanish (Venezuela)
|
|
case 1089: // Swahili
|
|
case 1053: // Swedish
|
|
case 2077: // Swedish (Finland)
|
|
case 5127: // German (Liechtenstein)
|
|
case 1078: // Afrikaans
|
|
case 6154: // Spanish (Panama)
|
|
case 4103: // German (Luxembourg)
|
|
case 16394: // Spanish (Bolivia)
|
|
case 2055: // German (Switzerland)
|
|
case 1039: // Icelandic
|
|
case 1057: // Indonesian
|
|
case 1040: // Italian (Italy)
|
|
case 2064: // Italian (Switzerland)
|
|
case 2068: // Norwegian (Nynorsk)
|
|
case 11274: // Spanish (Argentina)
|
|
case 1046: // Portuguese (Brazil)
|
|
case 1044: // Norwegian (Bokmal)
|
|
case 1086: // Malay (Malaysia)
|
|
case 2110: // Malay (Brunei Darussalam)
|
|
case 2070: // Portuguese (Portugal)
|
|
return 1252;
|
|
case 1032: // Greek
|
|
return 1253;
|
|
case 1091: // Uzbek (Latin)
|
|
case 1068: // Azeri (Latin)
|
|
case 1055: // Turkish
|
|
return 1254;
|
|
case 1037: // Hebrew
|
|
return 1255;
|
|
case 5121: // Arabic (Algeria)
|
|
case 15361: // Arabic (Bahrain)
|
|
case 9217: // Arabic (Yemen)
|
|
case 3073: // Arabic (Egypt)
|
|
case 2049: // Arabic (Iraq)
|
|
case 11265: // Arabic (Jordan)
|
|
case 13313: // Arabic (Kuwait)
|
|
case 12289: // Arabic (Lebanon)
|
|
case 4097: // Arabic (Libya)
|
|
case 6145: // Arabic (Morocco)
|
|
case 8193: // Arabic (Oman)
|
|
case 16385: // Arabic (Qatar)
|
|
case 1025: // Arabic (Saudi Arabia)
|
|
case 10241: // Arabic (Syria)
|
|
case 14337: // Arabic (U.A.E.)
|
|
case 1065: // Farsi
|
|
case 1056: // Urdu
|
|
case 7169: // Arabic (Tunisia)
|
|
return 1256;
|
|
case 1061: // Estonian
|
|
case 1062: // Latvian
|
|
case 1063: // Lithuanian
|
|
return 1257;
|
|
case 1066: // Vietnamese
|
|
return 1258;
|
|
}
|
|
return 65001; // utf-8
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct SEnvMapper
|
|
{
|
|
HWND hWnd;
|
|
irr::CIrrDeviceWin32* irrDev;
|
|
};
|
|
irr::core::list<SEnvMapper> EnvMap;
|
|
|
|
HKL KEYBOARD_INPUT_HKL=0;
|
|
unsigned int KEYBOARD_INPUT_CODEPAGE = 1252;
|
|
}
|
|
|
|
SEnvMapper* getEnvMapperFromHWnd(HWND hWnd)
|
|
{
|
|
irr::core::list<SEnvMapper>::Iterator it = EnvMap.begin();
|
|
for (; it!= EnvMap.end(); ++it)
|
|
if ((*it).hWnd == hWnd)
|
|
return &(*it);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd)
|
|
{
|
|
irr::core::list<SEnvMapper>::Iterator it = EnvMap.begin();
|
|
for (; it!= EnvMap.end(); ++it)
|
|
if ((*it).hWnd == hWnd)
|
|
return (*it).irrDev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void updateIMECompositonPosition(irr::core::position2di pos, HWND hwnd, HIMC himc)
|
|
{
|
|
COMPOSITIONFORM cf;
|
|
cf.dwStyle = CFS_POINT;
|
|
cf.ptCurrentPos.x = pos.X;
|
|
cf.ptCurrentPos.y = pos.Y;
|
|
ImmSetCompositionWindow(himc, &cf);
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
#ifndef WM_MOUSEWHEEL
|
|
#define WM_MOUSEWHEEL 0x020A
|
|
#endif
|
|
#ifndef WHEEL_DELTA
|
|
#define WHEEL_DELTA 120
|
|
#endif
|
|
|
|
irr::CIrrDeviceWin32* dev = 0;
|
|
irr::SEvent event;
|
|
|
|
static irr::s32 ClickCount=0;
|
|
if (GetCapture() != hWnd && ClickCount > 0)
|
|
ClickCount = 0;
|
|
|
|
|
|
struct messageMap
|
|
{
|
|
irr::s32 group;
|
|
UINT winMessage;
|
|
irr::s32 irrMessage;
|
|
};
|
|
|
|
static messageMap mouseMap[] =
|
|
{
|
|
{0, WM_LBUTTONDOWN, irr::EMIE_LMOUSE_PRESSED_DOWN},
|
|
{1, WM_LBUTTONUP, irr::EMIE_LMOUSE_LEFT_UP},
|
|
{0, WM_RBUTTONDOWN, irr::EMIE_RMOUSE_PRESSED_DOWN},
|
|
{1, WM_RBUTTONUP, irr::EMIE_RMOUSE_LEFT_UP},
|
|
{0, WM_MBUTTONDOWN, irr::EMIE_MMOUSE_PRESSED_DOWN},
|
|
{1, WM_MBUTTONUP, irr::EMIE_MMOUSE_LEFT_UP},
|
|
{2, WM_MOUSEMOVE, irr::EMIE_MOUSE_MOVED},
|
|
{3, WM_MOUSEWHEEL, irr::EMIE_MOUSE_WHEEL},
|
|
{-1, 0, 0}
|
|
};
|
|
|
|
// handle grouped events
|
|
messageMap * m = mouseMap;
|
|
while ( m->group >=0 && m->winMessage != message )
|
|
m += 1;
|
|
|
|
if ( m->group >= 0 )
|
|
{
|
|
if ( m->group == 0 ) // down
|
|
{
|
|
ClickCount++;
|
|
SetCapture(hWnd);
|
|
}
|
|
else
|
|
if ( m->group == 1 ) // up
|
|
{
|
|
ClickCount--;
|
|
if (ClickCount<1)
|
|
{
|
|
ClickCount=0;
|
|
ReleaseCapture();
|
|
}
|
|
}
|
|
|
|
event.EventType = irr::EET_MOUSE_INPUT_EVENT;
|
|
event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT) m->irrMessage;
|
|
event.MouseInput.X = (short)LOWORD(lParam);
|
|
event.MouseInput.Y = (short)HIWORD(lParam);
|
|
event.MouseInput.Shift = ((LOWORD(wParam) & MK_SHIFT) != 0);
|
|
event.MouseInput.Control = ((LOWORD(wParam) & MK_CONTROL) != 0);
|
|
// left and right mouse buttons
|
|
event.MouseInput.ButtonStates = wParam & ( MK_LBUTTON | MK_RBUTTON);
|
|
// middle and extra buttons
|
|
if (wParam & MK_MBUTTON)
|
|
event.MouseInput.ButtonStates |= irr::EMBSM_MIDDLE;
|
|
#if(_WIN32_WINNT >= 0x0500)
|
|
if (wParam & MK_XBUTTON1)
|
|
event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA1;
|
|
if (wParam & MK_XBUTTON2)
|
|
event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA2;
|
|
#endif
|
|
event.MouseInput.Wheel = 0.f;
|
|
|
|
// wheel
|
|
if ( m->group == 3 )
|
|
{
|
|
POINT p; // fixed by jox
|
|
p.x = 0; p.y = 0;
|
|
ClientToScreen(hWnd, &p);
|
|
event.MouseInput.X -= p.x;
|
|
event.MouseInput.Y -= p.y;
|
|
event.MouseInput.Wheel = ((irr::f32)((short)HIWORD(wParam))) / (irr::f32)WHEEL_DELTA;
|
|
}
|
|
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (dev && !dev->isIMEComposingStarted())
|
|
{
|
|
dev->postEventFromUser(event);
|
|
|
|
if ( event.MouseInput.Event >= irr::EMIE_LMOUSE_PRESSED_DOWN && event.MouseInput.Event <= irr::EMIE_MMOUSE_PRESSED_DOWN )
|
|
{
|
|
irr::u32 clicks = dev->checkSuccessiveClicks(event.MouseInput.X, event.MouseInput.Y, event.MouseInput.Event);
|
|
if ( clicks == 2 )
|
|
{
|
|
event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)(irr::EMIE_LMOUSE_DOUBLE_CLICK + event.MouseInput.Event-irr::EMIE_LMOUSE_PRESSED_DOWN);
|
|
dev->postEventFromUser(event);
|
|
}
|
|
else if ( clicks == 3 )
|
|
{
|
|
event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)(irr::EMIE_LMOUSE_TRIPLE_CLICK + event.MouseInput.Event-irr::EMIE_LMOUSE_PRESSED_DOWN);
|
|
dev->postEventFromUser(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
switch (message)
|
|
{
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
BeginPaint(hWnd, &ps);
|
|
EndPaint(hWnd, &ps);
|
|
}
|
|
return 0;
|
|
|
|
case WM_ERASEBKGND:
|
|
return 0;
|
|
case WM_SETFOCUS:
|
|
case WM_KILLFOCUS:
|
|
{
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (dev)
|
|
dev->setIMEComposingStarted(false);
|
|
return 0;
|
|
}
|
|
case WM_IME_SETCONTEXT:
|
|
{
|
|
// application wants to draw composition text by itself.
|
|
lParam &= ~(ISC_SHOWUICOMPOSITIONWINDOW);
|
|
break;
|
|
}
|
|
case WM_IME_STARTCOMPOSITION:
|
|
case WM_IME_ENDCOMPOSITION:
|
|
{
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (!dev)
|
|
return 0;
|
|
|
|
irr::gui::IGUIEnvironment* env = dev->getGUIEnvironment();
|
|
if (!env)
|
|
return 0;
|
|
|
|
irr::gui::IGUISkin* skin = env->getSkin();
|
|
if (!skin)
|
|
return 0;
|
|
|
|
irr::gui::IGUIFont* font = skin->getFont();
|
|
if (!font)
|
|
return 0;
|
|
|
|
irr::gui::IGUIEditBox* box = dynamic_cast<irr::gui::IGUIEditBox*>(env->getFocus());
|
|
if (!box)
|
|
return 0;
|
|
|
|
box->clearComposingText();
|
|
dev->setIMEComposingStarted(message == WM_IME_STARTCOMPOSITION);
|
|
HIMC imc = ImmGetContext(hWnd);
|
|
if (!imc)
|
|
return 0;
|
|
if (message == WM_IME_STARTCOMPOSITION)
|
|
{
|
|
updateIMECompositonPosition(box->getICPos(), hWnd, imc);
|
|
// Same height as system font so the composition window is
|
|
// positioned correctly vertically
|
|
LOGFONT lFont = {0};
|
|
lFont.lfHeight = font->getHeightPerLine();
|
|
lFont.lfCharSet = OEM_CHARSET;
|
|
ImmSetCompositionFont(imc, &lFont);
|
|
}
|
|
ImmReleaseContext(hWnd, imc);
|
|
return 0;
|
|
}
|
|
case WM_IME_COMPOSITION:
|
|
{
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (!dev)
|
|
return 0;
|
|
|
|
irr::gui::IGUIEnvironment* env = dev->getGUIEnvironment();
|
|
if (!env)
|
|
return 0;
|
|
|
|
irr::gui::IGUIEditBox* box = dynamic_cast<irr::gui::IGUIEditBox*>(env->getFocus());
|
|
if (!box)
|
|
return 0;
|
|
bool get_comp_str = (lParam & GCS_COMPSTR) != 0;
|
|
bool get_result_str = (lParam & GCS_RESULTSTR) != 0;
|
|
if (get_comp_str || get_result_str)
|
|
{
|
|
HIMC imc = ImmGetContext(hWnd);
|
|
if (!imc)
|
|
return 0;
|
|
LONG vec_size = ImmGetCompositionString(imc, get_comp_str ? GCS_COMPSTR : GCS_RESULTSTR, (void*)NULL, 0);
|
|
if ((vec_size == IMM_ERROR_NODATA) ||
|
|
(vec_size == IMM_ERROR_GENERAL))
|
|
{
|
|
ImmReleaseContext(hWnd, imc);
|
|
return 0;
|
|
}
|
|
std::vector<wchar_t> ct;
|
|
// NULL terminator
|
|
ct.resize(vec_size / sizeof(wchar_t) + 1, 0);
|
|
ImmGetCompositionString(imc, get_comp_str ? GCS_COMPSTR : GCS_RESULTSTR, ct.data(), vec_size);
|
|
std::u32string result = StringUtils::wideToUtf32(ct.data());
|
|
if (get_comp_str)
|
|
{
|
|
box->setComposingText(result);
|
|
}
|
|
else
|
|
{
|
|
for (char32_t c : result)
|
|
{
|
|
event.EventType = irr::EET_KEY_INPUT_EVENT;
|
|
event.KeyInput.PressedDown = true;
|
|
event.KeyInput.Char = (char32_t)c;
|
|
event.KeyInput.Key = irr::IRR_KEY_UNKNOWN;
|
|
event.KeyInput.Shift = false;
|
|
event.KeyInput.Control = false;
|
|
dev->postEventFromUser(event);
|
|
}
|
|
}
|
|
if (get_result_str)
|
|
updateIMECompositonPosition(box->getICPos(), hWnd, imc);
|
|
ImmReleaseContext(hWnd, imc);
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_SYSKEYDOWN:
|
|
case WM_SYSKEYUP:
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
{
|
|
BYTE allKeys[256];
|
|
|
|
event.EventType = irr::EET_KEY_INPUT_EVENT;
|
|
event.KeyInput.Key = (irr::EKEY_CODE)wParam;
|
|
event.KeyInput.PressedDown = (message==WM_KEYDOWN || message == WM_SYSKEYDOWN);
|
|
|
|
const UINT MY_MAPVK_VSC_TO_VK_EX = 3; // MAPVK_VSC_TO_VK_EX should be in SDK according to MSDN, but isn't in mine.
|
|
if ( event.KeyInput.Key == irr::IRR_KEY_SHIFT )
|
|
{
|
|
// this will fail on systems before windows NT/2000/XP, not sure _what_ will return there instead.
|
|
event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX );
|
|
}
|
|
if ( event.KeyInput.Key == irr::IRR_KEY_CONTROL )
|
|
{
|
|
event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX );
|
|
// some keyboards will just return LEFT for both - left and right keys. So also check extend bit.
|
|
if (lParam & 0x1000000)
|
|
event.KeyInput.Key = irr::IRR_KEY_RCONTROL;
|
|
}
|
|
if ( event.KeyInput.Key == irr::IRR_KEY_MENU )
|
|
{
|
|
event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX );
|
|
if (lParam & 0x1000000)
|
|
event.KeyInput.Key = irr::IRR_KEY_RMENU;
|
|
}
|
|
|
|
GetKeyboardState(allKeys);
|
|
|
|
event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0);
|
|
event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0);
|
|
event.KeyInput.Char = 0;
|
|
|
|
wchar_t keyChars[10] = {0};
|
|
UINT scanCode = HIWORD(lParam);
|
|
int conversionResult = ToUnicodeEx((UINT)wParam,scanCode,allKeys,keyChars,_countof(keyChars),0,KEYBOARD_INPUT_HKL);
|
|
// allow composing characters like '@' with Alt Gr on non-US keyboards
|
|
if ((allKeys[VK_MENU] & 0x80) != 0)
|
|
event.KeyInput.Control = 0;
|
|
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (dev && !dev->isIMEComposingStarted())
|
|
{
|
|
if (conversionResult >= 1)
|
|
{
|
|
for (int i = 0; i < conversionResult; i++)
|
|
{
|
|
event.KeyInput.Char = keyChars[i];
|
|
dev->postEventFromUser(event);
|
|
}
|
|
}
|
|
else
|
|
dev->postEventFromUser(event);
|
|
}
|
|
|
|
if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
// resize
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (dev)
|
|
dev->OnResized();
|
|
}
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
|
|
case WM_SYSCOMMAND:
|
|
// prevent screensaver or monitor powersave mode from starting
|
|
if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
|
|
(wParam & 0xFFF0) == SC_MONITORPOWER ||
|
|
(wParam & 0xFFF0) == SC_KEYMENU
|
|
)
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
// we need to take care for screen changes, e.g. Alt-Tab
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (dev && dev->isFullscreen())
|
|
{
|
|
if ((wParam&0xFF)==WA_INACTIVE)
|
|
{
|
|
// If losing focus we minimize the app to show other one
|
|
ShowWindow(hWnd,SW_MINIMIZE);
|
|
// and switch back to default resolution
|
|
dev->switchToFullScreen(true);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise we retore the fullscreen Irrlicht app
|
|
SetForegroundWindow(hWnd);
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
// and set the fullscreen resolution again
|
|
dev->switchToFullScreen();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_USER:
|
|
event.EventType = irr::EET_USER_EVENT;
|
|
event.UserEvent.UserData1 = (irr::s32)wParam;
|
|
event.UserEvent.UserData2 = (irr::s32)lParam;
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
|
|
if (dev)
|
|
dev->postEventFromUser(event);
|
|
|
|
return 0;
|
|
|
|
case WM_SETCURSOR:
|
|
// because Windows forgot about that in the meantime
|
|
dev = getDeviceFromHWnd(hWnd);
|
|
if (dev)
|
|
{
|
|
dev->getCursorControl()->setActiveIcon( dev->getCursorControl()->getActiveIcon() );
|
|
dev->getCursorControl()->setVisible( dev->getCursorControl()->isVisible() );
|
|
}
|
|
break;
|
|
|
|
case WM_INPUTLANGCHANGE:
|
|
// get the new codepage used for keyboard input
|
|
KEYBOARD_INPUT_HKL = GetKeyboardLayout(0);
|
|
KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) );
|
|
return 0;
|
|
}
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
namespace irr
|
|
{
|
|
|
|
//! constructor
|
|
CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params)
|
|
: CIrrDeviceStub(params), HWnd(0), ChangedToFullScreen(false), Resized(false),
|
|
ExternalWindow(false), Win32CursorControl(0), JoyControl(0)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CIrrDeviceWin32");
|
|
#endif
|
|
|
|
m_ime_composing_started = false;
|
|
// get windows version and create OS operator
|
|
core::stringc winversion;
|
|
getWindowsVersion(winversion);
|
|
Operator = new COSOperator(winversion);
|
|
os::Printer::log(winversion.c_str(), ELL_INFORMATION);
|
|
|
|
// get handle to exe file
|
|
HINSTANCE hInstance = GetModuleHandle(0);
|
|
|
|
// Store original desktop mode.
|
|
|
|
memset(&DesktopMode, 0, sizeof(DesktopMode));
|
|
DesktopMode.dmSize = sizeof(DesktopMode);
|
|
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DesktopMode);
|
|
|
|
// create the window if we need to and we do not use the null device
|
|
if (!CreationParams.WindowId && CreationParams.DriverType != video::EDT_NULL)
|
|
{
|
|
const wchar_t* ClassName = L"CIrrDeviceWin32";
|
|
|
|
// Register Class
|
|
WNDCLASSEX wcex;
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = WndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = NULL;
|
|
wcex.hCursor = 0; // LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
|
wcex.lpszMenuName = 0;
|
|
wcex.lpszClassName = ClassName;
|
|
wcex.hIconSm = 0;
|
|
|
|
// if there is an icon, load it
|
|
// Check icon_rc.template in tools/windows_installer for define
|
|
HICON icon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(100),
|
|
IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON),
|
|
LR_DEFAULTCOLOR);
|
|
if (icon != NULL)
|
|
wcex.hIcon = icon;
|
|
HICON icon_sm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(100),
|
|
IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON),
|
|
LR_DEFAULTCOLOR);
|
|
if (icon_sm != NULL)
|
|
wcex.hIconSm = icon_sm;
|
|
|
|
RegisterClassEx(&wcex);
|
|
|
|
// calculate client size
|
|
|
|
RECT clientSize;
|
|
clientSize.top = 0;
|
|
clientSize.left = 0;
|
|
clientSize.right = CreationParams.WindowSize.Width;
|
|
clientSize.bottom = CreationParams.WindowSize.Height;
|
|
|
|
DWORD style = WS_POPUP;
|
|
|
|
if (!CreationParams.Fullscreen)
|
|
style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
|
|
AdjustWindowRect(&clientSize, style, FALSE);
|
|
|
|
const s32 realWidth = clientSize.right - clientSize.left;
|
|
const s32 realHeight = clientSize.bottom - clientSize.top;
|
|
|
|
s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
|
|
s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
|
|
|
|
if ( windowLeft < 0 )
|
|
windowLeft = 0;
|
|
if ( windowTop < 0 )
|
|
windowTop = 0; // make sure window menus are in screen on creation
|
|
|
|
if (CreationParams.Fullscreen)
|
|
{
|
|
windowLeft = 0;
|
|
windowTop = 0;
|
|
}
|
|
|
|
// create window
|
|
|
|
HWnd = CreateWindow( ClassName, __TEXT(""), style, windowLeft, windowTop,
|
|
realWidth, realHeight, NULL, NULL, hInstance, NULL);
|
|
CreationParams.WindowId = HWnd;
|
|
// CreationParams.WindowSize.Width = realWidth;
|
|
// CreationParams.WindowSize.Height = realHeight;
|
|
|
|
ShowWindow(HWnd, SW_SHOWNORMAL);
|
|
UpdateWindow(HWnd);
|
|
|
|
// fix ugly ATI driver bugs. Thanks to ariaci
|
|
MoveWindow(HWnd, windowLeft, windowTop, realWidth, realHeight, TRUE);
|
|
|
|
// make sure everything gets updated to the real sizes
|
|
Resized = true;
|
|
}
|
|
else if (CreationParams.WindowId)
|
|
{
|
|
// attach external window
|
|
HWnd = static_cast<HWND>(CreationParams.WindowId);
|
|
RECT r;
|
|
GetWindowRect(HWnd, &r);
|
|
CreationParams.WindowSize.Width = r.right - r.left;
|
|
CreationParams.WindowSize.Height = r.bottom - r.top;
|
|
CreationParams.Fullscreen = false;
|
|
ExternalWindow = true;
|
|
}
|
|
|
|
// create cursor control
|
|
|
|
Win32CursorControl = new CCursorControl(this, CreationParams.WindowSize, HWnd, CreationParams.Fullscreen);
|
|
CursorControl = Win32CursorControl;
|
|
JoyControl = new SJoystickWin32Control(this);
|
|
|
|
// initialize doubleclicks with system values
|
|
MouseMultiClicks.DoubleClickTime = GetDoubleClickTime();
|
|
|
|
// create driver
|
|
|
|
createDriver();
|
|
|
|
if (VideoDriver)
|
|
createGUIAndScene();
|
|
|
|
// register environment
|
|
|
|
SEnvMapper em;
|
|
em.irrDev = this;
|
|
em.hWnd = HWnd;
|
|
EnvMap.push_back(em);
|
|
|
|
// set this as active window
|
|
if (!ExternalWindow)
|
|
{
|
|
SetActiveWindow(HWnd);
|
|
SetForegroundWindow(HWnd);
|
|
}
|
|
|
|
// get the codepage used for keyboard input
|
|
KEYBOARD_INPUT_HKL = GetKeyboardLayout(0);
|
|
KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) );
|
|
|
|
// inform driver about the window size etc.
|
|
resizeIfNecessary();
|
|
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CIrrDeviceWin32::~CIrrDeviceWin32()
|
|
{
|
|
delete JoyControl;
|
|
|
|
// unregister environment
|
|
|
|
irr::core::list<SEnvMapper>::Iterator it = EnvMap.begin();
|
|
for (; it!= EnvMap.end(); ++it)
|
|
{
|
|
if ((*it).hWnd == HWnd)
|
|
{
|
|
EnvMap.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
|
|
switchToFullScreen(true);
|
|
}
|
|
|
|
|
|
//! create the driver
|
|
void CIrrDeviceWin32::createDriver()
|
|
{
|
|
switch(CreationParams.DriverType)
|
|
{
|
|
case video::EDT_DIRECT3D8:
|
|
#ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
|
|
|
|
VideoDriver = video::createDirectX8Driver(CreationParams, FileSystem, HWnd);
|
|
|
|
if (!VideoDriver)
|
|
{
|
|
os::Printer::log("Could not create DIRECT3D8 Driver.", ELL_ERROR);
|
|
}
|
|
#else
|
|
os::Printer::log("DIRECT3D8 Driver was not compiled into this dll. Try another one.", ELL_ERROR);
|
|
#endif // _IRR_COMPILE_WITH_DIRECT3D_8_
|
|
|
|
break;
|
|
|
|
case video::EDT_DIRECT3D9:
|
|
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
|
|
|
|
VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, HWnd);
|
|
|
|
if (!VideoDriver)
|
|
{
|
|
os::Printer::log("Could not create DIRECT3D9 Driver.", ELL_ERROR);
|
|
}
|
|
#else
|
|
os::Printer::log("DIRECT3D9 Driver was not compiled into this dll. Try another one.", ELL_ERROR);
|
|
#endif // _IRR_COMPILE_WITH_DIRECT3D_9_
|
|
|
|
break;
|
|
|
|
case video::EDT_OPENGL:
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
switchToFullScreen();
|
|
|
|
VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, this);
|
|
if (!VideoDriver)
|
|
{
|
|
os::Printer::log("Could not create OpenGL driver.", ELL_ERROR);
|
|
}
|
|
#else
|
|
os::Printer::log("OpenGL driver was not compiled in.", ELL_ERROR);
|
|
#endif
|
|
break;
|
|
|
|
case video::EDT_SOFTWARE:
|
|
|
|
#ifdef _IRR_COMPILE_WITH_SOFTWARE_
|
|
switchToFullScreen();
|
|
|
|
VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
|
|
#else
|
|
os::Printer::log("Software driver was not compiled in.", ELL_ERROR);
|
|
#endif
|
|
|
|
break;
|
|
|
|
case video::EDT_BURNINGSVIDEO:
|
|
#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
|
|
switchToFullScreen();
|
|
|
|
VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
|
|
#else
|
|
os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR);
|
|
#endif
|
|
break;
|
|
|
|
case video::EDT_NULL:
|
|
// create null driver
|
|
VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
|
|
break;
|
|
|
|
default:
|
|
os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//! runs the device. Returns false if device wants to be deleted
|
|
bool CIrrDeviceWin32::run()
|
|
{
|
|
os::Timer::tick();
|
|
|
|
static_cast<CCursorControl*>(CursorControl)->update();
|
|
|
|
handleSystemMessages();
|
|
|
|
if (!Close)
|
|
resizeIfNecessary();
|
|
|
|
if(!Close && JoyControl)
|
|
JoyControl->pollJoysticks();
|
|
|
|
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
|
|
return !Close;
|
|
}
|
|
|
|
|
|
//! Pause the current process for the minimum time allowed only to allow other processes to execute
|
|
void CIrrDeviceWin32::yield()
|
|
{
|
|
Sleep(1);
|
|
}
|
|
|
|
//! Pause execution and let other processes to run for a specified amount of time.
|
|
void CIrrDeviceWin32::sleep(u32 timeMs, bool pauseTimer)
|
|
{
|
|
const bool wasStopped = Timer ? Timer->isStopped() : true;
|
|
if (pauseTimer && !wasStopped)
|
|
Timer->stop();
|
|
|
|
Sleep(timeMs);
|
|
|
|
if (pauseTimer && !wasStopped)
|
|
Timer->start();
|
|
}
|
|
|
|
|
|
void CIrrDeviceWin32::resizeIfNecessary()
|
|
{
|
|
if (!Resized || !getVideoDriver())
|
|
return;
|
|
|
|
RECT r;
|
|
GetClientRect(HWnd, &r);
|
|
|
|
char tmp[255];
|
|
|
|
if (r.right < 2 || r.bottom < 2)
|
|
{
|
|
sprintf(tmp, "Ignoring resize operation to (%ld %ld)", r.right, r.bottom);
|
|
os::Printer::log(tmp);
|
|
}
|
|
else
|
|
{
|
|
sprintf(tmp, "Resizing window (%ld %ld)", r.right, r.bottom);
|
|
os::Printer::log(tmp);
|
|
|
|
getVideoDriver()->OnResize(irr::core::dimension2du((u32)r.right, (u32)r.bottom));
|
|
getWin32CursorControl()->OnResize(getVideoDriver()->getScreenSize());
|
|
}
|
|
|
|
Resized = false;
|
|
}
|
|
|
|
|
|
//! sets the caption of the window
|
|
void CIrrDeviceWin32::setWindowCaption(const wchar_t* text)
|
|
{
|
|
// We use SendMessage instead of SetText to ensure proper
|
|
// function even in cases where the HWND was created in a different thread
|
|
DWORD_PTR dwResult;
|
|
SendMessageTimeoutW(HWnd, WM_SETTEXT, 0,
|
|
reinterpret_cast<LPARAM>(text),
|
|
SMTO_ABORTIFHUNG, 2000, &dwResult);
|
|
}
|
|
|
|
|
|
//! presents a surface in the client area
|
|
bool CIrrDeviceWin32::present(video::IImage* image, void* windowId, core::rect<s32>* src)
|
|
{
|
|
HWND hwnd = HWnd;
|
|
if ( windowId )
|
|
hwnd = reinterpret_cast<HWND>(windowId);
|
|
|
|
HDC dc = GetDC(hwnd);
|
|
|
|
if ( dc )
|
|
{
|
|
RECT rect;
|
|
GetClientRect(hwnd, &rect);
|
|
const void* memory = (const void *)image->lock();
|
|
|
|
BITMAPV4HEADER bi;
|
|
ZeroMemory (&bi, sizeof(bi));
|
|
bi.bV4Size = sizeof(BITMAPINFOHEADER);
|
|
bi.bV4BitCount = (WORD)image->getBitsPerPixel();
|
|
bi.bV4Planes = 1;
|
|
bi.bV4Width = image->getDimension().Width;
|
|
bi.bV4Height = -((s32)image->getDimension().Height);
|
|
bi.bV4V4Compression = BI_BITFIELDS;
|
|
bi.bV4AlphaMask = image->getAlphaMask();
|
|
bi.bV4RedMask = image->getRedMask();
|
|
bi.bV4GreenMask = image->getGreenMask();
|
|
bi.bV4BlueMask = image->getBlueMask();
|
|
|
|
if ( src )
|
|
{
|
|
StretchDIBits(dc, 0,0, rect.right, rect.bottom,
|
|
src->UpperLeftCorner.X, src->UpperLeftCorner.Y,
|
|
src->getWidth(), src->getHeight(),
|
|
memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
StretchDIBits(dc, 0,0, rect.right, rect.bottom,
|
|
0, 0, image->getDimension().Width, image->getDimension().Height,
|
|
memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY);
|
|
}
|
|
|
|
image->unlock();
|
|
|
|
ReleaseDC(hwnd, dc);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//! notifies the device that it should close itself
|
|
void CIrrDeviceWin32::closeDevice()
|
|
{
|
|
MSG msg;
|
|
PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
|
|
PostQuitMessage(0);
|
|
PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
|
|
if (!ExternalWindow)
|
|
{
|
|
DestroyWindow(HWnd);
|
|
const wchar_t* ClassName = L"CIrrDeviceWin32";
|
|
HINSTANCE hInstance = GetModuleHandle(0);
|
|
UnregisterClass(ClassName, hInstance);
|
|
}
|
|
Close=true;
|
|
}
|
|
|
|
|
|
//! returns if window is active. if not, nothing needs to be drawn
|
|
bool CIrrDeviceWin32::isWindowActive() const
|
|
{
|
|
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
|
|
return (GetActiveWindow() == HWnd);
|
|
}
|
|
|
|
|
|
//! returns if window has focus
|
|
bool CIrrDeviceWin32::isWindowFocused() const
|
|
{
|
|
bool ret = (GetFocus() == HWnd);
|
|
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! returns if window is minimized
|
|
bool CIrrDeviceWin32::isWindowMinimized() const
|
|
{
|
|
WINDOWPLACEMENT plc;
|
|
plc.length=sizeof(WINDOWPLACEMENT);
|
|
bool ret=false;
|
|
if (GetWindowPlacement(HWnd,&plc))
|
|
ret=(plc.showCmd & SW_SHOWMINIMIZED)!=0;
|
|
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! switches to fullscreen
|
|
bool CIrrDeviceWin32::switchToFullScreen(bool reset)
|
|
{
|
|
if (!CreationParams.Fullscreen)
|
|
return true;
|
|
|
|
if (reset)
|
|
{
|
|
if (ChangedToFullScreen)
|
|
{
|
|
return (ChangeDisplaySettings(&DesktopMode,0)==DISP_CHANGE_SUCCESSFUL);
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
// use default values from current setting
|
|
|
|
DEVMODE dm;
|
|
memset(&dm, 0, sizeof(dm));
|
|
dm.dmSize = sizeof(dm);
|
|
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
|
|
dm.dmPelsWidth = CreationParams.WindowSize.Width;
|
|
dm.dmPelsHeight = CreationParams.WindowSize.Height;
|
|
dm.dmBitsPerPel = CreationParams.Bits;
|
|
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
|
|
|
LONG res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
|
if (res != DISP_CHANGE_SUCCESSFUL)
|
|
{ // try again without forcing display frequency
|
|
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
|
}
|
|
|
|
bool ret = false;
|
|
switch(res)
|
|
{
|
|
case DISP_CHANGE_SUCCESSFUL:
|
|
ChangedToFullScreen = true;
|
|
ret = true;
|
|
break;
|
|
case DISP_CHANGE_RESTART:
|
|
os::Printer::log("Switch to fullscreen: The computer must be restarted in order for the graphics mode to work.", ELL_ERROR);
|
|
break;
|
|
case DISP_CHANGE_BADFLAGS:
|
|
os::Printer::log("Switch to fullscreen: An invalid set of flags was passed in.", ELL_ERROR);
|
|
break;
|
|
case DISP_CHANGE_BADPARAM:
|
|
os::Printer::log("Switch to fullscreen: An invalid parameter was passed in. This can include an invalid flag or combination of flags.", ELL_ERROR);
|
|
break;
|
|
case DISP_CHANGE_FAILED:
|
|
os::Printer::log("Switch to fullscreen: The display driver failed the specified graphics mode.", ELL_ERROR);
|
|
break;
|
|
case DISP_CHANGE_BADMODE:
|
|
os::Printer::log("Switch to fullscreen: The graphics mode is not supported.", ELL_ERROR);
|
|
break;
|
|
default:
|
|
os::Printer::log("An unknown error occured while changing to fullscreen.", ELL_ERROR);
|
|
break;
|
|
}
|
|
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! returns the win32 cursor control
|
|
CIrrDeviceWin32::CCursorControl* CIrrDeviceWin32::getWin32CursorControl()
|
|
{
|
|
return Win32CursorControl;
|
|
}
|
|
|
|
|
|
//! \return Returns a pointer to a list with all video modes supported
|
|
//! by the gfx adapter.
|
|
video::IVideoModeList* CIrrDeviceWin32::getVideoModeList()
|
|
{
|
|
if (!VideoModeList.getVideoModeCount())
|
|
{
|
|
// enumerate video modes.
|
|
DWORD i=0;
|
|
DEVMODE mode;
|
|
memset(&mode, 0, sizeof(mode));
|
|
mode.dmSize = sizeof(mode);
|
|
|
|
while (EnumDisplaySettings(NULL, i, &mode))
|
|
{
|
|
VideoModeList.addMode(core::dimension2d<u32>(mode.dmPelsWidth, mode.dmPelsHeight),
|
|
mode.dmBitsPerPel);
|
|
|
|
++i;
|
|
}
|
|
|
|
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode))
|
|
VideoModeList.setDesktop(mode.dmBitsPerPel, core::dimension2d<u32>(mode.dmPelsWidth, mode.dmPelsHeight));
|
|
}
|
|
|
|
return &VideoModeList;
|
|
}
|
|
|
|
typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
|
|
// Needed for old windows apis
|
|
// depending on the SDK version and compilers some defines might be available
|
|
// or not
|
|
#ifndef PRODUCT_ULTIMATE
|
|
#define PRODUCT_ULTIMATE 0x00000001
|
|
#define PRODUCT_HOME_BASIC 0x00000002
|
|
#define PRODUCT_HOME_PREMIUM 0x00000003
|
|
#define PRODUCT_ENTERPRISE 0x00000004
|
|
#define PRODUCT_HOME_BASIC_N 0x00000005
|
|
#define PRODUCT_BUSINESS 0x00000006
|
|
#define PRODUCT_STARTER 0x0000000B
|
|
#endif
|
|
#ifndef PRODUCT_ULTIMATE_N
|
|
#define PRODUCT_BUSINESS_N 0x00000010
|
|
#define PRODUCT_HOME_PREMIUM_N 0x0000001A
|
|
#define PRODUCT_ENTERPRISE_N 0x0000001B
|
|
#define PRODUCT_ULTIMATE_N 0x0000001C
|
|
#endif
|
|
#ifndef PRODUCT_STARTER_N
|
|
#define PRODUCT_STARTER_N 0x0000002F
|
|
#endif
|
|
#ifndef PRODUCT_PROFESSIONAL
|
|
#define PRODUCT_PROFESSIONAL 0x00000030
|
|
#define PRODUCT_PROFESSIONAL_N 0x00000031
|
|
#endif
|
|
#ifndef PRODUCT_ULTIMATE_E
|
|
#define PRODUCT_STARTER_E 0x00000042
|
|
#define PRODUCT_HOME_BASIC_E 0x00000043
|
|
#define PRODUCT_HOME_PREMIUM_E 0x00000044
|
|
#define PRODUCT_PROFESSIONAL_E 0x00000045
|
|
#define PRODUCT_ENTERPRISE_E 0x00000046
|
|
#define PRODUCT_ULTIMATE_E 0x00000047
|
|
#endif
|
|
|
|
void CIrrDeviceWin32::getWindowsVersion(core::stringc& out)
|
|
{
|
|
OSVERSIONINFOEX osvi;
|
|
PGPI pGPI;
|
|
BOOL bOsVersionInfoEx;
|
|
|
|
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
|
|
bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);
|
|
if (!bOsVersionInfoEx)
|
|
{
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (! GetVersionEx((OSVERSIONINFO *) &osvi))
|
|
return;
|
|
}
|
|
|
|
switch (osvi.dwPlatformId)
|
|
{
|
|
case VER_PLATFORM_WIN32_NT:
|
|
if (osvi.dwMajorVersion <= 4)
|
|
out.append("Microsoft Windows NT ");
|
|
else
|
|
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
|
|
out.append("Microsoft Windows 2000 ");
|
|
else
|
|
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
|
|
out.append("Microsoft Windows XP ");
|
|
else
|
|
if (osvi.dwMajorVersion == 6 )
|
|
{
|
|
if (osvi.dwMinorVersion == 0)
|
|
{
|
|
if (osvi.wProductType == VER_NT_WORKSTATION)
|
|
out.append("Microsoft Windows Vista ");
|
|
else
|
|
out.append("Microsoft Windows Server 2008 ");
|
|
}
|
|
else if (osvi.dwMinorVersion == 1)
|
|
{
|
|
if (osvi.wProductType == VER_NT_WORKSTATION)
|
|
out.append("Microsoft Windows 7 ");
|
|
else
|
|
out.append("Microsoft Windows Server 2008 R2 ");
|
|
}
|
|
else if (osvi.dwMinorVersion == 2)
|
|
{
|
|
if (osvi.wProductType == VER_NT_WORKSTATION)
|
|
out.append("Microsoft Windows 8 ");
|
|
else
|
|
out.append("Microsoft Windows Server 2012 ");
|
|
}
|
|
else if (osvi.dwMinorVersion == 3)
|
|
{
|
|
if (osvi.wProductType == VER_NT_WORKSTATION)
|
|
out.append("Microsoft Windows 8.1 ");
|
|
else
|
|
out.append("Microsoft Windows Server 2012 R2 ");
|
|
}
|
|
}
|
|
|
|
if (bOsVersionInfoEx)
|
|
{
|
|
if (osvi.dwMajorVersion == 6)
|
|
{
|
|
DWORD dwType;
|
|
pGPI = (PGPI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
|
|
pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
|
|
|
|
switch (dwType)
|
|
{
|
|
case PRODUCT_ULTIMATE:
|
|
case PRODUCT_ULTIMATE_E:
|
|
case PRODUCT_ULTIMATE_N:
|
|
out.append("Ultimate Edition ");
|
|
break;
|
|
case PRODUCT_PROFESSIONAL:
|
|
case PRODUCT_PROFESSIONAL_E:
|
|
case PRODUCT_PROFESSIONAL_N:
|
|
out.append("Professional Edition ");
|
|
break;
|
|
case PRODUCT_HOME_BASIC:
|
|
case PRODUCT_HOME_BASIC_E:
|
|
case PRODUCT_HOME_BASIC_N:
|
|
out.append("Home Basic Edition ");
|
|
break;
|
|
case PRODUCT_HOME_PREMIUM:
|
|
case PRODUCT_HOME_PREMIUM_E:
|
|
case PRODUCT_HOME_PREMIUM_N:
|
|
out.append("Home Premium Edition ");
|
|
break;
|
|
case PRODUCT_ENTERPRISE:
|
|
case PRODUCT_ENTERPRISE_E:
|
|
case PRODUCT_ENTERPRISE_N:
|
|
out.append("Enterprise Edition ");
|
|
break;
|
|
case PRODUCT_BUSINESS:
|
|
case PRODUCT_BUSINESS_N:
|
|
out.append("Business Edition ");
|
|
break;
|
|
case PRODUCT_STARTER:
|
|
case PRODUCT_STARTER_E:
|
|
case PRODUCT_STARTER_N:
|
|
out.append("Starter Edition ");
|
|
break;
|
|
}
|
|
}
|
|
#ifdef VER_SUITE_ENTERPRISE
|
|
else
|
|
if (osvi.wProductType == VER_NT_WORKSTATION)
|
|
{
|
|
#ifndef __BORLANDC__
|
|
if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
|
|
out.append("Personal ");
|
|
else
|
|
out.append("Professional ");
|
|
#endif
|
|
}
|
|
else if (osvi.wProductType == VER_NT_SERVER)
|
|
{
|
|
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
|
|
out.append("DataCenter Server ");
|
|
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
|
|
out.append("Advanced Server ");
|
|
else
|
|
out.append("Server ");
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
HKEY hKey;
|
|
char szProductType[80];
|
|
DWORD dwBufLen;
|
|
|
|
RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
__TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
|
|
0, KEY_QUERY_VALUE, &hKey );
|
|
RegQueryValueEx( hKey, __TEXT("ProductType"), NULL, NULL,
|
|
(LPBYTE) szProductType, &dwBufLen);
|
|
RegCloseKey( hKey );
|
|
|
|
if (_strcmpi( "WINNT", szProductType) == 0 )
|
|
out.append("Professional ");
|
|
if (_strcmpi( "LANMANNT", szProductType) == 0)
|
|
out.append("Server ");
|
|
if (_strcmpi( "SERVERNT", szProductType) == 0)
|
|
out.append("Advanced Server ");
|
|
}
|
|
|
|
// Display version, service pack (if any), and build number.
|
|
|
|
char tmp[255];
|
|
|
|
if (osvi.dwMajorVersion <= 4 )
|
|
{
|
|
sprintf(tmp, "version %ld.%ld %s (Build %ld)",
|
|
osvi.dwMajorVersion,
|
|
osvi.dwMinorVersion,
|
|
irr::core::stringc(osvi.szCSDVersion).c_str(),
|
|
osvi.dwBuildNumber & 0xFFFF);
|
|
}
|
|
else
|
|
{
|
|
sprintf(tmp, "%s (Build %ld)", irr::core::stringc(osvi.szCSDVersion).c_str(),
|
|
osvi.dwBuildNumber & 0xFFFF);
|
|
}
|
|
|
|
out.append(tmp);
|
|
break;
|
|
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
|
|
if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
|
|
{
|
|
out.append("Microsoft Windows 95 ");
|
|
if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
|
|
out.append("OSR2 " );
|
|
}
|
|
|
|
if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
|
|
{
|
|
out.append("Microsoft Windows 98 ");
|
|
if ( osvi.szCSDVersion[1] == 'A' )
|
|
out.append( "SE " );
|
|
}
|
|
|
|
if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
|
|
out.append("Microsoft Windows Me ");
|
|
|
|
break;
|
|
|
|
case VER_PLATFORM_WIN32s:
|
|
out.append("Microsoft Win32s ");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//! Notifies the device, that it has been resized
|
|
void CIrrDeviceWin32::OnResized()
|
|
{
|
|
Resized = true;
|
|
}
|
|
|
|
//! Sets if the window should be resizable in windowed mode.
|
|
void CIrrDeviceWin32::setResizable(bool resize)
|
|
{
|
|
if (ExternalWindow || !getVideoDriver() || CreationParams.Fullscreen)
|
|
return;
|
|
|
|
LONG_PTR style = WS_POPUP;
|
|
|
|
if (!resize)
|
|
style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
else
|
|
style = WS_THICKFRAME | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
|
|
|
if (!SetWindowLongPtr(HWnd, GWL_STYLE, style))
|
|
os::Printer::log("Could not change window style.");
|
|
|
|
RECT clientSize;
|
|
clientSize.top = 0;
|
|
clientSize.left = 0;
|
|
clientSize.right = getVideoDriver()->getScreenSize().Width;
|
|
clientSize.bottom = getVideoDriver()->getScreenSize().Height;
|
|
|
|
AdjustWindowRect(&clientSize, style, FALSE);
|
|
|
|
const s32 realWidth = clientSize.right - clientSize.left;
|
|
const s32 realHeight = clientSize.bottom - clientSize.top;
|
|
|
|
const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
|
|
const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
|
|
|
|
SetWindowPos(HWnd, HWND_TOP, windowLeft, windowTop, realWidth, realHeight,
|
|
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_SHOWWINDOW);
|
|
|
|
static_cast<CCursorControl*>(CursorControl)->updateBorderSize(CreationParams.Fullscreen, resize);
|
|
}
|
|
|
|
|
|
//! Minimizes the window.
|
|
void CIrrDeviceWin32::minimizeWindow()
|
|
{
|
|
WINDOWPLACEMENT wndpl;
|
|
wndpl.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(HWnd, &wndpl);
|
|
wndpl.showCmd = SW_SHOWMINNOACTIVE;
|
|
SetWindowPlacement(HWnd, &wndpl);
|
|
}
|
|
|
|
|
|
//! Maximizes the window.
|
|
void CIrrDeviceWin32::maximizeWindow()
|
|
{
|
|
WINDOWPLACEMENT wndpl;
|
|
wndpl.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(HWnd, &wndpl);
|
|
wndpl.showCmd = SW_SHOWMAXIMIZED;
|
|
SetWindowPlacement(HWnd, &wndpl);
|
|
}
|
|
|
|
|
|
//! Restores the window to its original size.
|
|
void CIrrDeviceWin32::restoreWindow()
|
|
{
|
|
WINDOWPLACEMENT wndpl;
|
|
wndpl.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(HWnd, &wndpl);
|
|
wndpl.showCmd = SW_SHOWNORMAL;
|
|
SetWindowPlacement(HWnd, &wndpl);
|
|
}
|
|
|
|
//! Move window to requested position
|
|
bool CIrrDeviceWin32::moveWindow(int x, int y)
|
|
{
|
|
if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen)
|
|
return false;
|
|
|
|
bool success = SetWindowPos(HWnd, HWND_TOP, x, y, -1, -1,
|
|
SWP_NOOWNERZORDER | SWP_NOSIZE);
|
|
|
|
return success;
|
|
}
|
|
|
|
//! Get current window position.
|
|
bool CIrrDeviceWin32::getWindowPosition(int* x, int* y)
|
|
{
|
|
if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen)
|
|
return false;
|
|
|
|
WINDOWPLACEMENT placement;
|
|
placement.length = sizeof(WINDOWPLACEMENT);
|
|
|
|
bool success = GetWindowPlacement(HWnd, &placement);
|
|
|
|
if (!success)
|
|
return false;
|
|
|
|
*x = (int)placement.rcNormalPosition.left;
|
|
*y = (int)placement.rcNormalPosition.top;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CIrrDeviceWin32::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)
|
|
{
|
|
if (JoyControl)
|
|
return JoyControl->activateJoysticks(joystickInfo);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
//! Set the current Gamma Value for the Display
|
|
bool CIrrDeviceWin32::setGammaRamp( f32 red, f32 green, f32 blue, f32 brightness, f32 contrast )
|
|
{
|
|
bool r;
|
|
u16 ramp[3][256];
|
|
|
|
calculateGammaRamp( ramp[0], red, brightness, contrast );
|
|
calculateGammaRamp( ramp[1], green, brightness, contrast );
|
|
calculateGammaRamp( ramp[2], blue, brightness, contrast );
|
|
|
|
HDC dc = GetDC(0);
|
|
r = SetDeviceGammaRamp ( dc, ramp ) == TRUE;
|
|
ReleaseDC(HWnd, dc);
|
|
return r;
|
|
}
|
|
|
|
//! Get the current Gamma Value for the Display
|
|
bool CIrrDeviceWin32::getGammaRamp( f32 &red, f32 &green, f32 &blue, f32 &brightness, f32 &contrast )
|
|
{
|
|
bool r;
|
|
u16 ramp[3][256];
|
|
|
|
HDC dc = GetDC(0);
|
|
r = GetDeviceGammaRamp ( dc, ramp ) == TRUE;
|
|
ReleaseDC(HWnd, dc);
|
|
|
|
if ( r )
|
|
{
|
|
calculateGammaFromRamp(red, ramp[0]);
|
|
calculateGammaFromRamp(green, ramp[1]);
|
|
calculateGammaFromRamp(blue, ramp[2]);
|
|
}
|
|
|
|
brightness = 0.f;
|
|
contrast = 0.f;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
//! Process system events
|
|
void CIrrDeviceWin32::handleSystemMessages()
|
|
{
|
|
MSG msg;
|
|
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
// No message translation because we don't use WM_CHAR and it would conflict with our
|
|
// deadkey handling.
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
if (ExternalWindow && msg.hwnd == HWnd)
|
|
WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
|
|
else
|
|
DispatchMessage(&msg);
|
|
|
|
if (msg.message == WM_QUIT)
|
|
Close = true;
|
|
}
|
|
}
|
|
|
|
|
|
//! Remove all messages pending in the system message loop
|
|
void CIrrDeviceWin32::clearSystemMessages()
|
|
{
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
|
|
{}
|
|
while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
|
|
{}
|
|
}
|
|
|
|
// shows last error in a messagebox to help internal debugging.
|
|
void CIrrDeviceWin32::ReportLastWinApiError()
|
|
{
|
|
// (based on code from ovidiucucu from http://www.codeguru.com/forum/showthread.php?t=318721)
|
|
LPCTSTR pszCaption = __TEXT("Windows SDK Error Report");
|
|
DWORD dwError = GetLastError();
|
|
|
|
if(NOERROR == dwError)
|
|
{
|
|
MessageBox(NULL, __TEXT("No error"), pszCaption, MB_OK);
|
|
}
|
|
else
|
|
{
|
|
const DWORD dwFormatControl = FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_SYSTEM;
|
|
|
|
LPVOID pTextBuffer = NULL;
|
|
DWORD dwCount = FormatMessage(dwFormatControl,
|
|
NULL,
|
|
dwError,
|
|
0,
|
|
(LPTSTR) &pTextBuffer,
|
|
0,
|
|
NULL);
|
|
if(0 != dwCount)
|
|
{
|
|
MessageBox(NULL, (LPCTSTR)pTextBuffer, pszCaption, MB_OK|MB_ICONERROR);
|
|
LocalFree(pTextBuffer);
|
|
}
|
|
else
|
|
{
|
|
MessageBox(NULL, __TEXT("Unknown error"), pszCaption, MB_OK|MB_ICONERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert an Irrlicht texture to a Windows cursor
|
|
// Based on http://www.codeguru.com/cpp/w-p/win32/cursors/article.php/c4529/
|
|
HCURSOR CIrrDeviceWin32::TextureToCursor(HWND hwnd, irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)
|
|
{
|
|
//
|
|
// create the bitmaps needed for cursors from the texture
|
|
|
|
HDC dc = GetDC(hwnd);
|
|
HDC andDc = CreateCompatibleDC(dc);
|
|
HDC xorDc = CreateCompatibleDC(dc);
|
|
HBITMAP andBitmap = CreateCompatibleBitmap(dc, sourceRect.getWidth(), sourceRect.getHeight());
|
|
HBITMAP xorBitmap = CreateCompatibleBitmap(dc, sourceRect.getWidth(), sourceRect.getHeight());
|
|
|
|
HBITMAP oldAndBitmap = (HBITMAP)SelectObject(andDc, andBitmap);
|
|
HBITMAP oldXorBitmap = (HBITMAP)SelectObject(xorDc, xorBitmap);
|
|
|
|
|
|
video::ECOLOR_FORMAT format = tex->getColorFormat();
|
|
u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8;
|
|
u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel;
|
|
u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel;
|
|
const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0);
|
|
data += sourceRect.UpperLeftCorner.Y*tex->getPitch();
|
|
for ( s32 y = 0; y < sourceRect.getHeight(); ++y )
|
|
{
|
|
data += bytesLeftGap;
|
|
for ( s32 x = 0; x < sourceRect.getWidth(); ++x )
|
|
{
|
|
video::SColor pixelCol;
|
|
pixelCol.setData((const void*)data, format);
|
|
data += bytesPerPixel;
|
|
|
|
if ( pixelCol.getAlpha() == 0 ) // transparent
|
|
{
|
|
SetPixel(andDc, x, y, RGB(255,255,255));
|
|
SetPixel(xorDc, x, y, RGB(0,0,0));
|
|
}
|
|
else // color
|
|
{
|
|
SetPixel(andDc, x, y, RGB(0,0,0));
|
|
SetPixel(xorDc, x, y, RGB(pixelCol.getRed(), pixelCol.getGreen(), pixelCol.getBlue()));
|
|
}
|
|
}
|
|
data += bytesRightGap;
|
|
}
|
|
tex->unlock();
|
|
|
|
SelectObject(andDc, oldAndBitmap);
|
|
SelectObject(xorDc, oldXorBitmap);
|
|
|
|
DeleteDC(xorDc);
|
|
DeleteDC(andDc);
|
|
|
|
ReleaseDC(hwnd, dc);
|
|
|
|
// create the cursor
|
|
|
|
ICONINFO iconinfo;
|
|
iconinfo.fIcon = false; // type is cursor not icon
|
|
iconinfo.xHotspot = hotspot.X;
|
|
iconinfo.yHotspot = hotspot.Y;
|
|
iconinfo.hbmMask = andBitmap;
|
|
iconinfo.hbmColor = xorBitmap;
|
|
|
|
HCURSOR cursor = CreateIconIndirect(&iconinfo);
|
|
|
|
DeleteObject(andBitmap);
|
|
DeleteObject(xorBitmap);
|
|
|
|
return cursor;
|
|
}
|
|
|
|
|
|
CIrrDeviceWin32::CCursorControl::CCursorControl(CIrrDeviceWin32* device, const core::dimension2d<u32>& wsize, HWND hwnd, bool fullscreen)
|
|
: Device(device), WindowSize(wsize), InvWindowSize(0.0f, 0.0f),
|
|
HWnd(hwnd), BorderX(0), BorderY(0),
|
|
UseReferenceRect(false), IsVisible(true)
|
|
, ActiveIcon(gui::ECI_NORMAL), ActiveIconStartTime(0)
|
|
{
|
|
if (WindowSize.Width!=0)
|
|
InvWindowSize.Width = 1.0f / WindowSize.Width;
|
|
|
|
if (WindowSize.Height!=0)
|
|
InvWindowSize.Height = 1.0f / WindowSize.Height;
|
|
|
|
updateBorderSize(fullscreen, false);
|
|
initCursors();
|
|
}
|
|
|
|
CIrrDeviceWin32::CCursorControl::~CCursorControl()
|
|
{
|
|
for ( u32 i=0; i < Cursors.size(); ++i )
|
|
{
|
|
for ( u32 f=0; f < Cursors[i].Frames.size(); ++f )
|
|
{
|
|
DestroyCursor(Cursors[i].Frames[f].IconHW);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CIrrDeviceWin32::CCursorControl::initCursors()
|
|
{
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_ARROW)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_CROSS)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_HAND)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_HELP)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_IBEAM)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_NO)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_WAIT)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZEALL)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENESW)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENWSE)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENS)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZEWE)) );
|
|
Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_UPARROW)) );
|
|
}
|
|
|
|
|
|
void CIrrDeviceWin32::CCursorControl::update()
|
|
{
|
|
if ( !Cursors[ActiveIcon].Frames.empty() && Cursors[ActiveIcon].FrameTime )
|
|
{
|
|
// update animated cursors. This could also be done by X11 in case someone wants to figure that out (this way was just easier to implement)
|
|
u32 now = Device->getTimer()->getRealTime();
|
|
u32 frame = ((now - ActiveIconStartTime) / Cursors[ActiveIcon].FrameTime) % Cursors[ActiveIcon].Frames.size();
|
|
SetCursor( Cursors[ActiveIcon].Frames[frame].IconHW );
|
|
}
|
|
}
|
|
|
|
//! Sets the active cursor icon
|
|
void CIrrDeviceWin32::CCursorControl::setActiveIcon(gui::ECURSOR_ICON iconId)
|
|
{
|
|
if ( iconId >= (s32)Cursors.size() )
|
|
return;
|
|
|
|
ActiveIcon = iconId;
|
|
ActiveIconStartTime = Device->getTimer()->getRealTime();
|
|
if ( Cursors[ActiveIcon].Frames.size() )
|
|
SetCursor( Cursors[ActiveIcon].Frames[0].IconHW );
|
|
}
|
|
|
|
|
|
//! Add a custom sprite as cursor icon.
|
|
gui::ECURSOR_ICON CIrrDeviceWin32::CCursorControl::addIcon(const gui::SCursorSprite& icon)
|
|
{
|
|
if ( icon.SpriteId >= 0 )
|
|
{
|
|
CursorW32 cW32;
|
|
cW32.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime;
|
|
|
|
for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i )
|
|
{
|
|
irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber;
|
|
irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber;
|
|
irr::core::rect<s32> rectIcon = icon.SpriteBank->getPositions()[rectId];
|
|
|
|
HCURSOR hc = Device->TextureToCursor(HWnd, icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot);
|
|
cW32.Frames.push_back( CursorFrameW32(hc) );
|
|
}
|
|
|
|
Cursors.push_back( cW32 );
|
|
return (gui::ECURSOR_ICON)(Cursors.size() - 1);
|
|
}
|
|
return gui::ECI_NORMAL;
|
|
}
|
|
|
|
|
|
//! replace the given cursor icon.
|
|
void CIrrDeviceWin32::CCursorControl::changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite& icon)
|
|
{
|
|
if ( iconId >= (s32)Cursors.size() )
|
|
return;
|
|
|
|
for ( u32 i=0; i < Cursors[iconId].Frames.size(); ++i )
|
|
DestroyCursor(Cursors[iconId].Frames[i].IconHW);
|
|
|
|
if ( icon.SpriteId >= 0 )
|
|
{
|
|
CursorW32 cW32;
|
|
cW32.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime;
|
|
for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i )
|
|
{
|
|
irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber;
|
|
irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber;
|
|
irr::core::rect<s32> rectIcon = icon.SpriteBank->getPositions()[rectId];
|
|
|
|
HCURSOR hc = Device->TextureToCursor(HWnd, icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot);
|
|
cW32.Frames.push_back( CursorFrameW32(hc) );
|
|
}
|
|
|
|
Cursors[iconId] = cW32;
|
|
}
|
|
}
|
|
|
|
|
|
//! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work.
|
|
core::dimension2di CIrrDeviceWin32::CCursorControl::getSupportedIconSize() const
|
|
{
|
|
core::dimension2di result;
|
|
|
|
result.Width = GetSystemMetrics(SM_CXCURSOR);
|
|
result.Height = GetSystemMetrics(SM_CYCURSOR);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
} // end namespace
|
|
|
|
#endif // _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|