irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp

1475 lines
43 KiB
C++
Raw Normal View History

// 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) {
2023-10-04 20:10:58 +02:00
if (m->group == 0) { // down
ClickCount++;
SetCapture(hWnd);
2023-10-04 20:10:58 +02:00
} 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;
2023-10-04 20:10:58 +02:00
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_