vkdoom_m/src/win32/i_mouse.cpp
Randy Heit 5245e8d4dd - I_CheckNativeMouse() now checks the foreground window to determine if the
mouse should be grabbed. This fixes the case where you start the game in
  the background and it grabs the mouse anyway.
- Changed raw mouse grabbing to call ShowCursor() directly instead of through
  SetCursorState(), since changing the pointer isn't working with it
  (probably due to the lack of legacy mouse messages), and the others work
  fine by setting an invisible cursor.
- Fixed: Raw mouse input passes wheel movements in an unsigned field, but the
  value is signed, so it requires a cast to use it.
- Reverted accidental changes to src/sdl/i_input.cpp.

SVN r1605 (trunk)
2009-05-26 00:30:51 +00:00

1180 lines
27 KiB
C++

#ifdef _MSC_VER
// Turn off "conversion from 'LONG_PTR' to 'LONG', possible loss of data"
// generated by SetClassLongPtr().
#pragma warning(disable:4244)
#endif
// HEADER FILES ------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#define DIRECTINPUT_VERSION 0x800
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <dinput.h>
#define USE_WINDOWS_DWORD
#include "i_input.h"
#include "i_system.h"
#include "d_event.h"
#include "d_gui.h"
#include "c_cvars.h"
#include "doomdef.h"
#include "doomstat.h"
#include "win32iface.h"
#include "rawinput.h"
// MACROS ------------------------------------------------------------------
#define DINPUT_BUFFERSIZE 32
// TYPES -------------------------------------------------------------------
class FRawMouse : public FMouse
{
public:
FRawMouse();
~FRawMouse();
bool GetDevice();
void ProcessInput();
bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
void Grab();
void Ungrab();
protected:
bool Grabbed;
POINT UngrabbedPointerPos;
};
class FDInputMouse : public FMouse
{
public:
FDInputMouse();
~FDInputMouse();
bool GetDevice();
void ProcessInput();
bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
void Grab();
void Ungrab();
protected:
LPDIRECTINPUTDEVICE8 Device;
bool Grabbed;
};
class FWin32Mouse : public FMouse
{
public:
FWin32Mouse();
~FWin32Mouse();
bool GetDevice();
void ProcessInput();
bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
void Grab();
void Ungrab();
protected:
void CenterMouse(int x, int y);
POINT UngrabbedPointerPos;
LONG PrevX, PrevY;
bool Grabbed;
};
enum EMouseMode
{
MM_None,
MM_Win32,
MM_DInput,
MM_RawInput
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void SetCursorState(bool visible);
static FMouse *CreateWin32Mouse();
static FMouse *CreateDInputMouse();
static FMouse *CreateRawMouse();
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern LPDIRECTINPUT8 g_pdi;
extern LPDIRECTINPUT g_pdi3;
extern bool GUICapture;
extern bool HaveFocus;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static bool NativeMouse;
static EMouseMode MouseMode = MM_None;
static FMouse *(*MouseFactory[])() =
{
CreateWin32Mouse,
CreateDInputMouse,
CreateRawMouse
};
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FMouse *Mouse;
HCURSOR TheArrowCursor;
HCURSOR TheInvisibleCursor;
CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, m_noprescale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, m_filter, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Int, in_mouse, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
{
if (self < 0)
{
self = 0;
}
else if (self > 3)
{
self = 3;
}
else
{
I_StartupMouse();
}
}
CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
{
if (self < 0)
{
self = 0;
}
else if (self > 2)
{
self = 2;
}
}
// CODE --------------------------------------------------------------------
//==========================================================================
//
// SetCursorState
//
// Ensures the cursor is either visible or invisible.
//
//==========================================================================
static void SetCursorState(bool visible)
{
HCURSOR usingCursor = visible ? TheArrowCursor : TheInvisibleCursor;
SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)usingCursor);
if (HaveFocus)
{
SetCursor(usingCursor);
}
}
//==========================================================================
//
// CaptureMode_InGame
//
//==========================================================================
static bool CaptureMode_InGame()
{
if (mouse_capturemode == 2)
{
return true;
}
else if (mouse_capturemode == 1)
{
return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE;
}
else
{
return gamestate == GS_LEVEL;
}
}
//==========================================================================
//
// I_CheckNativeMouse
//
// Should we be capturing mouse input, or should we let the OS have normal
// control of it (i.e. native mouse)?
//
//==========================================================================
void I_CheckNativeMouse(bool preferNative)
{
bool windowed = (screen == NULL) || !screen->IsFullscreen();
bool want_native;
if (!windowed)
{
want_native = false;
}
else
{
want_native =
(GetForegroundWindow() != Window) ||
!CaptureMode_InGame() ||
GUICapture ||
paused ||
preferNative ||
!use_mouse ||
demoplayback;
}
//Printf ("%d %d %d\n", wantNative, preferNative, NativeMouse);
if (want_native != NativeMouse)
{
if (Mouse != NULL)
{
NativeMouse = want_native;
if (want_native)
{
Mouse->Ungrab();
}
else
{
Mouse->Grab();
}
}
}
}
//==========================================================================
//
// FMouse - Constructor
//
//==========================================================================
FMouse::FMouse()
{
LastX = LastY = 0;
ButtonState = 0;
}
//==========================================================================
//
// FMouse :: PostMouseMove
//
// Posts a mouse movement event, potentially averaging it with the previous
// movement. If there is no movement to post, then no event is generated.
//
//==========================================================================
void FMouse::PostMouseMove(int x, int y)
{
event_t ev = { 0 };
if (m_filter)
{
ev.x = (x + LastX) / 2;
ev.y = (y + LastY) / 2;
}
else
{
ev.x = x;
ev.y = y;
}
LastX = x;
LastY = y;
if (ev.x | ev.y)
{
ev.type = EV_Mouse;
D_PostEvent(&ev);
}
}
//==========================================================================
//
// FMouse :: WheelMoved
//
// Generates events for a wheel move. Events are generated for every
// WHEEL_DELTA units that the wheel has moved. In normal mode, each move
// generates both a key down and a key up event. In GUI mode, only one
// event is generated for each unit of movement.
//
//==========================================================================
void FMouse::WheelMoved(int wheelmove)
{
event_t ev = { 0 };
int dir;
if (GUICapture)
{
ev.type = EV_GUI_Event;
if (wheelmove < 0)
{
dir = WHEEL_DELTA;
ev.subtype = EV_GUI_WheelDown;
}
else
{
dir = -WHEEL_DELTA;
ev.subtype = EV_GUI_WheelUp;
}
/* FIXME
ev.data3 = ((KeyState[VK_SHIFT]&128) ? GKM_SHIFT : 0) |
((KeyState[VK_CONTROL]&128) ? GKM_CTRL : 0) |
((KeyState[VK_MENU]&128) ? GKM_ALT : 0);
*/
while (abs(wheelmove) >= WHEEL_DELTA)
{
D_PostEvent(&ev);
wheelmove += dir;
}
}
else
{
if (wheelmove < 0)
{
dir = WHEEL_DELTA;
ev.data1 = KEY_MWHEELDOWN;
}
else
{
dir = -WHEEL_DELTA;
ev.data1 = KEY_MWHEELUP;
}
while (abs(wheelmove) >= WHEEL_DELTA)
{
ev.type = EV_KeyDown;
D_PostEvent(&ev);
ev.type = EV_KeyUp;
D_PostEvent(&ev);
wheelmove += dir;
}
}
}
//==========================================================================
//
// FMouse :: PostButtonEvent
//
// Posts a mouse button up/down event. Down events are always posted. Up
// events will only be sent if the button is currently marked as down.
//
//==========================================================================
void FMouse::PostButtonEvent(int button, bool down)
{
event_t ev = { 0 };
int mask = 1 << button;
ev.data1 = KEY_MOUSE1 + button;
if (down)
{
ButtonState |= mask;
ev.type = EV_KeyDown;
D_PostEvent(&ev);
}
else if (ButtonState & mask)
{
ButtonState &= ~mask;
ev.type = EV_KeyUp;
D_PostEvent(&ev);
}
}
//==========================================================================
//
// FMouse :: ClearButtonState
//
// Sends key up events for all buttons that are currently down and marks
// them as up. Used when focus is lost and we can no longer track up events,
// so get them marked up right away.
//
//==========================================================================
void FMouse::ClearButtonState()
{
if (ButtonState != 0)
{
int i, mask;
event_t ev = { 0 };
ev.type = EV_KeyUp;
for (i = sizeof(ButtonState) * 8, mask = 1; i > 0; --i, mask <<= 1)
{
if (ButtonState & mask)
{
ev.data1 = KEY_MOUSE1 + (int)sizeof(ButtonState) * 8 - i;
D_PostEvent(&ev);
}
}
ButtonState = 0;
}
}
//==========================================================================
//
// CreateRawMouse
//
//==========================================================================
static FMouse *CreateRawMouse()
{
return new FRawMouse;
}
//==========================================================================
//
// FRawMouse - Constructor
//
//==========================================================================
FRawMouse::FRawMouse()
{
Grabbed = false;
}
//==========================================================================
//
// FRawMouse - Destructor
//
//==========================================================================
FRawMouse::~FRawMouse()
{
Ungrab();
}
//==========================================================================
//
// FRawMouse :: GetDevice
//
// Ensure the API is present and we can listen for mouse input.
//
//==========================================================================
bool FRawMouse::GetDevice()
{
RAWINPUTDEVICE rid;
if (MyRegisterRawInputDevices == NULL)
{
return false;
}
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = 0;
rid.hwndTarget = Window;
if (!MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
}
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL; // Must be NULL for RIDEV_REMOVE.
MyRegisterRawInputDevices(&rid, 1, sizeof(rid));
return true;
}
//==========================================================================
//
// FRawMouse :: ProcessInput
//
// All input comes through WM_INPUT messages, so nothing to do here.
//
//==========================================================================
void FRawMouse::ProcessInput()
{
}
//==========================================================================
//
// FRawMouse :: Grab
//
//==========================================================================
extern BOOL AppActive;
void FRawMouse::Grab()
{
if (!Grabbed)
{
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY;
rid.hwndTarget = Window;
if (MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
GetCursorPos(&UngrabbedPointerPos);
Grabbed = true;
while (ShowCursor(FALSE) >= 0)
{ }
// By setting the cursor position, we force the pointer image
// to change right away instead of having it delayed until
// some time in the future.
SetCursorPos(0, 0);
}
}
}
//==========================================================================
//
// FRawMouse :: Ungrab
//
//==========================================================================
void FRawMouse::Ungrab()
{
if (Grabbed)
{
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
if (MyRegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
Grabbed = false;
ClearButtonState();
}
ShowCursor(TRUE);
SetCursorPos(UngrabbedPointerPos.x, UngrabbedPointerPos.y);
}
}
//==========================================================================
//
// FRawMouse :: WndProcHook
//
//==========================================================================
bool FRawMouse::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
if (!Grabbed)
{
return false;
}
if (message == WM_INPUT)
{
BYTE buffer[40];
UINT size = sizeof(buffer);
int i;
USHORT mask;
if (MyGetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) > 0)
{
RAWINPUT *raw = (RAWINPUT *)buffer;
if (raw->header.dwType != RIM_TYPEMOUSE)
{
return false;
}
// Check buttons. The up and down motions are stored in the usButtonFlags field.
// The ulRawButtons field, unfortunately, is device-dependant, and may well
// not contain any data at all. This means it is apparently impossible
// to read more than five mouse buttons with Windows, because RI_MOUSE_WHEEL
// gets in the way when trying to extrapolate to more than five.
for (i = 0, mask = 1; i < 5; ++i)
{
if (raw->data.mouse.usButtonFlags & mask) // button down
{
PostButtonEvent(i, true);
}
mask <<= 1;
if (raw->data.mouse.usButtonFlags & mask) // button up
{
PostButtonEvent(i, false);
}
mask <<= 1;
}
if (raw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL)
{
WheelMoved((SHORT)raw->data.mouse.usButtonData);
}
PostMouseMove(m_noprescale ? raw->data.mouse.lLastX : raw->data.mouse.lLastX<<2,
-raw->data.mouse.lLastY);
}
return false;
}
else if (message == WM_SYSCOMMAND)
{
wParam &= 0xFFF0;
if (wParam == SC_MOVE || wParam == SC_SIZE)
{
return true;
}
}
return false;
}
//==========================================================================
//
// CreateDInputMouse
//
//==========================================================================
static FMouse *CreateDInputMouse()
{
return new FDInputMouse;
}
//==========================================================================
//
// FDInputMouse - Constructor
//
//==========================================================================
FDInputMouse::FDInputMouse()
{
Device = NULL;
Grabbed = false;
}
//==========================================================================
//
// FDInputMouse - Destructor
//
//==========================================================================
FDInputMouse::~FDInputMouse()
{
if (Device != NULL)
{
Device->Release();
Device = NULL;
}
}
//==========================================================================
//
// FDInputMouse :: GetDevice
//
// Create the device interface and initialize it.
//
//==========================================================================
bool FDInputMouse::GetDevice()
{
HRESULT hr;
if (g_pdi3 != NULL)
{ // DirectInput3 interface
hr = g_pdi3->CreateDevice(GUID_SysMouse, (LPDIRECTINPUTDEVICE*)&Device, NULL);
}
else if (g_pdi != NULL)
{ // DirectInput8 interface
hr = g_pdi->CreateDevice(GUID_SysMouse, &Device, NULL);
}
else
{
hr = -1;
}
if (FAILED(hr))
{
return false;
}
// How many buttons does this mouse have?
DIDEVCAPS_DX3 caps = { sizeof(caps) };
hr = Device->GetCapabilities((DIDEVCAPS *)&caps);
// If that failed, just assume four buttons.
if (FAILED(hr))
{
caps.dwButtons = 4;
}
// Now select the data format with enough buttons for this mouse.
// (Can we use c_dfDIMouse2 with DirectInput3? If so, then we could just set
// that unconditionally.)
hr = Device->SetDataFormat(caps.dwButtons <= 4 ? &c_dfDIMouse : &c_dfDIMouse2);
if (FAILED(hr))
{
ufailit:
Device->Release();
Device = NULL;
return false;
}
// Set cooperative level.
hr = Device->SetCooperativeLevel(Window, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr))
{
goto ufailit;
}
// Set buffer size so we can use buffered input.
DIPROPDWORD prop;
prop.diph.dwSize = sizeof(prop);
prop.diph.dwHeaderSize = sizeof(prop.diph);
prop.diph.dwObj = 0;
prop.diph.dwHow = DIPH_DEVICE;
prop.dwData = DINPUT_BUFFERSIZE;
hr = Device->SetProperty(DIPROP_BUFFERSIZE, &prop.diph);
if (FAILED(hr))
{
goto ufailit;
}
return true;
}
//==========================================================================
//
// FDInputMouse::WndProcHook
//
//==========================================================================
bool FDInputMouse::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
// Do not allow window sizing or moving activity while the mouse is
// grabbed, because they will never be able to complete, causing mouse
// input to hang until the mouse is ungrabbed (e.g. by alt-tabbing away).
if (Grabbed && message == WM_SYSCOMMAND)
{
wParam &= 0xFFF0;
if (wParam == SC_MOVE || wParam == SC_SIZE)
{
return true;
}
}
return false;
}
//==========================================================================
//
// FDInputMouse :: ProcessInput
//
// Posts any events that have accumulated since the previous call.
//
//==========================================================================
void FDInputMouse::ProcessInput()
{
DIDEVICEOBJECTDATA od;
DWORD dwElements;
HRESULT hr;
int count = 0;
int dx, dy;
dx = 0;
dy = 0;
if (!Grabbed)
return;
event_t ev = { 0 };
for (;;)
{
DWORD cbObjectData = g_pdi3 ? sizeof(DIDEVICEOBJECTDATA_DX3) : sizeof(DIDEVICEOBJECTDATA);
dwElements = 1;
hr = Device->GetDeviceData(cbObjectData, &od, &dwElements, 0);
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
{
Grab();
hr = Device->GetDeviceData(cbObjectData, &od, &dwElements, 0);
}
/* Unable to read data or no data available */
if (FAILED(hr) || !dwElements)
break;
count++;
/* Look at the element to see what happened */
// GCC does not like putting the DIMOFS_ macros in case statements,
// so use ifs instead.
if (od.dwOfs == (DWORD)DIMOFS_X)
{
dx += od.dwData;
}
else if (od.dwOfs == (DWORD)DIMOFS_Y)
{
dy += od.dwData;
}
else if (od.dwOfs == (DWORD)DIMOFS_Z)
{
WheelMoved(od.dwData);
}
else if (od.dwOfs >= (DWORD)DIMOFS_BUTTON0 && od.dwOfs <= (DWORD)DIMOFS_BUTTON7)
{
/* [RH] Mouse button events mimic keydown/up events */
if (!GUICapture)
{
PostButtonEvent(od.dwOfs - DIMOFS_BUTTON0, !!(od.dwData & 0x80));
}
}
}
PostMouseMove(m_noprescale ? dx : dx<<2, -dy);
}
//==========================================================================
//
// FDInputMouse :: Grab
//
//==========================================================================
void FDInputMouse::Grab()
{
if (SUCCEEDED(Device->Acquire()))
{
Grabbed = true;
SetCursorState(false);
}
}
//==========================================================================
//
// FDInputMouse :: Ungrab
//
//==========================================================================
void FDInputMouse::Ungrab()
{
Device->Unacquire();
Grabbed = false;
SetCursorState(true);
ClearButtonState();
}
/**************************************************************************/
/**************************************************************************/
//==========================================================================
//
// CreateWin32Mouse
//
//==========================================================================
static FMouse *CreateWin32Mouse()
{
return new FWin32Mouse;
}
//==========================================================================
//
// FWin32Mouse - Constructor
//
//==========================================================================
FWin32Mouse::FWin32Mouse()
{
GetCursorPos(&UngrabbedPointerPos);
Grabbed = false;
}
//==========================================================================
//
// FWin32Mouse - Destructor
//
//==========================================================================
FWin32Mouse::~FWin32Mouse()
{
Ungrab();
}
//==========================================================================
//
// FWin32Mouse :: GetDevice
//
// The Win32 mouse is always available, since it is the lowest common
// denominator. (Even if it's not connected, it is still considered as
// "available"; it just won't generate any events.)
//
//==========================================================================
bool FWin32Mouse::GetDevice()
{
return true;
}
//==========================================================================
//
// FWin32Mouse :: ProcessInput
//
// Get current mouse position and post events if the mouse has moved from
// last time.
//
//==========================================================================
void FWin32Mouse::ProcessInput()
{
POINT pt;
int x, y;
if (!Grabbed || !GetCursorPos(&pt))
{
return;
}
x = pt.x - PrevX;
y = PrevY - pt.y;
if (!m_noprescale)
{
x *= 3;
y *= 2;
}
if (x | y)
{
CenterMouse(pt.x, pt.y);
}
PostMouseMove(x, y);
}
//==========================================================================
//
// FWin32Mouse :: WndProcHook
//
// Intercepts mouse-related window messages if the mouse is grabbed.
//
//==========================================================================
bool FWin32Mouse::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
{
if (!Grabbed)
{
return false;
}
if (message == WM_SIZE)
{
if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED)
{
CenterMouse(-1, -1);
return true;
}
}
else if (message == WM_MOVE)
{
CenterMouse(-1, -1);
return true;
}
else if (message == WM_SYSCOMMAND)
{
// Do not enter the window moving and sizing modes while grabbed,
// because those require the mouse.
wParam &= 0xFFF0;
if (wParam == SC_MOVE || wParam == SC_SIZE)
{
return true;
}
}
else if (message == WM_MOUSEWHEEL)
{
WheelMoved(HIWORD(wParam));
return true;
}
else if (message >= WM_LBUTTONDOWN && message <= WM_MBUTTONUP)
{
int action = (message - WM_LBUTTONDOWN) % 3;
int button = (message - WM_LBUTTONDOWN) / 3;
if (action == 2)
{ // double clicking we care not about.
return false;
}
event_t ev = { 0 };
ev.type = action ? EV_KeyUp : EV_KeyDown;
ev.data1 = KEY_MOUSE1 + button;
if (action)
{
ButtonState &= ~(1 << button);
}
else
{
ButtonState |= 1 << button;
}
D_PostEvent(&ev);
return true;
}
else if (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONUP)
{
// Microsoft's (lack of) documentation for the X buttons is unclear on whether
// or not simultaneous pressing of multiple X buttons will ever be merged into
// a single message. Winuser.h describes the button field as being filled with
// flags, which suggests that it could merge them. My testing
// indicates it does not, but I will assume it might in the future.
WORD xbuttons = GET_XBUTTON_WPARAM(wParam);
event_t ev = { 0 };
ev.type = (message == WM_XBUTTONDOWN) ? EV_KeyDown : EV_KeyUp;
// There are only two X buttons defined presently, so I extrapolate from
// the current winuser.h values to support up to 8 mouse buttons.
for (int i = 0; i < 5; ++i, xbuttons >>= 1)
{
if (xbuttons & 1)
{
ev.data1 = KEY_MOUSE4 + i;
if (ev.type == EV_KeyDown)
{
ButtonState |= 1 << (i + 4);
}
else
{
ButtonState &= ~(1 << (i + 4));
}
D_PostEvent(&ev);
}
}
*result = TRUE;
return true;
}
return false;
}
//==========================================================================
//
// FWin32Mouse :: Grab
//
// Hides the mouse and locks it inside the window boundaries.
//
//==========================================================================
void FWin32Mouse::Grab()
{
RECT rect;
if (Grabbed)
{
return;
}
GetCursorPos(&UngrabbedPointerPos);
ClipCursor(NULL); // helps with Win95?
GetClientRect(Window, &rect);
// Reposition the rect so that it only covers the client area.
ClientToScreen(Window, (LPPOINT)&rect.left);
ClientToScreen(Window, (LPPOINT)&rect.right);
ClipCursor(&rect);
SetCursorState(false);
CenterMouse(-1, -1);
Grabbed = true;
}
//==========================================================================
//
// FWin32Mouse :: Ungrab
//
// Shows the mouse and lets it roam free.
//
//==========================================================================
void FWin32Mouse::Ungrab()
{
if (!Grabbed)
{
return;
}
ClipCursor(NULL);
SetCursorPos(UngrabbedPointerPos.x, UngrabbedPointerPos.y);
SetCursorState(true);
Grabbed = false;
ClearButtonState();
}
//==========================================================================
//
// FWin32Mouse :: CenterMouse
//
// Moves the mouse to the center of the window, but only if the current
// position isn't already in the center.
//
//==========================================================================
void FWin32Mouse::CenterMouse(int curx, int cury)
{
RECT rect;
GetWindowRect (Window, &rect);
int centx = (rect.left + rect.right) >> 1;
int centy = (rect.top + rect.bottom) >> 1;
// Reduce the number of WM_MOUSEMOVE messages that get sent
// by only calling SetCursorPos when we really need to.
if (centx != curx || centy != cury)
{
PrevX = centx;
PrevY = centy;
SetCursorPos (centx, centy);
}
}
//==========================================================================
//
// I_StartupMouse
//
// Called during game init and whenever in_mouse changes.
//
//==========================================================================
void I_StartupMouse ()
{
EMouseMode new_mousemode;
switch(in_mouse)
{
case 0:
default:
if (OSPlatform == os_WinNT4)
{
new_mousemode = MM_Win32;
}
else if (MyRegisterRawInputDevices != NULL)
{
new_mousemode = MM_RawInput;
}
else
{
new_mousemode = MM_DInput;
}
break;
case 1:
new_mousemode = MM_Win32;
break;
case 2:
new_mousemode = MM_DInput;
break;
case 3:
new_mousemode = MM_RawInput;
break;
}
if (new_mousemode != MouseMode)
{
if (Mouse != NULL)
{
delete Mouse;
}
do
{
Mouse = MouseFactory[new_mousemode - MM_Win32]();
if (Mouse != NULL)
{
if (Mouse->GetDevice())
{
break;
}
delete Mouse;
Mouse = NULL;
}
new_mousemode = (EMouseMode)(new_mousemode - 1);
}
while (new_mousemode != MM_None);
MouseMode = new_mousemode;
NativeMouse = true;
}
HaveFocus = (GetFocus() == Window);
}