Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Relative Mouse Movement (Mouse Capture)  (Read 30980 times)

0 Members and 3 Guests are viewing this topic.

TheMaskedFox

  • Newbie
  • *
  • Posts: 7
    • View Profile
Relative Mouse Movement (Mouse Capture)
« on: October 10, 2012, 03:41:22 am »
This proposal stems from an issue raised on the French forum.

http://fr.sfml-dev.org/forums/index.php?topic=9075.msg62994#msg62994

For first-person games with mouse control, developers need relative mouse movement, as opposed to absolute coordinates.  SFML needs either a window style or a window method that locks the mouse, hides the cursor, and sends sf::Event::MouseMove events containing relative x and y updates.

Windows, X11, and OS X will all accomplish this differently, so this feature could take some time to fully implement.  It's critical for providing mouse support in an FPS though, so it would be well worth the time spent.

To accomplish this on OS X, the mouse cursor has to be hidden, the mouse has to be disconnected from the cursor, and the mouse delta has to be polled by the application for changes.  I've posted a sort of "starter kit" for accomplishing this over on Github (https://github.com/SFML/SFML/issues/290#issuecomment-9279071).

It's been so long since I did any WinAPI or X11 development that I don't have immediate suggestions for how to accomplish mouse locking on those platforms.  Relative mouse movement is a feature common to many games, but its implementation is platform specific.  It's the kind of input abstraction that belongs in SFML.

Developers would either call something like window.captureMouse(), or it would be passed as an option on window creation with a style like sf::Style::CaptureMouse.  In either case, they'd receive a window that has essentially "captured" the mouse, locking the cursor down and hiding it so that all mouse movements are reported as some distance from zero.  Anytime the mouse stops moving, it automatically returns to zero, and any further movement is again reported as some distance from zero.

As an example, on the X axis a reading of -5 would mean tell the game that the user moved the mouse slightly to the left.  Another reading of -35 would mean the user moved the mouse to the left faster.  A subsequent reading of 100 would indicate that the user has snapped the mouse quickly to the right.  This is a rough example, but illustrates how relative mouse movements are helpful.
« Last Edit: October 10, 2012, 03:59:46 am by TheMaskedFox »

model76

  • Full Member
  • ***
  • Posts: 231
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #1 on: October 10, 2012, 05:11:14 am »
This is possible with the current API. Here is how to do it:

Set the mouse cursor's position to the center of the screen. Whenever the mouse is moved, simply calculate the offset and reset cursor's position to the center of the screen.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #2 on: October 10, 2012, 09:48:50 am »
Yes it's possible to some extend, but the mouse doesn't really get locked, thus if you move it fast in window mode or with two screens the cursor will jump out of the window. Now the big problem with that is when you click, in the instance the mouse is outside the window, the SFML window will lose focus and on fullscreen mode it might change resolutions and switch back to the desktop, which makes it kind of impossible to create a FPS.

Instead of delta movement I'd rather request for a mouse lock style/function. ;)
« Last Edit: October 10, 2012, 12:58:34 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

model76

  • Full Member
  • ***
  • Posts: 231
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #3 on: October 10, 2012, 01:22:57 pm »
Now the big problem with that is when you click, in the instance the mouse is outside the window
That should not be possible with the method I described.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #4 on: October 10, 2012, 01:56:10 pm »
In theory with unlimited FPS count yes, but if the FPS is around 60 you can easily move the mouse faster than the width/height of the window.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

model76

  • Full Member
  • ***
  • Posts: 231
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #5 on: October 10, 2012, 03:36:59 pm »
I suppose if you were able to move the cursor outside the window and also produce a mouse down event, within the same update cycle, maybe a problem could arise.
But this is all very theoretical, and a lot of ifs and maybes, so I would like to know if you are actually able to produce an error like this in practice?

I have successfully used this method in the past with no problems, but it was a long time ago, and I think with SFML 1.x.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #6 on: October 10, 2012, 05:11:11 pm »
I suppose if you were able to move the cursor outside the window and also produce a mouse down event, within the same update cycle, maybe a problem could arise.
This has nothing to do with events. Events are bound to the window but the cursor isn't. ;)

Well it's kind of hard to get a nice demo since the recording does heavily influence the performance but I've made a demo anyways (the code is below):


#include <SFML/Graphics.hpp>
#include <iostream>
#include <sstream>

int main ( int argc, char** argv )
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML thread example");
    window.setFramerateLimit(120); // Limited FPS

    sf::Clock clock;
    sf::Time dt;

    sf::Font font;
    font.loadFromFile("C:/Windows/Fonts/Arial.ttf");
    sf::Text text("0", font);

    sf::Vector2i deltas;
    sf::Vector2i fixed(window.getSize());

    while (window.isOpen())
    {
        dt = clock.restart();

        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();

            else if (event.type == sf::Event::LostFocus)
                std::cout << "Lost focus D:" << std::endl;
        }

        deltas = fixed-sf::Mouse::getPosition();
        if(deltas != sf::Vector2i(0, 0))
            sf::Mouse::setPosition(fixed);

        std::stringstream ss;
        ss << 1/dt.asSeconds();
        text.setString(ss.str());

        window.clear();
        window.draw(text);
        window.display();
    }

    return 0;
}
 

