Search code examples
windowsopenglfocusmouseogre3d

Focus on MouseOver in Windows with Ogre3D


I have an application using the Ogre3D to create multiple render windows, and I'm using the solution posted here to support non-exclusive mouse input to these windows. However, I find that I have to physically click on a render window before it regains focus, whereas I'd really like the render windows to gain focus on a mouseover event. Is it possible to capture a mouseover event on an unfocused render window in Ogre3D/OIS and then subsequently set the focus for the render window?


Solution

  • To support this kind of functionality using Ogre3D in Windows, I had to implement a singleton object that kept a collection of all of the instantiated displays.

    class InputProcessor
    {
        /// @name Types
        /// @{
    public:
        /// @}
    
        /// @name InputProcessor implementation
        /// @{
    public:
        void addDisplay(Display* _pDisplay);
    
        bool processMouseMoved(int _x, int _y, int _z, int _keyModifier);
        bool processMousePressed(int _keyModifier, int _id);
        bool processMouseReleased(int _keyModifier, int _id);
    
        static InputProcessor& getSingleton();
        /// @}
    
        /// @name 'Structors
        /// @{
    private:
         InputProcessor();
        ~InputProcessor();
        /// @}
    
        /// @name Member Variables
        /// @{
    private:
        typedef std::set<Display*>     Displays_type;
        Displays_type                  m_displays;
        /// @}
    
    };  // class InputProcessor
    

    Then, in my UIFrameListener (which derives from Ogre3D's ExampleFrameListener), I transform the mouse window coordinates to global screen coordinates. If the mouse happens to reside outside of the window region, I apply the relative mouse movement to the last recorded mouse position; otherwise, I simply apply the absolute mouse position within the window:

    bool
    UIFrameListener::mouseMoved(const OIS::MouseEvent& e)
    {
        int keyModifierState = GetKeyModifierState();
        int windowLeft = m_display.getLeft();
        int windowTop = m_display.getTop();
        int windowWidth = m_display.m_pWindow->getWidth();
        int windowHeight = m_display.m_pWindow->getHeight();
    
        if (e.state.X.abs != 0 && e.state.X.abs != windowWidth)
        {
            m_lastX = e.state.X.abs;
        }
        else
        {
            m_lastX += e.state.X.rel;
        }
        int x = windowLeft + (m_display.m_width * m_lastX) / windowWidth;
    
        if (e.state.Y.abs != 0 && e.state.Y.abs != windowHeight)
        {
            m_lastY = e.state.Y.abs;
        }
        else
        {
            m_lastY += e.state.Y.rel;
        }
        int y = windowTop + (m_display.m_height * m_lastY) / windowHeight;
    
        int z = 0;
        if (e.state.Z.rel != 0)
        {
            z = e.state.Z.rel / -120;
        }
    
        return InputProcessor::getSingleton().processMouseMoved(x, y, z, keyModifierState);
    }
    

    And in InputProcessor::processMouseMoved(), I determine which window the mouse cursor is in (if any), and then set the focus appropriately, i.e.

    bool
    InputProcessor::processMouseMoved(int _x,
                                      int _y,
                                      int _z,
                                      int _keyModifier)
    {
        bool found = false;
    
        Displays_type::iterator iter = m_displays.begin();
        while (iter != m_displays.end() && !found)
        {
            int left = (*iter)->getLeft();
            int top = (*iter)->getTop();
            int width = (*iter)->m_pWindow->getWidth();
            int height = (*iter)->m_pWindow->getHeight();
    
            if (left <= _x && left + width  > _x &&
                top  <= _y && top  + height > _y)
            {
                found = true;
            }
            else
            {
                iter++;
            }
        }
    
        if (iter != m_displays.end())
        {
            int left = (*iter)->getLeft();
            int top = (*iter)->getTop();
    
            (*iter)->m_pContext->ProcessMouseMove(
                _x - left, _y - top, _keyModifier
            );
    
            (*iter)->m_pContext->ProcessMouseWheel(_z, _keyModifier);
    
            if (!(*iter)->hasFocus())
            {
                (*iter)->setFocus(true);
            }
        }
    
        return true;
    }
    

    And in the implementation of Display, I have a method Display::setFocus() that sets the focus on the appropriate window:

    void
    Display::setFocus(bool _hasFocus)
    {
        if (m_handle != NULL && _hasFocus)
        {
            SetFocus(m_handle);
        }
    }