irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp
cutealien c2dd664fc6 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-10-15 13:37:26 +02:00

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, &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];
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_