Skip to content

Commit

Permalink
Fixed rogue mouse grab after switching window.
Browse files Browse the repository at this point in the history
    // If you alt+TAB out of the window while any mouse button is down, OIS will not release it until you click in the window again.
    // See #2468
    // To work around, we keep internal button states and pay attention not to get them polluted by OIS.

Codechanges:
* InputEngine: the `getMouseState()` now returns the game's polished state, not raw state from OIS
* AppContext: added missing registration of window event listener --> the `windowFocusChanged()` callback is back from the dead.
* SceneMouse, OverlayWrapper, CameraManager: mouse handling functions no longer receive OIS input events, but query polished state from InputEngine.
  • Loading branch information
ohlidalp committed Sep 27, 2024
1 parent 00df880 commit d10cb2a
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 102 deletions.
64 changes: 29 additions & 35 deletions source/main/AppContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,15 @@ bool AppContext::mouseMoved(const OIS::MouseEvent& arg) // overrides OIS::MouseL
{
App::GetGuiManager()->WakeUpGUI();
App::GetGuiManager()->GetImGui().InjectMouseMoved(arg);
App::GetInputEngine()->ProcessMouseMotionEvent(arg);

if (!ImGui::GetIO().WantCaptureMouse) // true if mouse is over any window
{
bool handled = false;
if (App::GetOverlayWrapper())
if (!App::GetOverlayWrapper() || !App::GetOverlayWrapper()->handleMouseMoved()) // update the old airplane / autopilot gui
{
handled = App::GetOverlayWrapper()->mouseMoved(arg); // update the old airplane / autopilot gui
}
if (!handled)
{
if (!App::GetCameraManager()->mouseMoved(arg))
if (!App::GetCameraManager()->handleMouseMoved())
{
App::GetGameContext()->GetSceneMouse().mouseMoved(arg);
App::GetGameContext()->GetSceneMouse().handleMouseMoved();
}
}
}
Expand All @@ -100,51 +96,40 @@ bool AppContext::mouseMoved(const OIS::MouseEvent& arg) // overrides OIS::MouseL
bool AppContext::mousePressed(const OIS::MouseEvent& arg, OIS::MouseButtonID _id) // overrides OIS::MouseListener
{
App::GetGuiManager()->WakeUpGUI();
App::GetGuiManager()->GetImGui().InjectMousePressed(arg, _id);
App::GetGuiManager()->GetImGui().SetMouseButtonState(_id, /*down:*/true);
App::GetInputEngine()->ProcessMouseButtonEvent(arg);

if (!ImGui::GetIO().WantCaptureMouse) // true if mouse is over any window
{
bool handled = false;
if (App::GetOverlayWrapper())
{
handled = App::GetOverlayWrapper()->mousePressed(arg, _id); // update the old airplane / autopilot gui
}

if (!handled && App::app_state->getEnum<AppState>() == AppState::SIMULATION)
if (!App::GetOverlayWrapper() || !App::GetOverlayWrapper()->handleMousePressed()) // update the old airplane / autopilot gui
{
App::GetGameContext()->GetSceneMouse().mousePressed(arg, _id);
App::GetCameraManager()->mousePressed(arg, _id);
if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
{
App::GetGameContext()->GetSceneMouse().handleMousePressed();
App::GetCameraManager()->handleMousePressed();
}
}
}
else
{
App::GetInputEngine()->ProcessMouseEvent(arg);
}

return true;
}

bool AppContext::mouseReleased(const OIS::MouseEvent& arg, OIS::MouseButtonID _id) // overrides OIS::MouseListener
{
App::GetGuiManager()->WakeUpGUI();
App::GetGuiManager()->GetImGui().InjectMouseReleased(arg, _id);
App::GetGuiManager()->GetImGui().SetMouseButtonState(_id, /*down:*/false);
App::GetInputEngine()->ProcessMouseButtonEvent(arg);

if (!ImGui::GetIO().WantCaptureMouse) // true if mouse is over any window
{
bool handled = false;
if (App::GetOverlayWrapper())
if (!App::GetOverlayWrapper() || !App::GetOverlayWrapper()->handleMouseReleased()) // update the old airplane / autopilot gui
{
handled = App::GetOverlayWrapper()->mouseReleased(arg, _id); // update the old airplane / autopilot gui
}
if (!handled && App::app_state->getEnum<AppState>() == AppState::SIMULATION)
{
App::GetGameContext()->GetSceneMouse().mouseReleased(arg, _id);
if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
{
App::GetGameContext()->GetSceneMouse().handleMouseReleased();
}
}
}
else
{
App::GetInputEngine()->ProcessMouseEvent(arg);
}

return true;
}
Expand Down Expand Up @@ -201,7 +186,15 @@ void AppContext::windowResized(Ogre::RenderWindow* rw)

void AppContext::windowFocusChange(Ogre::RenderWindow* rw)
{
App::GetInputEngine()->resetKeys();
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
fmt::format("Window focus change; isActive={}", rw->isActive()));

// If you alt+TAB out of the window while any mouse button is down, OIS will not release it until you click in the window again.
// See https://github.com/RigsOfRods/rigs-of-rods/issues/2468
// To work around, we reset all internal mouse button states here and pay attention not to get them polluted by OIS again.
App::GetGuiManager()->GetImGui().ResetAllMouseButtons();
// Same applies to keyboard keys - reset them manually otherwise OIS will hold them 'down' the entire time.
App::GetInputEngine()->resetKeysAndMouseButtons();
}

