SFML community forums
Help => Window => Topic started by: model76 on February 10, 2010, 08:49:16 am
-
I am having a weird problem with GetInput() in combination with window.GetEvent() and sf::Event::MouseMoved.
Here is the offending code: sf::Event event;
while(window.GetEvent(event))
{
...snip...
else if(event.Type == sf::Event::MouseMoved)
{
sf::Vector2f canvasMouseCoord = window.ConvertCoords(event.MouseMove.X, event.MouseMove.Y, &canvasCam);
// If we're in drag-mode (the left mouse button is down)
if(dragging)
{
// If a vertex is selected, drag it to the mouse position
if(selectedVertice != -1)
{
sf::Vector2f newPosition(canvasMouseCoord.x/meter_to_pixel, canvasMouseCoord.y/meter_to_pixel);
// If left shift and left control is held down, snap to 1cm
if(window.GetInput().IsKeyDown(sf::Key::LShift) && window.GetInput().IsKeyDown(sf::Key::LControl))
{
newPosition.x = floor((newPosition.x * 100.f) + .5f)/100.f;
newPosition.y = floor((newPosition.y * 100.f) + .5f)/100.f;
}
// If left control is held down, snap to 10cm grid
else if(window.GetInput().IsKeyDown(sf::Key::LControl))
{
newPosition.x = floor((newPosition.x * 10.f) + .5f)/10.f;
newPosition.y = floor((newPosition.y * 10.f) + .5f)/10.f;
}
shape.point[selectedVertice] = newPosition;
}
}
}
}
Everything works well when I only use left control, but if I use left control plus left shift, and move the mouse around real quick, then GetInput() doesn't seem to register when I let go of left shift - window.GetInput().IsKeyDown(sf::Key::LShift) simply keeps evaluating true.
If I hold the mouse still and press shift again, then the problem goes away.
This is the only place in the code where I do anything with keyboard events, at all.
Is this a bug, or am I using it wrong?
I am using Visual C++ 2008 Express on Windows XP.
-
Hi
Can you give a complete and minimal example that I can copy, paste and test directly?
-
Hello Laurent,
Just so you know I haven't forgotten you, I am still working on your minimal example. I now think the problem is related to passing the RenderWindow object to a function. At least the problem goes away when I remove that line...
-
Here you go, Laurent, I finally finished, and also learned a lot more about the bug, which I now think it is.
/***********************************************************
* Possible Window.GetInput() bug Minimal example
***********************************************************/
///////////////////////////////////////////////////////////
// Headers
///////////////////////////////////////////////////////////
#include <SFML/Graphics.hpp>
#include <sstream>
#include <string>
///////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////
template <class T>
std::string to_string( T input )
{
std::stringstream sstream;
sstream << input;
return sstream.str();
}
void my_function(sf::RenderWindow& window)
{
sf::Sleep(.1f);
}
///////////////////////////////////////////////////////////
// Application entry point
///////////////////////////////////////////////////////////
int main()
{
/////////////////////////////
// Setup variables
/////////////////////////////
sf::RenderWindow window(sf::VideoMode(1024, 768, 32), "Possible Window.GetInput() bug Minimal example");
sf::String output;
/////////////////////////////
// Main loop
/////////////////////////////
while(window.IsOpened())
{
/////////////////////////
// Handle events
/////////////////////////
sf::Event event;
while(window.GetEvent(event))
{
if(event.Type == sf::Event::Closed)
window.Close();
}
/////////////////////////
// Draw
/////////////////////////
window.Clear();
my_function(window);
output.SetText( "LShift is down: " + to_string(window.GetInput().IsKeyDown(sf::Key::LShift)) + "\n" +
"LControl is down: " + to_string(window.GetInput().IsKeyDown(sf::Key::LControl)) );
window.Draw(output);
window.Display();
}
return EXIT_SUCCESS;
}
I have now learned that it hasn't got anything to do with the MouseMoved event, but with passing the RenderWindow to a function that takes a little time to complete.
The longer the function takes to complete, the faster the problem will occur, it seems.
This example will also show you that the problem does not happen with the left control key.
To invoke the bug, simply hold down the left shift key for a while (2-3 seconds is enough on this computer), and let go.
Window.GetInput().IsKeyDown(sf::Key::LShift) will now keep evaluating to true, until you press it again and let go.
-
Thanks for the code :)
There seems to be a bug, indeed. I'll investigate that.
-
I solved the problem. The fix will be in the 1.6 release.
Thanks a lot for your help.
-
That was fast! And no problem about the code - that's the least I could do as payment for you making SFML. Great deal, I think!
Unfortunately, I may have found another one, also related to GetInput().
A wiser man than before, I am including a minimal example:/***********************************************************
* Possible Window.GetInput() bug No. 2 Minimal example
***********************************************************/
///////////////////////////////////////////////////////////
// Headers
///////////////////////////////////////////////////////////
#include <SFML/Graphics.hpp>
///////////////////////////////////////////////////////////
// Application entry point
///////////////////////////////////////////////////////////
int main()
{
/////////////////////////////
// Setup variables
/////////////////////////////
sf::RenderWindow window(sf::VideoMode(1024, 768, 32), "Possible Window.GetInput() bug No. 2 Minimal example");
bool mouseClicked = false;
sf::String output;
/////////////////////////////
// Main loop
/////////////////////////////
while(window.IsOpened())
{
/////////////////////////
// Handle events
/////////////////////////
sf::Event event;
while(window.GetEvent(event))
{
if(event.Type == sf::Event::Closed)
window.Close();
else if(event.Type == sf::Event::MouseButtonPressed)
{
if(event.MouseButton.Button == sf::Mouse::Left)
mouseClicked = true;
else if(event.MouseButton.Button == sf::Mouse::Right)
{
mouseClicked = false;
output.SetText("");
}
}
else if(event.Type == sf::Event::MouseMoved)
{
if(window.GetInput().IsMouseButtonDown(sf::Mouse::Left) && mouseClicked == false)
output.SetText("Mouse was down before it was clicked!");
}
}
/////////////////////////
// Draw
/////////////////////////
window.Clear();
window.Draw(output);
window.Display();
}
return EXIT_SUCCESS;
}
With this program, if I click the left mouse button, while moving the mouse around, window.GetInput().IsMouseButtonDown(sf::Mouse::Left) evaluates to true before I get the chance to handle the MouseButtonPressed event with Window.GetEvent().
I can see how this could be interpreted as a "feature", but intuitively, shouldn't IsMouseButtonDown() only evaluate true after the MouseButtonPressed event has happened? Otherwise, IsMouseButtonDown() can't really be used as a true substitute manually handling "is down" states.
Oh, and in the program, you can use the right mouse button to reset.
-
shouldn't IsMouseButtonDown() only evaluate true after the MouseButtonPressed event has happened?
It does. But it can't happen after you handle the event.
Otherwise, IsMouseButtonDown() can't really be used as a true substitute manually handling "is down" states.
Well, your example doesn't look like a real one. In real code I don't think that it is really an issue.
-
Well, actually, I stumbled on it while making my current project - a kind of drawing program for making polygons.
-When a left mouse down event happens over a vertex, that vertex is selected.
-When a left mouse down event happens over a face, if the conditions are right, a new vertex is created and selected.
-When a left mouse down event happens over empty space, any selected vertex is deselected.
Anytime the user moves the mouse with the left mouse button down, it means that they are dragging something.
-If a vertex is selected, then that vertex is repositioned.
-If no vertex is selected, then the view is moved.
So you can probably see why, in this case, IsMouseButtonDown() is not suitable as a substitute for manually handling the "left mouse button is down" case.
Also, I don't deliberately think up ways to make SFML act undesirably. When I report something, it is because I am having a real issue with it, in real code. Of course, you can't see that from a minimal example. :wink:
-
I see.
I agree with your conclusion
in this case, IsMouseButtonDown() is not suitable as a substitute for manually handling the "left mouse button is down" case.
But I don't think that there is a solution to this problem, I can't make sf::Input aware of the mouse button down event after you process it in your event loop.
sf::Input is a convenience class, it does nothing that you can't do with events. So just don't use it in the situations where it is not suitable ;)
-
Not being an expert, and completely disregarding the fact that there could be more than one window, it seems like you could just let Window.GetEvent() set the state of the IsMouseButtonDown(sf::Mouse::Left). Of course, that would require a proper event loop, to work correctly.
I suggest the following solutions:
For SFML 1, document the cases where IsMouseButtonDown() cannot be used as a true substitute manually handling "is down" states.
For SFML 2, try to think up a new system that is both convenient, and easy to fully grasp for beginners.
Maybe such a system is as simple as the one described above?
-
it seems like you could just let Window.GetEvent() set the state of the IsMouseButtonDown(sf::Mouse::Left)
It does (if you never call GetEvent, IsMouseButtonDown will never be true), but it still happens before your own code.
-
OK, but then maybe I didn't explain myself well enough.
What I mean is that you could let Window.GetEvent() update the Window.GetInput().IsMouseButtonDown(sf::Mouse::Left) state only when Event.Type is sf::Event::MouseButtonPressed and Event.MouseButton.Button is sf::Mouse::Left.
Does this make sense?
-
OK, but then maybe I didn't explain myself well enough.
What I mean is that you could let Window.GetEvent() update the Window.GetInput().IsMouseButtonDown(sf::Mouse::Left) state only when Event.Type is sf::Event::MouseButtonPressed and Event.MouseButton.Button is sf::Mouse::Left.
Does this make sense?
I think that's what he does.
-
I think that's what he does.
Yes, maybe he does it in myWindow->DoEvents(). I wasn't able to find that method... But if it is done just before the event is popped, and not while filling the event cue, then the problem should go away, no?
-
So to be able to use sf::Input, one would have to pop all the corresponding events in his event loop, even if he doesn't use them? That doesn't make sense.
sf::Input is real-time while events are queued, so I can't do anything to force sf::Input to be synchronized with your own event handling, which could happen a long time after the event was actually triggered.
If you want a "pressed" state that exactly matches the events, don't use sf::Input.
bool pressed = false;
...
if (event.Type == sf::Event::MouseButtonPressed)
{
pressed = true;
}
else if (event.Type == sf::Event::MouseButtonReleased)
{
pressed = false;
}
else if (event.Type == sf::Event::MouseMoved)
{
if (pressed)
{
}
}
-
To me, this is not a discussion about my particular application, but about the workings of SFML. I hope this is OK, and maybe even encouraged?
The reason I keep posting about this is not to annoy you, but because, the more I look at this, the more inconsistent it seems.
So to be able to use sf::Input, one would have to pop all the corresponding events in his event loop, even if he doesn't use them? That doesn't make sense.
I am not sure what you mean. Since one can't pick and choose which event type will be popped next, all of them will be popped in order, whether they are handled or not. You still need to pop "unused" events for sf::Input to work, as it is.
sf::Input is real-time while events are queued, so I can't do anything to force sf::Input to be synchronized with your own event handling, which could happen a long time after the event was actually triggered.
I will refrain from commenting on the first part of the sentence, since that is entirely up to the implementation, which is exactly what we're discussing. The second part, about your inability to ensure when the event handling takes place, seems equally true for both approaches, though.
Maybe the problem is that I don't understand the purpose of sf::Input completely, because I am less experienced, or maybe even not as clever as you. But it was this part of the documentation that gave me the the understanding that sf::Input could be used as a substitute for manually handling "is down":A better strategy for this is to set a boolean variable to true when the key is pressed, and clear it when the key is released. Then at each loop, if the boolean is set, you move your character. However it can become annoying to use extra variables for this, especially when you have a lot of them. That's why SFML provides easy access to real-time input, with the sf::Input class.
When I read that I thought "Oh, wow! That is so cool!", because I too think it is annoying to have to bloat my program code with redundant bool values to see if a key is down. But as it stands, sf::Input does not free users from that task - at least not in all situations.
But even if you should happen to agree with me on all points, I understand why you might still be reluctant to change it: You might be breaking someone else's code, in ways I can't even begin to imagine, and you might be opening up a new can of worms in the form of new bugs to solve, when you really would rather get on with SFML 2.
So my recommendation still stands:
Document when sf::Input is unsuitable for handling "is down" in SFML 1, with the added recommendation that you also document that a proper GetEvent loop must be in place for sf::Input to work. This is not obvious from the documentation, and might lead a beginner to try handling events with sf::Input only.
Figure out a better, easier to understand approach for SFML 2. My personal wish would be for a true automatic "is down" bool substitute.
And lastly, thank you for taking the time to listen to my arguments. I hope you will appreciate and consider my input. I only took the time to put it forward because I appreciate SFML so much.
-
To me, this is not a discussion about my particular application, but about the workings of SFML. I hope this is OK, and maybe even encouraged?
It is, and I really appreciate that you don't just say "ok, you created SFML so you must be right and I must be wrong, thank you bye bye" :)
I really need this kind of discussions, because I can't focus on every detail of SFML, and I can't be right about everything.
I think you're right, the update of sf::Input and the call to GetEvent are tied together anyway, so there's no reason not to synchronize them properly. It's just a matter of sending an event to you and to sf::Input at the same time, rather than sending all of them (I mean, all that is in the queue) to sf::Input first and then to you. Not a big deal, and it will ensure a perfectly consistent behaviour when sf::Input and events are mixed.
I'll think about it again later, in order to ensure that there's no special case that we forgot, and then I'll probably write a better implementation in SFML 2.
And I totally agree on the documentation, many important details like this one need to be added.
And lastly, thank you for taking the time to listen to my arguments. I hope you will appreciate and consider my input
I really do, thank you ;)
-
Very good, Laurent, that makes me a little proud, actually. :)
Let me know what your final decision is, will you?
Thanks again!
-
Let me know what your final decision is, will you?
Sure :)
-
It's done (in SFML 2) ;)
I did the simple modification that you suggested: sf::Input is now notified only when the event is popped by the user, not when it is pushed into the event queue.
-
OK, cool :)
So SFML 1 will stay as it is?
-
Yes.
-
I got confused about it.. for now, I haven't any problems with sf::Input. I am calling the empty getEvent loop before using sf::Input class, and it works as expected (version 1.6, MingW 4.4.0 under Windows)
Should I change my code after I update to SFML 2?
Or empty GetEvent loop is enough?
-
No, this modification won't change anything in SFML 2 from the user point of view.