mirror of
https://github.com/minetest/irrlicht.git
synced 2024-12-26 10:40:31 +01:00
c2dd664fc6
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
1599 lines
44 KiB
C++
1599 lines
44 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
|
|
|
|
|
|
#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 "os.h"
|
|
|
|
#include "CTimer.h"
|
|
#include "irrString.h"
|
|
#include "COSOperator.h"
|
|
#include "dimension2d.h"
|
|
#include "IGUISpriteBank.h"
|
|
#include <winuser.h>
|
|
#include "SExposedVideoData.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>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_)
|
|
#include "CEGLManager.h"
|
|
#endif
|
|
|
|
#if defined(_IRR_COMPILE_WITH_OPENGL_)
|
|
#include "CWGLManager.h"
|
|
#endif
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OGLES1_
|
|
IVideoDriver* createOGLES1Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OGLES2_
|
|
IVideoDriver* createOGLES2Driver(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, ®size);
|
|
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, ®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 );
|
|
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];
|
|
snprintf_irr(logString, sizeof(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
|
|
|
|
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;
|
|
}
|
|
|
|
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 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 (wParam & MK_XBUTTON1)
|
|
event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA1;
|
|
if (wParam & MK_XBUTTON2)
|
|
event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA2;
|
|
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);
|
|
|
|
if ( event.KeyInput.Key == irr::KEY_SHIFT )
|
|
{
|
|
event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MAPVK_VSC_TO_VK_EX );
|
|
}
|
|
if ( event.KeyInput.Key == irr::KEY_CONTROL )
|
|
{
|
|
event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), 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), 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
|
|
WCHAR keyChars[2];
|
|
UINT scanCode = HIWORD(lParam);
|
|
int conversionResult = ToUnicodeEx(static_cast<UINT>(wParam),scanCode,allKeys,keyChars,2,0,KEYBOARD_INPUT_HKL);
|
|
if (conversionResult == 1)
|
|
event.KeyInput.Char = keyChars[0];
|
|
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 DefWindowProcW(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_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);
|
|
return 0;
|
|
}
|
|
return DefWindowProcW(hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
namespace irr
|
|
{
|
|
|
|
//! constructor
|
|
CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params)
|
|
: CIrrDeviceStub(params), HWnd(0), Resized(false),
|
|
ExternalWindow(false), Win32CursorControl(0), JoyControl(0),
|
|
WindowMaximized(params.WindowMaximized)
|
|
{
|
|
#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);
|
|
|
|
// 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
|
|
WNDCLASSEXW wcex;
|
|
wcex.cbSize = sizeof(WNDCLASSEXW);
|
|
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;
|
|
|
|
RegisterClassExW(&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 = CreateWindowW( ClassName, L"", style, windowLeft, windowTop,
|
|
realWidth, realHeight, NULL, NULL, hInstance, NULL);
|
|
if (!HWnd)
|
|
{
|
|
os::Printer::log("Window could not be created.", ELL_ERROR);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
KEYBOARD_INPUT_HKL = GetKeyboardLayout(0);
|
|
|
|
// inform driver about the window size etc.
|
|
resizeIfNecessary();
|
|
|
|
if (params.WindowMaximized)
|
|
maximizeWindow();
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CIrrDeviceWin32::~CIrrDeviceWin32()
|
|
{
|
|
delete JoyControl;
|
|
|
|
// unregister environment
|
|
for (u32 i=0; i< EnvMap.size(); ++i)
|
|
{
|
|
if (EnvMap[i].hWnd == HWnd)
|
|
{
|
|
EnvMap.erase(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! create the driver
|
|
void CIrrDeviceWin32::createDriver()
|
|
{
|
|
switch(CreationParams.DriverType)
|
|
{
|
|
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_OGLES1:
|
|
#ifdef _IRR_COMPILE_WITH_OGLES1_
|
|
switchToFullScreen();
|
|
|
|
ContextManager = new video::CEGLManager();
|
|
ContextManager->initialize(CreationParams, video::SExposedVideoData(HWnd));
|
|
|
|
VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager);
|
|
|
|
if (!VideoDriver)
|
|
os::Printer::log("Could not create OpenGL-ES1 driver.", ELL_ERROR);
|
|
#else
|
|
os::Printer::log("OpenGL-ES1 driver was not compiled in.", ELL_ERROR);
|
|
#endif
|
|
break;
|
|
case video::EDT_OGLES2:
|
|
#ifdef _IRR_COMPILE_WITH_OGLES2_
|
|
switchToFullScreen();
|
|
|
|
ContextManager = new video::CEGLManager();
|
|
ContextManager->initialize(CreationParams, video::SExposedVideoData(HWnd));
|
|
|
|
VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager);
|
|
|
|
if (!VideoDriver)
|
|
os::Printer::log("Could not create OpenGL-ES2 driver.", ELL_ERROR);
|
|
#else
|
|
os::Printer::log("OpenGL-ES2 driver was not compiled in.", ELL_ERROR);
|
|
#endif
|
|
break;
|
|
case video::EDT_WEBGL1:
|
|
os::Printer::log("WebGL1 driver not supported on Win32 device.", ELL_ERROR);
|
|
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)
|
|
{
|
|
snprintf_irr(tmp, sizeof(tmp), "Ignoring resize operation to (%ld %ld)", r.right, r.bottom);
|
|
os::Printer::log(tmp);
|
|
}
|
|
else
|
|
{
|
|
snprintf_irr(tmp, sizeof(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);
|
|
}
|
|
|
|
|
|
//! Sets the window icon.
|
|
bool CIrrDeviceWin32::setWindowIcon(const video::IImage *img)
|
|
{
|
|
// Ignore the img, instead load the ICON from resource file
|
|
// (This is minetest-specific!)
|
|
const HICON hicon = LoadIcon(GetModuleHandle(NULL),
|
|
MAKEINTRESOURCE(130) // The ID of the ICON defined in
|
|
// winresource.rc
|
|
);
|
|
|
|
if (hicon) {
|
|
SendMessage(HWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hicon));
|
|
SendMessage(HWnd, WM_SETICON, ICON_SMALL,
|
|
reinterpret_cast<LPARAM>(hicon));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//! 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 wchar_t* ClassName = L"CIrrDeviceWin32";
|
|
HINSTANCE hInstance = GetModuleHandle(0);
|
|
UnregisterClassW(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;
|
|
}
|
|
|
|
|
|
//! returns last state from maximizeWindow() and restoreWindow()
|
|
bool CIrrDeviceWin32::isWindowMaximized() const
|
|
{
|
|
return WindowMaximized;
|
|
}
|
|
|
|
|
|
//! switches to fullscreen
|
|
bool CIrrDeviceWin32::switchToFullScreen()
|
|
{
|
|
if (!CreationParams.Fullscreen)
|
|
return true;
|
|
|
|
// No border, title bar, etc. is already set up through getWindowStyle()
|
|
// We only set the window size to match the monitor.
|
|
|
|
MONITORINFO mi;
|
|
mi.cbSize = sizeof(mi);
|
|
if (GetMonitorInfo(MonitorFromWindow(HWnd,MONITOR_DEFAULTTOPRIMARY),&mi))
|
|
{
|
|
UINT flags = SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_FRAMECHANGED;
|
|
SetWindowPos(HWnd, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
|
|
mi.rcMonitor.right - mi.rcMonitor.left,
|
|
mi.rcMonitor.bottom - mi.rcMonitor.top, flags);
|
|
}
|
|
else
|
|
{
|
|
CreationParams.Fullscreen = false;
|
|
}
|
|
|
|
return CreationParams.Fullscreen;
|
|
}
|
|
|
|
|
|
//! returns the win32 cursor control
|
|
CIrrDeviceWin32::CCursorControl* CIrrDeviceWin32::getWin32CursorControl()
|
|
{
|
|
return Win32CursorControl;
|
|
}
|
|
|
|
void CIrrDeviceWin32::getWindowsVersion(core::stringc& out)
|
|
{
|
|
OSVERSIONINFO osvi;
|
|
|
|
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&osvi);
|
|
|
|
char tmp[255];
|
|
snprintf(tmp, sizeof(tmp), "Microsoft Windows %lu.%lu %s", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.szCSDVersion);
|
|
out.append(tmp);
|
|
}
|
|
|
|
//! 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);
|
|
|
|
WindowMaximized = true;
|
|
}
|
|
|
|
|
|
//! 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);
|
|
|
|
WindowMaximized = false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//! 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))
|
|
{}
|
|
}
|
|
|
|
|
|
//! Get the display density in dots per inch.
|
|
float CIrrDeviceWin32::getDisplayDensity() const
|
|
{
|
|
HDC hdc = GetDC(HWnd);
|
|
float dpi = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
ReleaseDC(HWnd, hdc);
|
|
return dpi;
|
|
}
|
|
|
|
|
|
// 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_
|