Skip to content

Commit

Permalink
fix #5956
Browse files Browse the repository at this point in the history
  • Loading branch information
rt committed Aug 31, 2018
1 parent a7f92cf commit d83f7a0
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 31 deletions.
25 changes: 19 additions & 6 deletions rts/Game/UI/MouseHandler.cpp
Expand Up @@ -76,7 +76,7 @@ static CInputReceiver*& activeReceiver = CInputReceiver::GetActiveReceiverRef();

CMouseHandler::CMouseHandler()
{
const int2 mousepos = IMouseInput::GetInstance()->GetPos();
const int2 mousepos = mouseInput->GetPos();

lastx = mousepos.x;
lasty = mousepos.y;
Expand Down Expand Up @@ -111,11 +111,15 @@ CMouseHandler::~CMouseHandler()
void CMouseHandler::InitStatic()
{
assert(mouse == nullptr);
assert(mouseInput == nullptr);

mouseInput = IMouseInput::GetInstance();
mouse = new CMouseHandler();
}

void CMouseHandler::KillStatic()
{
IMouseInput::FreeInstance(mouseInput);
spring::SafeDelete(mouse);
}

Expand Down Expand Up @@ -615,7 +619,7 @@ void CMouseHandler::WarpMouse(int x, int y)
lastx = x + globalRendering->viewPosX;
lasty = y + globalRendering->viewPosY;

mouseInput->SetPos(int2(lastx, lasty));
mouseInput->SetWarpPos({lastx, lasty});
}


Expand All @@ -627,25 +631,34 @@ void CMouseHandler::ShowMouse()
hide = false;
cursorText = "none"; // force hardware cursor rebinding (else we have standard b&w cursor)

// don't use SDL_ShowCursor here, it would cause a flicker with hwCursor
SDL_SetRelativeMouseMode(SDL_FALSE);

// don't use SDL_ShowCursor here since it would cause flickering with hwCursor
// (by switching between default cursor and later the real one, e.g. `attack`)
// instead update state and cursor at the same time
if (hardwareCursor) {
hwHide = true;
} else {
SDL_ShowCursor(SDL_DISABLE);
}
}

mouseInput->SetWarpPos(globalRendering->GetScreenCenter());
}

void CMouseHandler::HideMouse()
{
if (hide)
return;

hide = true;
hwHide = true;

SDL_ShowCursor(SDL_DISABLE);
mouseInput->SetWMMouseCursor(nullptr);
// signal that we are only interested in relative motion events when MMB-scrolling
// this way the mouse position will never change so it is also unnecessary to call
// SDL_WarpMouseInWindow with associated warts (i.e. filtering of motion events by
// hand...); technically supercedes SDL_ShowCursor as well
SDL_SetRelativeMouseMode(SDL_TRUE);

const int2 screenCenter = globalRendering->GetScreenCenter();

Expand All @@ -654,8 +667,8 @@ void CMouseHandler::HideMouse()
lastx = screenCenter.x;
lasty = screenCenter.y;

mouseInput->SetWMMouseCursor(nullptr);
mouseInput->SetPos(screenCenter);
hide = true;
}


Expand Down
84 changes: 63 additions & 21 deletions rts/System/Input/MouseInput.cpp
Expand Up @@ -22,15 +22,15 @@
#include "MouseInput.h"
#include "InputHandler.h"

#include "Game/GlobalUnsynced.h"
#include "Game/UI/MouseHandler.h"
#include "Rendering/GlobalRendering.h"
#include "Rendering/GL/FBO.h"
#include "System/MainDefines.h"
#include "System/SafeUtil.h"

#include <functional>

#include <SDL_events.h>
#include <SDL_hints.h>
#include <SDL_syswm.h>


Expand Down Expand Up @@ -94,12 +94,12 @@ bool IMouseInput::HandleSDLMouseEvent(const SDL_Event& event)

//////////////////////////////////////////////////////////////////////

#if defined(WIN32) && !defined (HEADLESS)
#if defined(WIN32) && !defined(HEADLESS)