// --------------------------
Expand Down Expand Up @@ -376,6 +369,7 @@ bool AppContext::SetUpRendering()
"Rigs of Rods version " + Ogre::String (ROR_VERSION_STRING),
width, height, ropts["Full Screen"].currentValue == "Yes", &miscParams);
OgreBites::WindowEventUtilities::_addRenderWindow(m_render_window);
OgreBites::WindowEventUtilities::addWindowEventListener(m_render_window, this);

this->SetRenderWindowIcon(m_render_window);
m_render_window->setActive(true);
Expand Down
20 changes: 12 additions & 8 deletions source/main/gameplay/SceneMouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,13 @@ void SceneMouse::reset()
mouseGrabState = 0;
}

bool SceneMouse::mouseMoved(const OIS::MouseEvent& _arg)
bool SceneMouse::handleMouseMoved()
{
const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();


// experimental mouse hack
if (ms.buttonDown(OIS::MB_Left) && mouseGrabState == 0)
{
lastMouseY = ms.Y.abs;
Expand Down Expand Up @@ -176,15 +178,15 @@ bool SceneMouse::mouseMoved(const OIS::MouseEvent& _arg)
}
}
}
else if (ms.buttonDown(OIS::MB_Left) && mouseGrabState == 1)
else if (App::GetInputEngine()->getMouseState().buttonDown(OIS::MB_Left) && mouseGrabState == 1)
{
// force applying and so forth happens in update()
lastMouseY = ms.Y.abs;
lastMouseX = ms.X.abs;
// not fixed
return false;
}
else if (!ms.buttonDown(OIS::MB_Left) && mouseGrabState == 1)
else if (!App::GetInputEngine()->getMouseState().buttonDown(OIS::MB_Left) && mouseGrabState == 1)
{
releaseMousePick();
// not fixed
Expand Down Expand Up @@ -224,11 +226,13 @@ void SceneMouse::UpdateVisuals()
}
}

bool SceneMouse::mousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id)
bool SceneMouse::handleMousePressed()
{
if (App::sim_state->getEnum<SimState>() == SimState::PAUSED) { return true; } // Do nothing when paused

const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

if (ms.buttonDown(OIS::MB_Middle))
{
Expand Down Expand Up @@ -300,7 +304,7 @@ bool SceneMouse::mousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _i
return true;
}

bool SceneMouse::mouseReleased(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id)
bool SceneMouse::handleMouseReleased()
{
if (App::sim_state->getEnum<SimState>() == SimState::PAUSED) { return true; } // Do nothing when paused

Expand Down
6 changes: 3 additions & 3 deletions source/main/gameplay/SceneMouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class SceneMouse

SceneMouse();

bool mouseMoved(const OIS::MouseEvent& _arg);
bool mousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id);
bool mouseReleased(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id);
bool handleMouseMoved();
bool handleMousePressed();
bool handleMouseReleased();

void InitializeVisuals();
void UpdateSimulation();
Expand Down
58 changes: 35 additions & 23 deletions source/main/gfx/camera/CameraManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,22 +531,25 @@ void CameraManager::ResetAllBehaviors()
this->SwitchBehaviorOnVehicleChange(CAMERA_BEHAVIOR_INVALID, nullptr);
}