For the recording I used 120 fps in the limiter but it also drops down further while recording. You can try the code yourself and if you don't want to you just need to belief me that this is nothing 'theoretical'. ;)
Also the behavior is quite logical, since we limit the FPS the application isn't able to capture everything what's happening (sf::sleep(x) is called = the thread sleeps for x milliseconds), thus he doesn't notice the mouse movement until he 'wakes' up again.
Now one could argue and say that you shouldn't limit the fps then. This will probably work for a while but has two flaws: a) The CPU will be unnecessarily maxed out b) if the application's logic & rendering get more time costly the fps will get limited automatically and you'd miss out on some stuff that happens.
So I conclude that the only way to really make sure the mouse gets reset fast enough is having one thread the runs at it's maximum speed and constantly checks if the mouse position has changed.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

model76

  • Full Member
  • ***
  • Posts: 231
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #7 on: October 10, 2012, 08:36:18 pm »
This has nothing to do with events. Events are bound to the window but the cursor isn't. ;)
Well surely, you would have to cause an event somewhere in order for the window to loose focus, would you not? ;)

I implemented the solution myself, and it seems that 2 things have changed since SFML 1.x:
  • sf::Mouse::setPosition now causes an sf::EventMouseMoved. To get around that, check if the mouse cursor is already in the center of the window before setting it. Otherwise you will have an endless loop.
  • Moving the mouse cursor outside the window will cause the SFML window to stop producing MouseMoved events. To get around this one, simply also check for sf::Event::MouseLeft.

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window( sf::VideoMode( 800, 600 ), "SFML" );
    window.setVerticalSyncEnabled( true );

    const sf::Vector2i windowCenter( 400, 300 );

    // Set the mouse cursor's position to the center of the screen.
    sf::Mouse::setPosition( windowCenter, window );

    while( window.isOpen() )
    {
        sf::Event event;
        while( window.pollEvent( event ) )
        {
            if( event.type == sf::Event::Closed )
                window.close();
            else if( ( event.type == sf::Event::MouseLeft || event.type == sf::Event::MouseMoved ) &&
                     sf::Mouse::getPosition( window ) != windowCenter )
            {
                // Whenever the mouse is moved or leaves the window, simply calculate the offset
                // and reset cursor's position to the center of the screen.
                // For this test I am not going to calculate the offset.
                sf::Mouse::setPosition( windowCenter, window );
            }
        }

        window.clear();
        window.display();
    }
}
 

Can you make the window loose focus with the mouse with this solution? If you can, then perhaps Laurent should take a look at it.
« Last Edit: October 11, 2012, 01:54:54 am by model76 »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #8 on: October 10, 2012, 09:03:18 pm »
Well surely, you would have to cause an event somewhere in order for the window to loose focus, would you not? ;)
Sure but like you said somewhere and that doesn't have anything to do with SFML. ;)

sf::Mouse::setPosition now causes an sf::EventMouseMoved. To get around that, check if the mouse cursor is already in the center of the window before setting it. Otherwise you will have an endless loop.
Wrong sf::Mouse and the window event system don't have anything in common. Note events are triggered by the OS and sent to the window, while sf::Mouse access the device itself. ;)

Moving the mouse cursor outside the window will cause the SFML window to stop producing MouseMoved events. To get around this one, simply also check for sf::Event::MouseLeft.
MouseMoved event shouldn't have been triggered in SFML 1.6

Can you make the window loose focus with the mouse with this solution? If you can, then perhaps Laurent should take a look at it.
Sure I can get reproduce the problem. Additionally my 'logical' argumentation hasn't changed either. ;)

Btw you're implementation has twice sf::Event event; declared. And since you're checking sf::Mouse::getPosition() inside the event loop it probably doesn't event reset the position for all the possible interations... ;)
« Last Edit: October 10, 2012, 09:08:10 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

model76

  • Full Member
  • ***
  • Posts: 231
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #9 on: October 11, 2012, 02:40:27 am »
Sure but like you said somewhere and that doesn't have anything to do with SFML. ;)
I never said it did.

Wrong sf::Mouse and the window event system don't have anything in common. Note events are triggered by the OS and sent to the window, while sf::Mouse access the device itself. ;)
So are you saying that calling sf::Mouse::setPosition does not cause an sf::EventMouseMoved event to happen?
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window( sf::VideoMode( 800, 600 ), "SFML" );

    while( window.isOpen() )
    {
        sf::Event event;
        while( window.pollEvent( event ) )
        {
            sf::Mouse::setPosition( sf::Vector2i( 400, 300 ), window );

            if( event.type == sf::Event::Closed )
                window.close();
        }

        window.clear();
        window.display();
    }
}
 
