From 0c4c90ac8902abc9a71b4363a5a0475ca4c9737f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 24 May 2009 03:15:04 +0000 Subject: [PATCH] - SetCursorState() now calls ShowCursor() again, because capturing the mouse with RIDEV_NOLEGACY apparently prevents SetCursor() from doing anything. - Split mouse code off from i_input.cpp into i_mouse.cpp and added raw mouse handling. (WM_INPUT obsoleted most of DirectInput for XP.) - Fixed: Similar to the Win32 mouse, using the DirectInput mouse in windowed mode, if you alt-tabbed away and then clicked on the window's title bar, mouse input would be frozen until the mouse was ungrabbed again. SVN r1601 (trunk) --- docs/rh-log.txt | 12 +- src/CMakeLists.txt | 1 + src/d_event.h | 3 + src/d_main.h | 2 - src/sdl/i_input.cpp | 3 +- src/win32/i_input.cpp | 833 ++--------------------------- src/win32/i_input.h | 63 +++ src/win32/i_mouse.cpp | 1171 +++++++++++++++++++++++++++++++++++++++++ src/win32/rawinput.h | 18 + zdoom.vcproj | 8 + 10 files changed, 1316 insertions(+), 798 deletions(-) create mode 100644 src/win32/i_mouse.cpp create mode 100644 src/win32/rawinput.h diff --git a/docs/rh-log.txt b/docs/rh-log.txt index e9953b1dc..0b6ac745d 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,13 @@ -May 23, 2009 (Changes by Graf Zahl) +May 23, 2009 +- SetCursorState() now calls ShowCursor() again, because capturing the mouse + with RIDEV_NOLEGACY apparently prevents SetCursor() from doing anything. +- Split mouse code off from i_input.cpp into i_mouse.cpp and added raw mouse + handling. (WM_INPUT obsoleted most of DirectInput for XP.) +- Fixed: Similar to the Win32 mouse, using the DirectInput mouse in windowed + mode, if you alt-tabbed away and then clicked on the window's title bar, + mouse input would be frozen until the mouse was ungrabbed again. + +May 23, 2009 (Changes by Graf Zahl) - Fixed: When setting up a deep water sector with Transfer_Heights the floorclip information of all actors in the sector needs to be updated. @@ -6,6 +15,7 @@ May 22, 2009 (Changes by Graf Zahl) - Fixed: A_CountdownArg and A_Die must ensure a certain kill. May 22, 2009 +- Moved SetSoundPaused() out of i_input.cpp and into s_sound.cpp. - Fixed: When using Win32 mouse, windowed mode, alt-tabbing away and then clicking on the window's title bar moved it practically off the screen. - Beginnings of i_input.cpp rewrite: Win32 and DirectInput mouse handling has diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 57c93c71f..2b7398f25 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -398,6 +398,7 @@ if( WIN32 ) win32/i_cd.cpp win32/i_crash.cpp win32/i_input.cpp + win32/i_mouse.cpp win32/i_main.cpp win32/i_movie.cpp win32/i_system.cpp diff --git a/src/d_event.h b/src/d_event.h index 1ace621ed..88a117429 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -112,6 +112,9 @@ typedef enum BT_USER4 = 1<<24, } buttoncode_t; +// Called by IO functions when input is detected. +void D_PostEvent (const event_t* ev); + // // GLOBAL VARIABLES diff --git a/src/d_main.h b/src/d_main.h index 56cc1ef1a..3ef536ab4 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -38,8 +38,6 @@ struct event_t; // void D_DoomMain (void); -// Called by IO functions when input is detected. -void D_PostEvent (const event_t* ev); void D_Display (); diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index ab27dfde6..b42c0385e 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -21,8 +21,7 @@ static void I_CheckGUICapture (); static void I_CheckNativeMouse (); -static bool GUICapture; -static bool NativeMouse = true; +bool GUICapture; extern int paused; diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index 9d1cbf703..1cf31ff89 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -103,34 +103,14 @@ #include "d_event.h" #include "v_text.h" -#define DINPUT_BUFFERSIZE 32 +// Prototypes and declarations. +#include "rawinput.h" +// Definitions +#define RIF(name, ret, args) \ + name##Proto My##name; +#include "rawinput.h" -class FInputDevice -{ -public: - virtual bool GetDevice() = 0; - virtual void ProcessInput() = 0; - virtual bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); -}; - -class FMouse : public FInputDevice -{ -public: - FMouse(); - - virtual void Grab() = 0; - virtual void Ungrab() = 0; - -protected: - void PostMouseMove(int x, int y); - void WheelMoved(int wheelmove); - void PostButtonEvent(int button, bool down); - void ClearButtonState(); - - int LastX, LastY; // for m_filter - WORD ButtonState; // bit mask of current button states (1=down, 0=up) -}; #ifdef _DEBUG #define INGAME_PRIORITY_CLASS NORMAL_PRIORITY_CLASS @@ -139,6 +119,7 @@ protected: #define INGAME_PRIORITY_CLASS NORMAL_PRIORITY_CLASS #endif +static void FindRawInputFunctions(); BOOL DI_InitJoy (void); extern HINSTANCE g_hInst; @@ -151,15 +132,10 @@ static HMODULE DInputDLL; static void KeyRead (); static BOOL I_StartupKeyboard (); -static void I_StartupMouse (); -static void CenterMouse_Win32 (LONG curx, LONG cury); -static void DI_Acquire (LPDIRECTINPUTDEVICE8 mouse); -static void DI_Unacquire (LPDIRECTINPUTDEVICE8 mouse); -static void SetCursorState (bool visible); static HRESULT InitJoystick (); -static bool GUICapture; -static bool NativeMouse; +bool GUICapture; +extern FMouse *Mouse; bool VidResizing; @@ -175,15 +151,12 @@ EXTERN_CVAR (String, language) EXTERN_CVAR (Bool, lookstrafe) -typedef enum { none, win32, dinput } mousemode_t; -static mousemode_t mousemode = none; - extern BOOL paused; -static bool HaveFocus = false; +bool HaveFocus; static bool noidle = false; -static LPDIRECTINPUT8 g_pdi; -static LPDIRECTINPUT g_pdi3; +LPDIRECTINPUT8 g_pdi; +LPDIRECTINPUT g_pdi3; static LPDIRECTINPUTDEVICE8 g_pJoy; @@ -194,10 +167,6 @@ static LPDIRECTINPUTDEVICE8 g_pJoy; static LPDIRECTINPUTDEVICE8 g_pKey; -FMouse *Mouse; - -HCURSOR TheArrowCursor, TheInvisibleCursor; - TArray JoystickNames; static DIDEVCAPS JoystickCaps; @@ -225,10 +194,6 @@ static const BYTE POVButtons[9] = { 0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x BOOL AppActive = TRUE; int SessionState = 0; -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) - CVAR (Bool, use_joystick, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CUSTOM_CVAR (GUID, joy_guid, NULL, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) @@ -347,26 +312,6 @@ static FBaseCVar * const JoyConfigVars[] = &joy_upspeed }; -CUSTOM_CVAR (Int, in_mouse, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) -{ - if (self < 0) - { - self = 0; - } - else if (self > 2) - { - self = 2; - } - else if (g_pdi == NULL && g_pdi3 == NULL) - { - return; - } - else - { - I_StartupMouse(); - } -} - static BYTE KeyState[256]; static BYTE DIKState[2][NUM_KEYS]; static int KeysReadCount; @@ -439,53 +384,11 @@ static void I_CheckGUICapture () } } -CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) -{ - if (self < 0) self = 0; - else if (self > 2) self = 2; -} - -static bool inGame() -{ - switch (mouse_capturemode) - { - default: - case 0: - return gamestate == GS_LEVEL; - case 1: - return gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_FINALE; - case 2: - return true; - } -} - -void I_CheckNativeMouse(bool preferNative) -{ - bool wantNative = !HaveFocus || - ((!screen || !screen->IsFullscreen()) && - (!inGame() || GUICapture || paused || preferNative || !use_mouse || demoplayback)); - - //Printf ("%d %d %d\n", wantNative, preferNative, NativeMouse); - - if (wantNative != NativeMouse) - { - if (wantNative) - { - Mouse->Ungrab(); - } - else - { - Mouse->Grab(); - } - NativeMouse = wantNative; - } -} - LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (Mouse != NULL) { - LRESULT result; + LRESULT result = 0; if (Mouse->WndProcHook(hWnd, message, wParam, lParam, &result)) { @@ -507,6 +410,8 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_HOTKEY: break; + case WM_INPUT: + case WM_PAINT: if (screen != NULL && 0) { @@ -1278,14 +1183,13 @@ bool I_InitInput (void *hwnd) Printf ("I_InitInput\n"); atterm (I_ShutdownInput); - NativeMouse = true; - noidle = !!Args->CheckParm ("-noidle"); g_pdi = NULL; g_pdi3 = NULL; - // Try for DirectInput 8 first, then DirectInput 3 for NT 4's benefit. + FindRawInputFunctions(); + // Try for DirectInput 8 first, then DirectInput 3 for NT 4's benefit. DInputDLL = LoadLibrary("dinput8.dll"); if (DInputDLL != NULL) { @@ -1383,16 +1287,6 @@ void I_ShutdownInput () } } -static void SetCursorState(bool visible) -{ - HCURSOR usingCursor = visible ? TheArrowCursor : TheInvisibleCursor; - SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)usingCursor); - if (HaveFocus) - { - SetCursor(usingCursor); - } -} - // Initialize the keyboard static BOOL I_StartupKeyboard (void) { @@ -1619,6 +1513,16 @@ CCMD (playmovie) I_PlayMovie (argv[1]); } +//========================================================================== +// +// FInputDevice - Destructor +// +//========================================================================== + +FInputDevice::~FInputDevice() +{ +} + //========================================================================== // // FInputDevice :: WndProcHook @@ -1634,678 +1538,21 @@ bool FInputDevice::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP //========================================================================== // -// FMouse - Constructor +// FindRawInputFunctions +// +// Finds functions for raw input, if available. // //========================================================================== -FMouse::FMouse() +static void FindRawInputFunctions() { - 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; - } - 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; - } -} - -class FDInputMouse : public FMouse -{ -public: - FDInputMouse(); - ~FDInputMouse(); - - bool GetDevice(); - void ProcessInput(); - void Grab(); - void Ungrab(); - -protected: - LPDIRECTINPUTDEVICE8 Device; -}; - -//========================================================================== -// -// FDInputMouse - Constructor -// -//========================================================================== - -FDInputMouse::FDInputMouse() -{ - Device = NULL; -} - -//========================================================================== -// -// 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) - { // DirectInput3 interface - hr = g_pdi3->CreateDevice(GUID_SysMouse, (LPDIRECTINPUTDEVICE*)&Device, NULL); - } - else - { // DirectInput8 interface - hr = g_pdi->CreateDevice(GUID_SysMouse, &Device, NULL); - } - 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 :: 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 (!HaveFocus || NativeMouse) - 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() -{ - Device->Acquire(); - SetCursorState(NativeMouse); -} - -//========================================================================== -// -// FDInputMouse :: Ungrab -// -//========================================================================== - -void FDInputMouse::Ungrab() -{ - Device->Unacquire(); - SetCursorState(true); - ClearButtonState(); -} - -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; -}; - -//========================================================================== -// -// 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 (!HaveFocus || !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); - *result = 0; - return true; - } - } - else if (message == WM_MOVE) - { - CenterMouse(-1, -1); - *result = 0; - 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) - { - *result = 0; - 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); - } -} - -static void I_StartupMouse () -{ - mousemode_t new_mousemode; - - if (in_mouse == 1 || (in_mouse == 0 && OSPlatform == os_WinNT4)) - new_mousemode = win32; - else - new_mousemode = dinput; - - if (new_mousemode != mousemode) - { - if (new_mousemode == dinput) - { - Mouse = new FDInputMouse(); - } - else - { - Mouse = new FWin32Mouse(); - } - if (!Mouse->GetDevice()) - { - delete Mouse; - if (new_mousemode == dinput) - { - new_mousemode = win32; - Mouse = new FWin32Mouse(); - if (!Mouse->GetDevice()) - { - Mouse = NULL; - new_mousemode = none; - } - } - } - mousemode = new_mousemode; - } - HaveFocus = GetFocus() == Window; + HMODULE user32 = GetModuleHandle("user32.dll"); + + if (user32 == NULL) + { + return; // WTF kind of broken system are we running on? + } +#define RIF(name,ret,args) \ + My##name = (name##Proto)GetProcAddress(user32, #name); +#include "rawinput.h" } diff --git a/src/win32/i_input.h b/src/win32/i_input.h index 84d565d85..5c797551b 100644 --- a/src/win32/i_input.h +++ b/src/win32/i_input.h @@ -54,4 +54,67 @@ extern char *JoyAxisNames[8]; extern void DI_EnumJoy (); +#ifdef USE_WINDOWS_DWORD +// Don't make these definitions available to the main body of the source code. + +class FInputDevice +{ +public: + virtual ~FInputDevice() = 0; + virtual bool GetDevice() = 0; + virtual void ProcessInput() = 0; + virtual bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); +}; + +class FMouse : public FInputDevice +{ +public: + FMouse(); + + virtual void Grab() = 0; + virtual void Ungrab() = 0; + +protected: + void PostMouseMove(int x, int y); + void WheelMoved(int wheelmove); + void PostButtonEvent(int button, bool down); + void ClearButtonState(); + + int LastX, LastY; // for m_filter + WORD ButtonState; // bit mask of current button states (1=down, 0=up) +}; + +void I_StartupMouse(); +void I_CheckNativeMouse(bool prefer_native); + +// USB HID usage page numbers +#define HID_GENERIC_DESKTOP_PAGE 0x01 +#define HID_SIMULATION_CONTROLS_PAGE 0x02 +#define HID_VR_CONTROLS_PAGE 0x03 +#define HID_SPORT_CONTROLS_PAGE 0x04 +#define HID_GAME_CONTROLS_PAGE 0x05 +#define HID_GENERIC_DEVICE_CONTROLS_PAGE 0x06 +#define HID_KEYBOARD_PAGE 0x07 +#define HID_LED_PAGE 0x08 +#define HID_BUTTON_PAGE 0x09 +#define HID_ORDINAL_PAGE 0x0a +#define HID_TELEPHONY_DEVICE_PAGE 0x0b +#define HID_CONSUMER_PAGE 0x0c +#define HID_DIGITIZERS_PAGE 0x0d +#define HID_UNICODE_PAGE 0x10 +#define HID_ALPHANUMERIC_DISPLAY_PAGE 0x14 +#define HID_MEDICAL_INSTRUMENT_PAGE 0x40 + +// HID Generic Desktop Page usages +#define HID_GDP_UNDEFINED 0x00 +#define HID_GDP_POINTER 0x01 +#define HID_GDP_MOUSE 0x02 +#define HID_GDP_JOYSTICK 0x04 +#define HID_GDP_GAMEPAD 0x05 +#define HID_GDP_KEYBOARD 0x06 +#define HID_GDP_KEYPAD 0x07 +#define HID_GDP_MULTIAXIS_CONTROLLER 0x08 +#endif + + #endif diff --git a/src/win32/i_mouse.cpp b/src/win32/i_mouse.cpp new file mode 100644 index 000000000..100a1836c --- /dev/null +++ b/src/win32/i_mouse.cpp @@ -0,0 +1,1171 @@ +#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 +#include +#include + +#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); + if (visible) + { + ShowCursor(TRUE); + } + else + { + while (ShowCursor(FALSE) >= 0) + { } + } + } +} + +//========================================================================== +// +// 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 wantNative = (GetFocus() != Window) || + ((!screen || !screen->IsFullscreen()) && + (!CaptureMode_InGame() || GUICapture || paused || preferNative || !use_mouse || demoplayback)); + + //Printf ("%d %d %d\n", wantNative, preferNative, NativeMouse); + + if (wantNative != NativeMouse) + { + if (Mouse != NULL) + { + NativeMouse = wantNative; + if (wantNative) + { + 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 +// +//========================================================================== + +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; + SetCursorState(false); + // 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(); + } + SetCursorState(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(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); +} + diff --git a/src/win32/rawinput.h b/src/win32/rawinput.h new file mode 100644 index 000000000..c3db4ff8d --- /dev/null +++ b/src/win32/rawinput.h @@ -0,0 +1,18 @@ +// Pointers to raw-input related functions. They were introduced with XP, +// so we can't use static linking with them. +#ifndef RIF +#define RIF(name,ret,args) \ + typedef ret (WINAPI *name##Proto)args; \ + extern name##Proto My##name; +#endif + +RIF(DefRawInputProc, LRESULT, (PRAWINPUT *paRawInput, INT nInput, UINT cbSizeHeader)) +RIF(GetRawInputBuffer, UINT, (PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader)) +RIF(GetRawInputData, UINT, (HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader)) +RIF(GetRawInputDeviceInfoA, UINT, (HANDLE hDevice, UINT uiCommand, LPVOID pData, PUINT pcbSize)) +RIF(GetRawInputDeviceInfoW, UINT, (HANDLE hDevice, UINT uiCommand, LPVOID pData, PUINT pcbSize)) +RIF(GetRawInputDeviceList, UINT, (PRAWINPUTDEVICELIST pRawInputDeviceList, PUINT puiNumDevices, UINT cbSize)) +RIF(GetRegisteredRawInputDevices, UINT, (PRAWINPUTDEVICE pRawInputDevices, PUINT puiNumDevices, UINT cbSize)) +RIF(RegisterRawInputDevices, BOOL, (PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize)) + +#undef RIF diff --git a/zdoom.vcproj b/zdoom.vcproj index c65be22f4..da9a1fbef 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1980,6 +1980,10 @@ RelativePath=".\src\win32\i_main.cpp" > + + @@ -2000,6 +2004,10 @@ RelativePath=".\src\win32\icon2.ico" > + +