bool CameraManager::mouseMoved(const OIS::MouseEvent& _arg)
bool CameraManager::handleMouseMoved()
{

if (App::sim_state->getEnum<SimState>() == SimState::PAUSED)
{
return true; // Do nothing when paused
}

// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

switch(m_current_behavior)
{
case CAMERA_BEHAVIOR_CHARACTER: {
if (!App::GetGameContext()->GetPlayerCharacter())
return false;
if (!m_charactercam_is_3rdperson)
{
const OIS::MouseState ms = _arg.state;
Radian angle = App::GetGameContext()->GetPlayerCharacter()->getRotation();

m_cam_rot_y += Degree(ms.Y.rel * 0.13f);
Expand All @@ -562,14 +565,13 @@ bool CameraManager::mouseMoved(const OIS::MouseEvent& _arg)
return true;
}

return CameraManager::CameraBehaviorOrbitMouseMoved(_arg);
return CameraManager::CameraBehaviorOrbitMouseMoved();
}
case CAMERA_BEHAVIOR_STATIC: return CameraBehaviorStaticMouseMoved(_arg);
case CAMERA_BEHAVIOR_VEHICLE: return CameraBehaviorOrbitMouseMoved(_arg);
case CAMERA_BEHAVIOR_VEHICLE_SPLINE: return this->CameraBehaviorVehicleSplineMouseMoved(_arg);
case CAMERA_BEHAVIOR_VEHICLE_CINECAM: return CameraBehaviorOrbitMouseMoved(_arg);
case CAMERA_BEHAVIOR_STATIC: return CameraBehaviorStaticMouseMoved();
case CAMERA_BEHAVIOR_VEHICLE: return CameraBehaviorOrbitMouseMoved();
case CAMERA_BEHAVIOR_VEHICLE_SPLINE: return this->CameraBehaviorVehicleSplineMouseMoved();
case CAMERA_BEHAVIOR_VEHICLE_CINECAM: return CameraBehaviorOrbitMouseMoved();
case CAMERA_BEHAVIOR_FREE: {
const OIS::MouseState ms = _arg.state;

App::GetCameraManager()->GetCameraNode()->yaw(Degree(-ms.X.rel * 0.13f), Ogre::Node::TS_WORLD);
App::GetCameraManager()->GetCameraNode()->pitch(Degree(-ms.Y.rel * 0.13f));
Expand All @@ -586,11 +588,13 @@ bool CameraManager::mouseMoved(const OIS::MouseEvent& _arg)
}
}

bool CameraManager::mousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id)
bool CameraManager::handleMousePressed()
{
const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

if (ms.buttonDown(OIS::MB_Right) && _id == OIS::MB_Middle)
if (ms.buttonDown(OIS::MB_Right) && ms.buttonDown(OIS::MB_Middle))
{
ResetCurrentBehavior();
}
Expand All @@ -599,9 +603,9 @@ bool CameraManager::mousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID
{
case CAMERA_BEHAVIOR_CHARACTER: return false;
case CAMERA_BEHAVIOR_STATIC: return false;
case CAMERA_BEHAVIOR_VEHICLE: return this->CameraBehaviorVehicleMousePressed(_arg, _id);
case CAMERA_BEHAVIOR_VEHICLE_SPLINE: return this->CameraBehaviorVehicleMousePressed(_arg, _id);
case CAMERA_BEHAVIOR_VEHICLE_CINECAM: return this->CameraBehaviorVehicleMousePressed(_arg, _id);
case CAMERA_BEHAVIOR_VEHICLE: return this->CameraBehaviorVehicleMousePressed();
case CAMERA_BEHAVIOR_VEHICLE_SPLINE: return this->CameraBehaviorVehicleMousePressed();
case CAMERA_BEHAVIOR_VEHICLE_CINECAM: return this->CameraBehaviorVehicleMousePressed();
case CAMERA_BEHAVIOR_FREE: return false;
case CAMERA_BEHAVIOR_FIXED: return false;
case CAMERA_BEHAVIOR_ISOMETRIC: return false;
Expand Down Expand Up @@ -797,9 +801,11 @@ void CameraManager::UpdateCameraBehaviorStatic()
App::GetCameraManager()->GetCamera()->setFOVy(Radian(fov));
}

bool CameraManager::CameraBehaviorStaticMouseMoved(const OIS::MouseEvent& _arg)
bool CameraManager::CameraBehaviorStaticMouseMoved()
{
const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

if (ms.buttonDown(OIS::MB_Right))
{
Expand Down Expand Up @@ -938,9 +944,11 @@ void CameraManager::CameraBehaviorOrbitUpdate()
App::GetCameraManager()->GetCameraNode()->lookAt(m_cam_look_at_smooth, Ogre::Node::TS_WORLD);
}

bool CameraManager::CameraBehaviorOrbitMouseMoved(const OIS::MouseEvent& _arg)
bool CameraManager::CameraBehaviorOrbitMouseMoved()
{
const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

if (ms.buttonDown(OIS::MB_Right))
{
Expand Down Expand Up @@ -1085,9 +1093,11 @@ void CameraManager::CameraBehaviorVehicleReset()
m_cam_dist = m_cam_dist_min * 1.5f + 2.0f;
}

bool CameraManager::CameraBehaviorVehicleMousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id)
bool CameraManager::CameraBehaviorVehicleMousePressed()
{
const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

if ( ms.buttonDown(OIS::MB_Middle) && RoR::App::GetInputEngine()->isKeyDown(OIS::KC_LSHIFT) )
{
Expand Down Expand Up @@ -1181,9 +1191,11 @@ void CameraManager::CameraBehaviorVehicleSplineUpdate()
CameraManager::CameraBehaviorOrbitUpdate();
}

bool CameraManager::CameraBehaviorVehicleSplineMouseMoved( const OIS::MouseEvent& _arg)
bool CameraManager::CameraBehaviorVehicleSplineMouseMoved( )
{
const OIS::MouseState ms = _arg.state;
// IMPORTANT: get mouse button state from InputEngine, not from OIS directly
// - that state may be dirty, see commentary in `InputEngine::getMouseState()`
const OIS::MouseState ms = App::GetInputEngine()->getMouseState();

m_cam_ratio = 1.0f / (m_cct_dt * 4.0f);

Expand Down Expand Up @@ -1233,7 +1245,7 @@ bool CameraManager::CameraBehaviorVehicleSplineMouseMoved( const OIS::MouseEven
}
else
{
return CameraManager::CameraBehaviorOrbitMouseMoved(_arg);
return CameraManager::CameraBehaviorOrbitMouseMoved();
}
}

Expand Down
12 changes: 6 additions & 6 deletions source/main/gfx/camera/CameraManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ class CameraManager
void NotifyVehicleChanged(ActorPtr new_vehicle);

void CameraBehaviorOrbitReset();
bool CameraBehaviorOrbitMouseMoved(const OIS::MouseEvent& _arg);
bool CameraBehaviorOrbitMouseMoved();
void CameraBehaviorOrbitUpdate();

bool mouseMoved(const OIS::MouseEvent& _arg);
bool mousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id);
bool handleMouseMoved();
bool handleMousePressed();

void ResetAllBehaviors();
void ReCreateCameraNode(); //!< Needed since we call `Ogre::SceneManager::ClearScene()` after end of sim. session
Expand All @@ -89,14 +89,14 @@ class CameraManager
void ResetCurrentBehavior();
void DeactivateCurrentBehavior();
void UpdateCameraBehaviorStatic();
bool CameraBehaviorStaticMouseMoved(const OIS::MouseEvent& _arg);
bool CameraBehaviorStaticMouseMoved();
void UpdateCameraBehaviorFree();
void UpdateCameraBehaviorFixed();
void UpdateCameraBehaviorVehicle();
void CameraBehaviorVehicleReset();
bool CameraBehaviorVehicleMousePressed(const OIS::MouseEvent& _arg, OIS::MouseButtonID _id);
bool CameraBehaviorVehicleMousePressed();
void CameraBehaviorVehicleSplineUpdate();
bool CameraBehaviorVehicleSplineMouseMoved(const OIS::MouseEvent& _arg);
bool CameraBehaviorVehicleSplineMouseMoved();
void CameraBehaviorVehicleSplineReset();
void CameraBehaviorVehicleSplineCreateSpline();
void CameraBehaviorVehicleSplineUpdateSpline();
Expand Down
Loading

0 comments on commit d10cb2a

Please sign in to comment.