On my system, this program gets stuck in the event loop, and the window is never cleared.

Sure I can get reproduce the problem.
I checked, and it seems to be the case. By moving the mouse quickly and pressing a button, it is possible to make the window loose focus. It will be interesting to hear what Laurent has to say about it.

Btw you're implementation has twice sf::Event event; declared.
It certainly did. Must have happened when I was replacing the TAB characters with spaces. Anyway, it is now corrected.

And since you're checking sf::Mouse::getPosition() inside the event loop it probably doesn't event reset the position for all the possible interations... ;)
I am not sure it matters, since I believe that the global inputs and events are synchronized in SFML 2. I haven't checked the documentation or done any tests, though. It was just something I talked to Laurent about long ago in 1.x where they were not synchronized.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #10 on: October 11, 2012, 07:20:12 am »
So are you saying that calling sf::Mouse::setPosition does not cause an sf::EventMouseMoved event to happen?
On my system, this program gets stuck in the event loop, and the window is never cleared.
Sure this ends up in a infinity loop, but that does say anything about the two systems begin connected.
The only thing that happens is that you move the mouse over the window which triggers an sf::EventMouseMoved event by the OS that gets send to the event loop which triggers and event and so on, but it's not SFML that triggers the event.
You can for instance set the mouse pointer outside the window and not get stuck in the event loop.

I am not sure it matters, since I believe that the global inputs and events are synchronized in SFML 2. I haven't checked the documentation or done any tests, though. It was just something I talked to Laurent about long ago in 1.x where they were not synchronized.
Well then you certaintly should take a look again at the implementation (as I have) and they are not connected, which also is what Laurent has said many times and what the whole sense was when splitting up sf::Mouse, sf::Keyboard and sf::Joystick from the sf::Window class.
As example don't use a sf::Window/sf::RenderWindow but use a plain old terminal and the include the Mouse.hpp header and retrive the mouse position etc. If the events and sf::Mouse where connected things wouldn't work (since you don't have an window = you don't have a event queue).
So the systems are not connected. :)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Robert42

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #11 on: October 14, 2012, 03:59:33 pm »
I think it's also important to discuss, whether this feature fits to SFML's design.

If not, I would suggest to create a wiki-page-tutorial howto implement relative mouse movement working on slow machines.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #12 on: October 14, 2012, 05:39:55 pm »
If not, I would suggest to create a wiki-page-tutorial howto implement relative mouse movement working on slow machines.
Relative mouse positions are already possible with SFML... (sf::Mouse)
Also how is this connected to slow machines? ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Robert42

  • Newbie
  • *
  • Posts: 31
    • View Profile
Re: Relative Mouse Movement (Mouse Capture)
« Reply #13 on: October 14, 2012, 06:09:47 pm »
Relative mouse positions are already possible with SFML... (sf::Mouse)
I have only browsed over this thread very fast. I think you've written that low fps (that's why I said slow machines) and window mode can cause the window to loose focus.

Correct me if I am wrong, but I also think to have read, that you've written that a solution for this would be using a seperate thread to get the mouse delta using sf::Mouse

So yes it is possible to implement relative mouse movement using sfml only. But it seems to require some extra amount of work (most implementation would make some mistake when using multiple threads) implemented multiple times for different projects.
So writing library or writing a tutorial could be useful.

This thread is a feature request about doing exactly this in sfml. As a feature itself it would be clearly useful. So it's imho a design question whether to add this to sfml.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Relative Mouse Movement (Mouse Capture)
« Reply #14 on: October 14, 2012, 06:28:55 pm »
Okay let me clarify things again:

Relative mouse position (deltas) can be used with the current SFML 2 and it works quite well if you have a high enough update rate (i.e. you can separate the updating & drawing).

The only problem at this point (and that's the only thing I'd request as a feature) is that the mouse pointer isn't locked within the window, thus with a fast mouse movement in combination with a lower update rate it's possible that the mouse cursor leaves the window for a split second, within which you could click outside the window by accident (see video above).

Correct me if I am wrong, but I also think to have read, that you've written that a solution for this would be using a seperate thread to get the mouse delta using sf::Mouse
That was more a theoretical answer than one that should be implemented in any way. The theory behind was that you could max out that full processor (given that you're on a multi-processor architecture) and thus you had a very high update rate which reduces the chances of you being able to move the mouse outside the window. On the other hand you'd probably could run into troubles with getting useful deltas and you'd max out one CPU core which you shouldn't do (if not nessecary).

So at the moment you can implement the original requested features but it has some (possible) problems which could be fixed with having a way to lock the mouse within the window.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/