khanat-opennel-code/code/ryzom/client/src/input.cpp
2010-06-10 11:50:19 +02:00

534 lines
13 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////
// INCLUDE //
/////////////
#include "stdpch.h"
// Client
#include "actions.h"
#include "input.h"
#include "interface_v3/interface_manager.h"
#include "interface_v3/input_handler_manager.h"
#include "client_cfg.h"
#include "time_client.h"
#include "interface_v3/custom_mouse.h"
// 3D
#include "nel/3d/u_driver.h"
// Misc
#include "nel/misc/mouse_device.h"
#include "nel/misc/mouse_smoother.h"
#include "nel/misc/system_utils.h"
// Game Share
///////////
// USING //
///////////
using namespace NL3D;
using namespace NLMISC;
////////////
// EXTERN //
////////////
extern UDriver *Driver;
extern UMaterial GenericMat;
extern CActionsManager Actions; // Actions Manager.
////////////
// GLOBAL //
////////////
IMouseDevice *MouseDevice = NULL;
bool MouseHardware = false;
bool MouseFreeLook = false;
float MouseCursorSpeed = 1.f;
uint MouseCursorAcceleration = 0;
bool InitMouseFirstTime = true;
bool SetMousePosFirstTime = true;
// mask for mouse buttons that are known to be down
uint DownMouseButtons = 0;
#ifdef NL_OS_UNIX
// on X11 and cocoa, store whether the mouse was captured or not
bool MouseCapture = false;
#endif
//////////////
// FUNCTION //
//////////////
// *********************************************************************************
uint GetMouseButtonsState()
{
return DownMouseButtons;
}
// *********************************************************************************
// Initialize the mouse
bool InitMouseWithCursor (bool hardware)
{
Driver->showCursor(false);
// First init ?
if (MouseDevice)
{
// No.. change soft to hard or hard to soft ?
if (hardware ^ MouseHardware)
{
// Ok, reinit the mouse
Driver->enableLowLevelMouse (false, false);
MouseDevice = NULL;
MouseHardware = false;
}
}
// Get the new mouse state
MouseHardware = hardware;
// Reinit ?
if (MouseDevice == NULL)
{
if (!ClientCfg.DisableDirectInput)
{
// mouse capture not taken in account for hardware mouse
MouseDevice = Driver->enableLowLevelMouse(true, hardware);
if (!MouseDevice)
return false;
}
// Update mouse informations
UpdateMouse ();
if (InitMouseFirstTime)
{
InitMouseFirstTime = false;
}
else
{
if (!MouseFreeLook)
{
// Get the current mouse position
if (hardware)
{
if (CInterfaceManager::getInstance()->getPointer())
{
float x = (float)CInterfaceManager::getInstance()->getPointer()->getX()/(float)Driver->getWindowWidth();
float y = (float)CInterfaceManager::getInstance()->getPointer()->getY()/(float)Driver->getWindowHeight();
CustomMouse.updateCursor(); // update current hardware icon to avoid to have the plain arrow
Driver->showCursor(true);
if (SetMousePosFirstTime)
{
SetMousePosFirstTime = false;
}
else
{
Driver->setMousePos(x, y);
}
}
else
{
CustomMouse.updateCursor(); // update current hardware icon to avoid to have the plain arrow
Driver->showCursor(true);
}
}
else
{
CInterfaceManager *pIm = CInterfaceManager::getInstance();
CViewPointer *vp = pIm->getPointer();
Driver->showCursor(false);
SetMousePosFirstTime = false;
if (vp)
{
float x = (float) vp->getX();
float y = (float) vp->getY();
// First, hide the hardware mouse
if (MouseDevice)
{
MouseDevice->setMousePos(x, y);
}
else
{
uint width = Driver->getWindowWidth();
uint height = Driver->getWindowHeight();
if (width != 0 && height != 0)
{
Driver->setMousePos(x / width, y / height);
}
}
}
}
}
}
}
return true;
}
// *********************************************************************************
// Is mouse cursor hardware ?
bool IsMouseCursorHardware ()
{
return MouseHardware;
}
// *********************************************************************************
// Set the mouse mode. Call this method once per frame to update window size
void UpdateMouse ()
{
// Freelook ?
if (MouseFreeLook)
{
// Raw mode
if (MouseDevice)
{
MouseDevice->setMessagesMode(IMouseDevice::RawMode);
MouseDevice->setMouseAcceleration(ClientCfg.FreeLookAcceleration);
}
}
else
{
// Get the driver size
uint32 width, height;
Driver->getWindowSize(width, height);
// Set the mouse properties
if (MouseDevice)
{
MouseDevice->setMessagesMode(IMouseDevice::NormalMode);
MouseDevice->setMouseMode(IMouseDevice::XAxis, IMouseDevice::Clamped);
MouseDevice->setMouseMode(IMouseDevice::YAxis, IMouseDevice::Clamped);
CRect window (0, 0, width, height);
MouseDevice->setMouseFrame(window);
MouseDevice->setFactors(1.f/std::max((float)width, 1.0f), 1.f/std::max((float)height, 1.0f));
MouseDevice->setMouseSpeed(MouseCursorSpeed);
MouseDevice->setMouseAcceleration(MouseCursorAcceleration);
}
}
if (!IsSystemCursorCaptured())
{
DownMouseButtons = 0;
}
}
// *********************************************************************************
// Use this method to toggle the mouse (freelook <- cursor)
void SetMouseFreeLook ()
{
if (!MouseFreeLook)
{
MouseFreeLook = true;
if (MouseHardware)
Driver->showCursor(false);
else
{
CInterfaceManager *im = CInterfaceManager::getInstance();
if (im)
{
CViewPointer *pointer = im->getPointer();
if (pointer)
pointer->show (false);
}
}
UpdateMouse ();
}
#ifdef NL_OS_UNIX
// on X11 and cocoa the mouse needs to get pulled into the middle each update,
// else the cursor would reach the border of the window / desktop
// and freelook would hang
Driver->setMousePos(0.5f, 0.5f);
#endif
}
// *********************************************************************************
bool IsMouseFreeLook()
{
return MouseFreeLook;
}
// *********************************************************************************
// Use this method to toggle the mouse (freelook -> cursor)
void SetMouseCursor (bool updatePos)
{
if (MouseFreeLook)
{
// Get the last cursor
float x = 0.5f, y = 0.5f;
// Window size
uint width = Driver->getWindowWidth();
uint height = Driver->getWindowHeight();
// Update the interface pointer
CInterfaceManager *instance = CInterfaceManager::getInstance();
if (instance)
{
// Get the cursor instance
CViewPointer *cursor = instance->getPointer();
if (cursor)
{
sint32 ix, iy;
cursor->getPointerPos (ix, iy);
x = (float)ix / (float)width;
y = (float)iy / (float)height;
}
}
MouseFreeLook = false;
UpdateMouse ();
// Integer coordinates
sint ix = (sint)(x*(float)width+0.5f);
sint iy = (sint)(y*(float)height+0.5f);
if (updatePos)
{
if (MouseDevice)
MouseDevice->setMousePos((float)ix, (float)iy);
else
Driver->setMousePos(x, y);
if (MouseHardware)
{
Driver->setMousePos(x, y);
}
}
// Update the interface pointer
if (instance)
{
// Get the cursor instance
CViewPointer *cursor = instance->getPointer();
if (cursor)
{
cursor->setPointerPos(ix, iy);
cursor->setPointerDispPos(ix, iy);
}
}
CInputHandlerManager *IHM = CInputHandlerManager::getInstance ();
if (IHM)
{
IHM->resetPos (ix, iy);
}
// Show the cursor
if (MouseHardware)
{
Driver->showCursor(true);
}
else
{
CInterfaceManager *im = CInterfaceManager::getInstance();
if (im)
{
CViewPointer *pointer = im->getPointer();
if (pointer)
pointer->show (true);
}
}
}
}
// *********************************************************************************
// Use this method to set the cursor speed
void SetMouseSpeed (float speed)
{
MouseCursorSpeed = speed;
UpdateMouse ();
}
// *********************************************************************************
// Use this method to set the cursor acceleration
void SetMouseAcceleration (uint accel)
{
MouseCursorAcceleration = accel;
UpdateMouse ();
}
// *********************************************************************************
void CaptureSystemCursor()
{
if (IsSystemCursorCaptured()) return;
#ifdef NL_OS_WINDOWS
HWND drvWnd = Driver->getDisplay();
if (!drvWnd) return;
SetCapture(drvWnd);
#else
// on X11 and cocoa, set driver mouse capture on and store it locally as well
Driver->setCapture(MouseCapture = true);
#endif
}
// *********************************************************************************
void ReleaseSystemCursor()
{
if (!IsSystemCursorCaptured()) return;
#ifdef NL_OS_WINDOWS
// if hardware mouse and not in client area, then force to update its aspect by updating its pos
if (!IsSystemCursorInClientArea())
{
// force update
ShowCursor(FALSE);
ShowCursor(TRUE);
}
ReleaseCapture();
#else
// on X11 and cocoa, set driver mouse capture off and store it locally as well
Driver->setCapture(MouseCapture = false);
#endif
}
// *********************************************************************************
bool IsSystemCursorCaptured()
{
if (!Driver) return false;
#ifdef NL_OS_WINDOWS
return GetCapture() == Driver->getDisplay();
#else
return MouseCapture;
#endif
}
// *********************************************************************************
void HandleSystemCursorCapture(const CEvent &event)
{
if (event == EventMouseDownId)
{
CEventMouseDown &em = (CEventMouseDown &) event;
DownMouseButtons |= em.Button & (leftButton | middleButton | rightButton);
if (IsSystemCursorInClientArea())
{
CaptureSystemCursor();
}
}
if (event == EventMouseUpId)
{
// if all buttons up
CEventMouseDown &em = (CEventMouseDown &) event;
DownMouseButtons &= ~(em.Button & (leftButton | middleButton | rightButton));
if (DownMouseButtons == 0)
{
ReleaseSystemCursor();
}
}
// if focus was lost then says that all buttons are up from this app viewpoint
if (event == EventSetFocusId)
{
DownMouseButtons = 0;
}
}
// *********************************************************************************
bool IsSystemCursorInClientArea()
{
if (!Driver) return false;
#ifdef NL_OS_WINDOWS
HWND drvWnd = Driver->getDisplay();
if (!drvWnd) return false;
UDriver::CMode videoMode;
Driver->getCurrentScreenMode(videoMode);
if (!videoMode.Windowed || !IsMouseCursorHardware())
{
// just test visibility
return IsWindowVisible(drvWnd) != FALSE;
}
else
{
POINT cursPos;
// the mouse should be in the client area of the window
if (!GetCursorPos(&cursPos))
{
return false;
}
HWND wnd = WindowFromPoint(cursPos);
if (wnd != drvWnd)
{
return false; // not the same window
}
// want that the mouse be in the client area
RECT clientRect;
if (!GetClientRect(drvWnd, &clientRect))
{
return false;
}
POINT tl, br;
tl.x = clientRect.left;
tl.y = clientRect.top;
br.x = clientRect.right;
br.y = clientRect.bottom;
if (!ClientToScreen(drvWnd, &tl))
{
return false;
}
if (!ClientToScreen(drvWnd, &br))
{
return false;
}
if (cursPos.x < tl.x ||
cursPos.x >= br.x ||
cursPos.y < tl.y ||
cursPos.y >= br.y)
{
return false;
}
}
#else
// TODO for Linux and Mac OS
#endif
return true;
}
sint CNiceInputAuto::_Count = 0;
CNiceInputAuto::CNiceInputAuto()
{
if (_Count == 0)
{
Driver->enableLowLevelMouse(false, false); // but ignore direct input (win 32 msg only)
CustomMouse.setCursor("curs_default.tga", CRGBA::White, 0, 0x15, 0x18);
Driver->showCursor(true); // keep cursor visible in windowed mode
MouseDevice = NULL;
Driver->enableLowLevelKeyboard (false);
}
++ _Count;
}
CNiceInputAuto::~CNiceInputAuto()
{
-- _Count;
nlassert(_Count >= 0);
if (_Count == 0)
{
InitMouseWithCursor (ClientCfg.HardwareCursor);
Driver->enableLowLevelKeyboard (!ClientCfg.DisableDirectInputKeyboard); // the return value has already been tested at startup
}
}