class CWin32MouseInput : public IMouseInput
{
public:
static CWin32MouseInput *inst;
static CWin32MouseInput* inst;

LONG_PTR sdl_wndproc;
HWND wnd;
Expand All @@ -108,11 +108,11 @@ class CWin32MouseInput : public IMouseInput
static LRESULT CALLBACK SpringWndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_SETCURSOR:
{
if (inst->hCursor!=NULL) {
Uint16 hittest = LOWORD(lParam);
if ( hittest == HTCLIENT ) {
case WM_SETCURSOR: {
if (inst->hCursor != nullptr) {
const Uint16 hittest = LOWORD(lParam);

if (hittest == HTCLIENT) {
SetCursor(inst->hCursor);
return TRUE;
}
Expand All @@ -137,9 +137,10 @@ class CWin32MouseInput : public IMouseInput
wnd = info.info.win.window;

LONG_PTR cur_wndproc = GetWindowLongPtr(wnd, GWLP_WNDPROC);

if (cur_wndproc != (LONG_PTR)SpringWndProc) {
sdl_wndproc = GetWindowLongPtr(wnd, GWLP_WNDPROC);
SetWindowLongPtr(wnd,GWLP_WNDPROC,(LONG_PTR)SpringWndProc);
SetWindowLongPtr(wnd,GWLP_WNDPROC, (LONG_PTR)SpringWndProc);
}
}

Expand All @@ -149,42 +150,79 @@ class CWin32MouseInput : public IMouseInput
hCursor = nullptr;
sdl_wndproc = 0;
wnd = 0;

InstallWndCallback();
// Windows 10 FCU (Fall Creators Update) causes spurious SDL_MOUSEMOTION
// events to be generated with SDL_HINT_MOUSE_RELATIVE_MODE_WARP enabled
//
// while Spring did not previously set this hint and SDL defaults to raw
// input, the update also affects MMB scrolling via SDL_WarpMouseInWindow
// (our ancient manually implemented method of achieving relative motion)
//
// win32 SDL hides these events in 2.0.8 only if mouse->relative_mode_warp
// (which configures relative mouse mode to *internally* use mouse warping
// instead of raw input and gets toggled by SDL_SetRelativeMouseMode based
// on the hint given here); the alternative to RMW would be to *duplicate*
// the SDL patch in SetPosSDL
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
}
~CWin32MouseInput()
{
// reinstall the SDL window proc
SetWindowLongPtr(wnd, GWLP_WNDPROC, sdl_wndproc);
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "0");
}
};

CWin32MouseInput* CWin32MouseInput::inst = nullptr;


static uint8_t mouseInputMem[sizeof(CWin32MouseInput)];
#else
static uint8_t mouseInputMem[sizeof(IMouseInput)];
#endif



#if 1
static SDL_Event events[100];
#endif

void IMouseInput::SetPos(int2 pos)
bool IMouseInput::SetPos(int2 pos)
{
if (!globalRendering->active)
return;
return false;

// calling SDL_WarpMouse at 300fps eats ~5% cpu usage, so only update when needed
if (pos.x == mousepos.x && pos.y == mousepos.y)
return;
return false;

mousepos = pos;
return (mousepos = pos, true);
}

bool IMouseInput::WarpPos(int2 pos)
{
SDL_WarpMouseInWindow(globalRendering->GetWindow(0), pos.x, pos.y);

// SDL_WarpMouse generates SDL_MOUSEMOTION events
// in `middle click scrolling` those SDL generated ones would point into
// the opposite direction the user moved the mouse, and so events would
// cancel each other -> camera wouldn't move at all
// so we need to catch those SDL generated events and delete them

// delete all SDL_MOUSEMOTION in the queue
// cancel each other -> camera wouldn't move at all or jitter
// need to catch the SDL generated events and delete them from its queue
//
// NOTE [2018]:
// the above comment dates back to 2010, but also describes the recent
// Windows 10 FCU bug with relative mode warping which similarly relies
// on WMIW
#if 1
SDL_PumpEvents();
SDL_PeepEvents(&events[0], sizeof(events) / sizeof(events[0]), SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION);
#else
// should be equivalent, but for some reason is not
SDL_FlushEvent(SDL_MOUSEMOTION);
#endif

return true;
}


Expand All @@ -193,14 +231,18 @@ IMouseInput* IMouseInput::GetInstance()
{
if (mouseInput == nullptr) {
#if defined(WIN32) && !defined(HEADLESS)
mouseInput = new CWin32MouseInput;
mouseInput = new (mouseInputMem) CWin32MouseInput();
#else
mouseInput = new IMouseInput;
mouseInput = new (mouseInputMem) IMouseInput();
#endif
}

return mouseInput;
}

void IMouseInput::FreeInstance(IMouseInput* mouseInp) {
spring::SafeDelete(mouseInp);
assert(mouseInp == &mouseInputMem[0]);
spring::SafeDestruct(mouseInp);
memset(mouseInputMem, 0, sizeof(mouseInputMem));
}

7 changes: 5 additions & 2 deletions rts/System/Input/MouseInput.h
Expand Up @@ -20,8 +20,11 @@ class IMouseInput

virtual void InstallWndCallback() {}

virtual int2 GetPos() { return mousepos; }
void SetPos(int2 pos);
int2 GetPos() const { return mousepos; }

bool SetPos(int2 pos);
bool WarpPos(int2 pos);
bool SetWarpPos(int2 pos) { return (SetPos(pos) && WarpPos(pos)); }

bool HandleSDLMouseEvent(const SDL_Event& event);

Expand Down
2 changes: 0 additions & 2 deletions rts/System/SpringApp.cpp
Expand Up @@ -259,7 +259,6 @@ bool SpringApp::Init()
CInfoConsole::InitStatic();
CMouseHandler::InitStatic();

mouseInput = IMouseInput::GetInstance();
input.AddHandler(std::bind(&SpringApp::MainEventHandler, this, std::placeholders::_1));

// Global structures
Expand Down Expand Up @@ -908,7 +907,6 @@ void SpringApp::Kill(bool fromRun)

CInfoConsole::KillStatic();
CMouseHandler::KillStatic();
IMouseInput::FreeInstance(mouseInput);

LOG("[SpringApp::%s][6]", __func__);
gs->Kill();
Expand Down

0 comments on commit d83f7a0

Please sign in to comment.