From 02ccb758949b64abe026766e141a562d53313feb Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Wed, 9 Jan 2013 06:10:05 -0400 Subject: [PATCH] Wrap the mouse to the window (except in debug mode) Grab the mouse when not in the main menu (except in debug mode) Always hide the cursor when it's over the window Allow warping the mouse around Handle ^C properly --- apps/openmw/mwinput/inputmanagerimp.cpp | 75 +++++------ apps/openmw/mwinput/inputmanagerimp.hpp | 5 + apps/openmw/mwinput/sdlinputwrapper.cpp | 170 ++++++++++++++++++------ apps/openmw/mwinput/sdlinputwrapper.hpp | 22 ++- extern/oics/OISCompat.h | 12 ++ 5 files changed, 194 insertions(+), 90 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f13eb9daf..805f4926c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -44,6 +44,7 @@ namespace MWInput , mUserFile(userFile) , mDragDrop(false) , mGuiCursorEnabled(false) + , mDebug(debug) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) @@ -52,50 +53,6 @@ namespace MWInput , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) { - Ogre::RenderWindow* window = ogre.getWindow (); - size_t windowHnd; - - resetIdleTime(); - - window->getCustomAttribute("WINDOW", &windowHnd); - - - // Set non-exclusive mouse and keyboard input if the user requested - // it. - - //TODO: re-enable this and make it work with SDL - /* - - std::ostringstream windowHndStr; - OIS::ParamList pl; - - windowHndStr << windowHnd; - pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - - if (debug) - { - #if defined OIS_WIN32_PLATFORM - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_FOREGROUND" ))); - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_NONEXCLUSIVE"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_FOREGROUND"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_NONEXCLUSIVE"))); - #elif defined OIS_LINUX_PLATFORM - pl.insert(std::make_pair(std::string("x11_mouse_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_mouse_hide"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_keyboard_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("XAutoRepeatOn"), - std::string("true"))); - #endif - } - */ - #if defined(__APPLE__) && !defined(__LP64__) // Give the application window focus to receive input events ProcessSerialNumber psn = { 0, kCurrentProcess }; @@ -103,9 +60,12 @@ namespace MWInput SetFrontProcess(&psn); #endif + Ogre::RenderWindow* window = ogre.getWindow (); + mInputManager = new MWSDLInputWrapper(window); mInputManager->setMouseEventCallback (this); mInputManager->setKeyboardEventCallback (this); + mInputManager->setWindowEventCallback(this); std::string file = userFileExists ? userFile : ""; mInputCtrl = new ICS::InputControlSystem(file, true, this, NULL, A_Last); @@ -256,6 +216,15 @@ namespace MWInput // event callbacks (which may crash) mWindows.update(); + if(!mDebug) + { + //don't keep the pointer away from the window edge in GUI mode + mInputManager->setWrapPointer(!mWindows.isGuiMode()); + + //we let the mouse escape in the main menu + mInputManager->setGrabPointer(!mWindows.containsMode(MWGui::GM_MainMenu)); + } + // Disable movement in Gui mode if (mWindows.isGuiMode()) return; @@ -510,6 +479,24 @@ namespace MWInput return true; } + bool InputManager::windowFocusChange(bool have_focus) + { + if(!mDebug) + { + + } + return true; + } + + bool InputManager::windowVisibilityChange(bool visible) + { + if(!mDebug) + { + //TODO: Pause game? + } + return true; + } + void InputManager::toggleMainMenu() { if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index df6ce3c43..9a7656a67 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -55,6 +55,7 @@ namespace MWInput public MWBase::InputManager, public ICS::MWSDLKeyListener, public ICS::MWSDLMouseListener, + public ICS::MWSDLWindowListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { @@ -94,6 +95,9 @@ namespace MWInput virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual bool mouseMoved( const ICS::MWSDLMouseMotionEvent &arg ); + virtual bool windowVisibilityChange( bool visible ); + virtual bool windowFocusChange( bool have_focus ); + virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control @@ -145,6 +149,7 @@ namespace MWInput bool mMouseLookEnabled; bool mGuiCursorEnabled; + bool mDebug; float mMouseX; float mMouseY; diff --git a/apps/openmw/mwinput/sdlinputwrapper.cpp b/apps/openmw/mwinput/sdlinputwrapper.cpp index 61fc3fc8b..c8922cf3a 100644 --- a/apps/openmw/mwinput/sdlinputwrapper.cpp +++ b/apps/openmw/mwinput/sdlinputwrapper.cpp @@ -3,6 +3,7 @@ #include #include +#include #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX # include @@ -14,7 +15,11 @@ namespace MWInput { MWSDLInputWrapper::MWSDLInputWrapper(Ogre::RenderWindow *window) : - mWindow(window), mStarted(false), mSDLWindow(NULL) + mWindow(window), + mSDLWindow(NULL), + mWarpCompensate(false), + mWrapPointer(false), + mGrabPointer(false) { _start(); } @@ -26,44 +31,7 @@ namespace MWInput SDL_Quit(); } - void MWSDLInputWrapper::capture() - { - _start(); - - SDL_Event evt; - while(SDL_PollEvent(&evt)) - { - switch(evt.type) - { - case SDL_MOUSEMOTION: - mMouseListener->mouseMoved(ICS::MWSDLMouseMotionEvent(evt.motion)); - break; - case SDL_MOUSEWHEEL: - mMouseListener->mouseMoved(ICS::MWSDLMouseMotionEvent(evt.wheel)); - break; - case SDL_MOUSEBUTTONDOWN: - mMouseListener->mousePressed(evt.button, evt.button.button); - break; - case SDL_MOUSEBUTTONUP: - mMouseListener->mouseReleased(evt.button, evt.button.button); - break; - - case SDL_KEYDOWN: - mKeyboardListener->keyPressed(evt.key); - break; - case SDL_KEYUP: - mKeyboardListener->keyReleased(evt.key); - break; - } - } - } - - bool MWSDLInputWrapper::isModifierHeld(int mod) - { - return SDL_GetModState() & mod; - } - - void MWSDLInputWrapper::_start() + bool MWSDLInputWrapper::_start() { Uint32 flags = SDL_INIT_VIDEO; if(SDL_WasInit(flags) == 0) @@ -74,11 +42,15 @@ namespace MWInput //kindly ask SDL not to trash our OGL context SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - SDL_Init(SDL_INIT_VIDEO); + if(SDL_Init(SDL_INIT_VIDEO) != 0) + return false; //wrap our own event handler around ogre's mSDLWindow = SDL_CreateWindowFrom((void*)windowHnd); + if(mSDLWindow == NULL) + return false; + #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX //linux-specific event-handling fixups SDL_SysWMinfo wm_info; @@ -86,8 +58,6 @@ namespace MWInput if(SDL_GetWindowWMInfo(mSDLWindow,&wm_info)) { - printf("SDL version %d.%d.%d\n", wm_info.version.major, wm_info.version.minor, wm_info.version.patch); - Display* display = wm_info.info.x11.display; Window w = wm_info.info.x11.window; @@ -111,6 +81,122 @@ namespace MWInput XFlush(display); } #endif + SDL_ShowCursor(SDL_FALSE); + } + + return true; + } + + void MWSDLInputWrapper::capture() + { + if(!_start()) + throw std::runtime_error(SDL_GetError()); + + SDL_Event evt; + while(SDL_PollEvent(&evt)) + { + switch(evt.type) + { + case SDL_MOUSEMOTION: + //ignore this if it happened due to a warp + if(!_handleWarpMotion(evt.motion)) + { + mMouseListener->mouseMoved(ICS::MWSDLMouseMotionEvent(evt.motion)); + + //try to keep the mouse inside the window + _wrapMousePointer(evt.motion); + } + break; + case SDL_MOUSEWHEEL: + mMouseListener->mouseMoved(ICS::MWSDLMouseMotionEvent(evt.wheel)); + break; + case SDL_MOUSEBUTTONDOWN: + mMouseListener->mousePressed(evt.button, evt.button.button); + break; + case SDL_MOUSEBUTTONUP: + mMouseListener->mouseReleased(evt.button, evt.button.button); + break; + + case SDL_KEYDOWN: + mKeyboardListener->keyPressed(evt.key); + break; + case SDL_KEYUP: + mKeyboardListener->keyReleased(evt.key); + break; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + mWindowListener->windowFocusChange(true); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + mWindowListener->windowFocusChange(false); + break; + case SDL_WINDOWEVENT_EXPOSED: + mWindowListener->windowVisibilityChange(true); + break; + case SDL_WINDOWEVENT_HIDDEN: + mWindowListener->windowVisibilityChange(false); + break; + + //SDL traps ^C signals, pass it to OGRE. + case SDL_QUIT: + Ogre::Root::getSingleton().queueEndRendering(); + break; + } + } + } + + bool MWSDLInputWrapper::isModifierHeld(int mod) + { + return SDL_GetModState() & mod; + } + + void MWSDLInputWrapper::warpMouse(int x, int y) + { + SDL_WarpMouseInWindow(mSDLWindow, x, y); + mWarpCompensate = true; + mWarpX = x; + mWarpY = y; + } + + void MWSDLInputWrapper::setGrabPointer(bool grab) + { + SDL_bool sdlGrab = grab ? SDL_TRUE : SDL_FALSE; + + mGrabPointer = grab; + SDL_SetWindowGrab(mSDLWindow, sdlGrab); + } + + bool MWSDLInputWrapper::_handleWarpMotion(const SDL_MouseMotionEvent& evt) + { + if(!mWarpCompensate) return false; + + //this was a warp event, signal the caller to eat it. + if(evt.x == mWarpX && evt.y == mWarpY) + { + mWarpCompensate = false; + return true; + } + + return false; + } + + void MWSDLInputWrapper::_wrapMousePointer(const SDL_MouseMotionEvent& evt) + { + if(!mWrapPointer || !mGrabPointer) return; + + int width = 0; + int height = 0; + + SDL_GetWindowSize(mSDLWindow, &width, &height); + + const int FUDGE_FACTOR_X = width / 4; + const int FUDGE_FACTOR_Y = height / 4; + + //warp the mouse if it's about to go outside the window + if(evt.x - FUDGE_FACTOR_X < 0 || evt.x + FUDGE_FACTOR_X > width + || evt.y - FUDGE_FACTOR_Y < 0 || evt.y + FUDGE_FACTOR_Y > height) + { + warpMouse(width / 2, height / 2); } } } diff --git a/apps/openmw/mwinput/sdlinputwrapper.hpp b/apps/openmw/mwinput/sdlinputwrapper.hpp index ee07efbdb..d507073b8 100644 --- a/apps/openmw/mwinput/sdlinputwrapper.hpp +++ b/apps/openmw/mwinput/sdlinputwrapper.hpp @@ -16,19 +16,33 @@ namespace MWInput void setMouseEventCallback(ICS::MWSDLMouseListener* listen) { mMouseListener = listen; } void setKeyboardEventCallback(ICS::MWSDLKeyListener* listen) { mKeyboardListener = listen; } + void setWindowEventCallback(ICS::MWSDLWindowListener* listen) { mWindowListener = listen; } void capture(); bool isModifierHeld(int mod); + void setWrapPointer(bool wrap) { mWrapPointer = wrap; } + void setGrabPointer(bool grab); + + void warpMouse(int x, int y); private: + bool _handleWarpMotion(const SDL_MouseMotionEvent& evt); + void _wrapMousePointer(const SDL_MouseMotionEvent &evt); + + bool _start(); + ICS::MWSDLMouseListener* mMouseListener; ICS::MWSDLKeyListener* mKeyboardListener; - Ogre::RenderWindow* mWindow; - SDL_Window* mSDLWindow; + ICS::MWSDLWindowListener* mWindowListener; - bool mStarted; - void _start(); + Uint16 mWarpX; + Uint16 mWarpY; + bool mWarpCompensate; + bool mWrapPointer; + bool mGrabPointer; + Ogre::RenderWindow* mWindow; + SDL_Window* mSDLWindow; }; } diff --git a/extern/oics/OISCompat.h b/extern/oics/OISCompat.h index e8fd6904c..5813c17bf 100644 --- a/extern/oics/OISCompat.h +++ b/extern/oics/OISCompat.h @@ -91,5 +91,17 @@ public: virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;} }; +class MWSDLWindowListener +{ +public: + virtual ~MWSDLWindowListener() {} + + /** @remarks The window's visibility changed */ + virtual bool windowVisibilityChange( bool visible ) = 0; + + /** @remarks The window got / lost input focus */ + virtual bool windowFocusChange( bool have_focus ) = 0; +}; + } #endif