irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp
cutealien a3adfc196b CIrrDeviceWin32::yield() now uses Sleep(0) instead of Sleep(1).
We had changed that once before in the other direction in svn r421
Reason back then was "Sleep(0) doesn't allow any lower priority threads to execute"
But Microsoft changed the behaviour of Sleep(0) after Windows XP so that's no longer true.
And the costs of it is pretty high - due to this using a timer with a 15ms resolutions it meant not just giving up the thread but it also always waited for 15ms on Windows.
I also replaced a few sleep calls in examples for that reason with yield() calls.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6459 dfc29bdd-3216-0410-991c-e03cc46cb475
2023-04-03 15:32:41 +00:00

2191 lines
62 KiB
C++

// 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_
#if defined (__STRICT_ANSI__)
#error Compiling with __STRICT_ANSI__ not supported. g++ does set this when compiling with -std=c++11 or -std=c++0x. Use instead -std=gnu++11 or -std=gnu++0x. Or use -U__STRICT_ANSI__ to disable strict ansi.
#endif
#include "CIrrDeviceWin32.h"
#include "IEventReceiver.h"
#include "irrList.h"
#include "os.h"
#include "CTimer.h"
#include "irrString.h"
#include "COSOperator.h"
#include "dimension2d.h"
#include "IGUISpriteBank.h"
#include <winuser.h>
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
#include <mmsystem.h>
#include <regstr.h>
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.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
#if defined(_IRR_COMPILE_WITH_OPENGL_)
#include "CWGLManager.h"
#endif
namespace irr
{
namespace video
{
#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, IContextManager* contextManager);
#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
};
core::array<JoystickInfo> ActiveJoysticks;
#endif
SJoystickWin32Control(CIrrDeviceWin32* dev);
~SJoystickWin32Control();
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_)
static BOOL CALLBACK EnumJoysticks(LPCDIDEVICEINSTANCE lpddi, LPVOID cp);
void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi);
#endif
void pollJoysticks();
bool activateJoysticks(core::array<SJoystickInfo> & joystickInfo);
irr::core::stringc findJoystickName(int index, const JOYCAPS &caps) const;
};
SJoystickWin32Control::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::~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_)
BOOL CALLBACK SJoystickWin32Control::EnumJoysticks(LPCDIDEVICEINSTANCE lpddi, LPVOID cp)
{
SJoystickWin32Control* p=(SJoystickWin32Control*)cp;
p->directInputAddJoystick(lpddi);
return DIENUM_CONTINUE;
}
void SJoystickWin32Control::directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi)
{
//Get the GUID of the joystuck
const GUID guid = lpddi->guidInstance;
JoystickInfo activeJoystick;
activeJoystick.Index=ActiveJoysticks.size();
activeJoystick.guid=guid;
activeJoystick.Name=lpddi->tszProductName;
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;
}
ActiveJoysticks.push_back(activeJoystick);
}
#endif
void SJoystickWin32Control::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)
{
// 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)))
{
SEvent event;
event.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
event.JoystickEvent.Joystick = (u8)joystick;
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
if (0 == ActiveJoysticks.size())
return;
u32 joystick;
JOYINFOEX info;
for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
{
// 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(JOYERR_NOERROR == joyGetPosEx(ActiveJoysticks[joystick].Index, &info))
{
SEvent event;
event.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
event.JoystickEvent.Joystick = (u8)joystick;
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;
for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis)
event.JoystickEvent.Axis[axis] = 0;
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);
}
(void)Device->postEventFromUser(event);
}
}
#endif
#endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
}
/** This function is ported from SDL and released under zlib-license:
* Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> */
irr::core::stringc SJoystickWin32Control::findJoystickName(int index, const JOYCAPS &caps) const
{
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
// As a default use the name given in the joystick structure.
// It is always the same name, independent of joystick.
irr::core::stringc result(caps.szPname);
core::stringc key = core::stringc(REGSTR_PATH_JOYCONFIG)+ "\\" + caps.szRegKey + "\\" + REGSTR_KEY_JOYCURR;
HKEY hTopKey = HKEY_LOCAL_MACHINE;
HKEY hKey;
long regresult = RegOpenKeyExA(hTopKey, key.c_str(), 0, KEY_READ, &hKey);
if (regresult != ERROR_SUCCESS)
{
hTopKey = HKEY_CURRENT_USER;
regresult = RegOpenKeyExA(hTopKey, key.c_str(), 0, KEY_READ, &hKey);
}
if (regresult != ERROR_SUCCESS)
return result;
/* find the registry key name for the joystick's properties */
char regname[256];
DWORD regsize = sizeof(regname);
core::stringc regvalue = core::stringc("Joystick")+core::stringc(index+1) + REGSTR_VAL_JOYOEMNAME;
regresult = RegQueryValueExA(hKey, regvalue.c_str(), 0, 0, (LPBYTE)regname, &regsize);
RegCloseKey(hKey);
if (regresult != ERROR_SUCCESS)
return result;
/* open that registry key */
core::stringc regkey = core::stringc(REGSTR_PATH_JOYOEM) + "\\" + regname;
regresult = RegOpenKeyExA(hTopKey, regkey.c_str(), 0, KEY_READ, &hKey);
if (regresult != ERROR_SUCCESS)
return result;
/* find the size for the OEM name text */
regsize = sizeof(regvalue);
regresult = RegQueryValueEx(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0,
NULL, &regsize);
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, &regsize );
result = name;
}
delete[] name;
}
RegCloseKey(hKey);
return result;
#endif
return "";
}
bool SJoystickWin32Control::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)
{
#if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
joystickInfo.clear();
ActiveJoysticks.clear();
#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
const u32 numberOfJoysticks = ::joyGetNumDevs();
JOYINFOEX info;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
JoystickInfo activeJoystick;
SJoystickInfo returnInfo;
joystickInfo.reallocate(numberOfJoysticks);
ActiveJoysticks.reallocate(numberOfJoysticks);
u32 joystick = 0;
for(; joystick < numberOfJoysticks; ++joystick)
{
if(JOYERR_NOERROR == joyGetPosEx(joystick, &info)
&&
JOYERR_NOERROR == joyGetDevCaps(joystick,
&activeJoystick.Caps,
sizeof(activeJoystick.Caps)))
{
activeJoystick.Index = joystick;
ActiveJoysticks.push_back(activeJoystick);
returnInfo.Joystick = (u8)joystick;
returnInfo.Axes = activeJoystick.Caps.wNumAxes;
returnInfo.Buttons = activeJoystick.Caps.wNumButtons;
returnInfo.Name = findJoystickName(joystick, activeJoystick.Caps);
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;
};
// NOTE: This is global. We can have more than one Irrlicht Device at same time.
irr::core::array<SEnvMapper> EnvMap;
HKL KEYBOARD_INPUT_HKL=0;
unsigned int KEYBOARD_INPUT_CODEPAGE = 1252;
}
irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd)
{
const irr::u32 end = EnvMap.size();
for ( irr::u32 i=0; i < end; ++i )
{
const SEnvMapper& env = EnvMap[i];
if ( env.hWnd == hWnd )
return env.irrDev;
}
return 0;
}
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->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_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::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::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::KEY_RCONTROL;
}
if ( event.KeyInput.Key == 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::KEY_RMENU;
}
GetKeyboardState(allKeys);
event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0);
event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0);
// Handle unicode and deadkeys in a way that works since Windows 95 and nt4.0
// Using ToUnicode instead would be shorter, but would to my knowledge not run on 95 and 98.
WORD keyChars[2];
UINT scanCode = HIWORD(lParam);
int conversionResult = ToAsciiEx(static_cast<UINT>(wParam),scanCode,allKeys,keyChars,0,KEYBOARD_INPUT_HKL);
if (conversionResult == 1)
{
WORD unicodeChar;
MultiByteToWideChar(
KEYBOARD_INPUT_CODEPAGE,
MB_PRECOMPOSED, // default
(LPCSTR)keyChars,
sizeof(keyChars),
(WCHAR*)&unicodeChar,
1 );
event.KeyInput.Char = unicodeChar;
}
else
event.KeyInput.Char = 0;
// 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->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 = static_cast<size_t>(wParam);
event.UserEvent.UserData2 = static_cast<size_t>(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
// 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 fschar_t* ClassName = __TEXT("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
wcex.hIcon = (HICON)LoadImage(hInstance, __TEXT("irrlicht.ico"), IMAGE_ICON, 0,0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
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 = getWindowStyle(CreationParams.Fullscreen, CreationParams.WindowResizable > 0 ? true : false);
AdjustWindowRect(&clientSize, style, FALSE);
const s32 realWidth = clientSize.right - clientSize.left;
const s32 realHeight = clientSize.bottom - clientSize.top;
s32 windowLeft = (CreationParams.WindowPosition.X == -1 ?
(GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2 :
CreationParams.WindowPosition.X);
s32 windowTop = (CreationParams.WindowPosition.Y == -1 ?
(GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2 :
CreationParams.WindowPosition.Y);
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
for (u32 i=0; i< EnvMap.size(); ++i)
{
if (EnvMap[i].hWnd == HWnd)
{
EnvMap.erase(i);
break;
}
}
switchToFullScreen(true);
}
//! create the driver
void CIrrDeviceWin32::createDriver()
{
switch(CreationParams.DriverType)
{
case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS:
os::Printer::log("DIRECT3D8 Driver is no longer supported in Irrlicht. Try another one.", ELL_ERROR);
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
break;
case video::EDT_OPENGL:
#ifdef _IRR_COMPILE_WITH_OPENGL_
switchToFullScreen();
ContextManager = new video::CWGLManager();
ContextManager->initialize(CreationParams, video::SExposedVideoData(HWnd));
VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, ContextManager);
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:
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();
return !Close;
}
//! Pause the current process for the minimum time allowed only to allow other processes to execute
void CIrrDeviceWin32::yield()
{
Sleep(0);
}
//! 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;
}
DWORD CIrrDeviceWin32::getWindowStyle(bool fullscreen, bool resizable) const
{
if ( fullscreen )
return WS_POPUP;
if ( resizable )
return WS_THICKFRAME | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
return WS_BORDER | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
}
//! 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 = static_cast<HWND>(windowId);
HDC dc = GetDC(hwnd);
if ( dc )
{
RECT rect;
GetClientRect(hwnd, &rect);
const void* memory = (const void *)image->getData();
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);
}
ReleaseDC(hwnd, dc);
}
return true;
}
//! notifies the device that it should close itself
void CIrrDeviceWin32::closeDevice()
{
if (!ExternalWindow)
{
MSG msg;
PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
PostQuitMessage(0);
PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
DestroyWindow(HWnd);
const fschar_t* ClassName = __TEXT("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
{
return (GetActiveWindow() == HWnd);
}
//! returns if window has focus
bool CIrrDeviceWin32::isWindowFocused() const
{
bool ret = (GetFocus() == HWnd);
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;
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 occurred while changing to fullscreen.", ELL_ERROR);
break;
}
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;
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)
{
out.append("Microsoft Windows 8 or later ");
}
}
if (bOsVersionInfoEx)
{
if (osvi.dwMajorVersion == 6)
{
DWORD dwType = 0; // (PRODUCT_UNDEFINED not available on MinGW)
HMODULE hmKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if ( hmKernel32 )
{
PGPI pGPI = (PGPI)GetProcAddress(hmKernel32, "GetProductInfo");
if ( pGPI )
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 (irr::core::stringc("WINNT").equals_ignore_case(szProductType))
out.append("Professional ");
if (irr::core::stringc("LANMANNT").equals_ignore_case(szProductType))
out.append("Server ");
if (irr::core::stringc("SERVERNT").equals_ignore_case(szProductType))
out.append("Advanced Server ");
}
// Display version, service pack (if any), and build number.
char tmp[255];
if (osvi.dwMajorVersion <= 4 )
{
sprintf(tmp, "version %lu.%lu %s (Build %lu)",
osvi.dwMajorVersion,
osvi.dwMinorVersion,
irr::core::stringc(osvi.szCSDVersion).c_str(),
osvi.dwBuildNumber & 0xFFFF);
}
else
{
sprintf(tmp, "%s (Build %lu)", 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;
}
//! Resize the render window.
void CIrrDeviceWin32::setWindowSize(const irr::core::dimension2d<u32>& size)
{
if (ExternalWindow || !getVideoDriver() || CreationParams.Fullscreen)
return;
// get size of the window for the give size of the client area
DWORD style = static_cast<DWORD>(GetWindowLongPtr(HWnd, GWL_STYLE));
DWORD exStyle = static_cast<DWORD>(GetWindowLongPtr(HWnd, GWL_EXSTYLE));
RECT clientSize;
clientSize.top = 0;
clientSize.left = 0;
clientSize.right = size.Width;
clientSize.bottom = size.Height;
AdjustWindowRectEx(&clientSize, style, false, exStyle);
const s32 realWidth = clientSize.right - clientSize.left;
const s32 realHeight = clientSize.bottom - clientSize.top;
UINT flags = SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER;
SetWindowPos(HWnd, HWND_TOP, 0, 0, realWidth, realHeight, flags);
}
//! Sets if the window should be resizable in windowed mode.
void CIrrDeviceWin32::setResizable(bool resize)
{
if (ExternalWindow || !getVideoDriver() || CreationParams.Fullscreen)
return;
LONG_PTR style = (LONG_PTR)getWindowStyle(false, resize);
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, static_cast<DWORD>(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);
}
core::position2di CIrrDeviceWin32::getWindowPosition()
{
WINDOWPLACEMENT wndpl;
wndpl.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(HWnd, &wndpl))
{
return core::position2di((int)wndpl.rcNormalPosition.left,
(int)wndpl.rcNormalPosition.top);
}
else
{
// No reason for this to happen
os::Printer::log("Failed to retrieve window location", ELL_ERROR);
return core::position2di(-1, -1);
}
}
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))
{
if (ExternalWindow && msg.hwnd == HWnd)
{
if (msg.hwnd == HWnd)
{
WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// No message translation because we don't use WM_CHAR and it would conflict with our
// deadkey handling.
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);
}
}
}
// Same function Windows offers in VersionHelpers.h, but we can't use that as it's not available in older sdk's (minimum is SDK 8.1)
bool CIrrDeviceWin32::isWindowsVistaOrGreater()
{
#if (_WIN32_WINNT >= 0x0500)
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 6; // Windows Vista
if ( !GetVersionEx((OSVERSIONINFO*)&osvi) )
{
return false;
}
return VerifyVersionInfo(&osvi, VER_MAJORVERSION, VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL)) ? true : false;
#else
return false;
#endif
}
// 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_