There used to be a function named SDL_EnableKeyRepeat() in SDL, but not anymore in SDL2.
I searched around in SDL2-wiki but failed to locate anything relevant.
Any ideas?
When handling a keyboard event, just filter out any events that are repeat events, i.e. check the repeat field of the SDL_KeyboardEvent of the SDL_Event union.
For example:
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
if (event.key.keysym.sym == SDLK_d)
debug = debug ? false : true;
// ... handle other keys
}
}
See https://wiki.libsdl.org/SDL_KeyboardEvent
You may be better served by the SDL_GetKeyboardState function which tells you all the keys that are pressed. You can also just check the repeat flag in the event and ignore when repeat is true.
You can do it yourself, by adding your "down" key into a list and removing them when you catch a KEY_UP and at each frames you can iterate your list to know which key is still down.
Related
I'm trying to detect keyPress only once, but it goes for random amount of presses. I'm using keyrelease, and it work normally if I have breakpoint.
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
} else {
characterScreen.handleEvent(event, window);
}
}
bool keyReleased = event.type == sf::Event::KeyPressed;
bool rightArrowKey = event.key.code == sf::Keyboard::Right;
bool leftArrowKey = event.key.code == sf::Keyboard::Left;
if (keyReleased&&leftArrowKey)
{
if (selectedCharacter == 0)
{
selectedCharacter = characterList.size() - 1;
}
else
{
selectedCharacter--;
}
changeCharacters(characterList[selectedCharacter], font, characterScreen);
std::cout << "left pressed" << std::endl;
keyReleased = false;
std::cout << keyReleased << std::endl;
}
else if (keyReleased && rightArrowKey)
{
if (selectedCharacter == characterList.size()-1)
{
selectedCharacter = 0;
}
else
{
selectedCharacter++;
}
changeCharacters(characterList[selectedCharacter], font, characterScreen);
}
window.clear(sf::Color::White);
characterScreen.update();
characterScreen.render(window);
window.display();
}
I've tryed window.setKeyRepeatEnabled(false); and keypressed, still nothing. Tryed different keyboard aswell.
When using (C)SMFL, we generally have a main loop condition rendering the main frame (the windows) and checking events, it can be an infinite loop (such as while (1)) or a more recomanded one like your while (window.isOpen()).
It means that the windows and all events listener are refreshed / updated each loop's turn, so if you press a key on your keyboard, you will trigger your keyboard's event listener, fine, and you will continue your code until re-enter in your main loop, and re-refreshing the windows and re-updating your event listener.
What the point of explaining that ? it just means by only pressing once a key, your computer will process maybe 10, 100 or maybe 1000 iteration of your main loop (depending on it's performances and more). So it's "normal" to works fine when you use convenient features like breakpoints, but in reality you can't.
You have a lot of possibilities, bad ones (like "use delay / pause" who's not recommended since it will halt your window's refresh and can be interpreted by a crash by your OS) or good ones like tampon variables, who'll cancel the entrance of some conditions by their state:
Example:
bool tmp_var = false;
while (window.isOpen()) {
// Warn, you called your variable keyReleased but it check keyPressed
if (event.type == sf::Event::KeyPressed && tmp_var == false) {
// Check wich key is pressed and perform required actions
tmp_var = true;
} else if (event.type == sf::Event::KeyReleased) {
tmp_var = false;
}
}
Note that the code can be not correct, I wanted to point out the reasoning
I am using sfml library for my graphics. During paused I want to avoid drawing on the screen. I want to intercept a spacebar press using sf::Event::MouseButtonPressed. In the debug mode, the event is successfully intercepted but failed in normal execution, why? The bool variable 'paused' don't seemed to update.
while (window.isOpen()) {
Event event;
while (window.pollEvent(event)) {
/*..*/
if (event.type = Event::KeyPressed) {
if (event.key.code == Keyboard::Space) {
if (!paused) {
paused = true;// UDATE HERE
}
else
if (paused)
{
paused = false; // UDATE HERE
}
}
}
}
if (!paused) //// UDATE DONT SEEM TO AFFECT HERE
{
/* draw here */
}
}
Edited: Even tried by declaring the variable paused as atomic, but still don't work.
Use the Event::KeyReleased instead of the Event::KeyPressed as the key press gets called every loop that a key is down leading to many enters while the Release event is only called once per click.
trying to setup my classes to programm faster with SDL2 - so far evrything worked fine.
But in the end of my last coding session my SDL_PollEvent does no longer catch SDL_QUIT.
I´m using my own SDL_Window class to handle events and other SDL relevant Vars.
In my Window class i have the function loadEvent(), its my gameloop function, and in it i call SDL_PollEvent and right after i check if it is SDL_QUIT and if so it returns a false and ends the gameloop, otherwise it returns true. SDL_PollEvent saves into a private SDL_Event varriable of my class so i can later ask for keys pressed by looking into the event of my window class.
Any keydown event works just fine, i can end my game with esc if i want to, but i never seem to catch SDL_QUIT.
There is no error, it just does not get any SDL_QUIT back.
Any ideas what could be the problem?
Code:
class My_SDL_Window{
private:
SDL_Window* window;
SDL_Surface* surface;
SDL_Renderer* renderer;
SDL_Event sEvent;
My_Object_Node* allObj;
public:
…
bool loadEvent()
{
if(SDL_PollEvent( &sEvent ) != 0)
{
if(sEvent.type == SDL_QUIT)
{
destroy();
return false ;
}
}
return true;
}
You're only checking whether there's an event once every time the program loops.
Replace:
if(SDL_PollEvent( &sEvent ) != 0) , which only checks whether there are any events at all, where you then only act on the first event in that list, with:
while (SDL_PollEvent(&sEvent)) , which loops through every event in the event queue, where you then act on every single event that was performed since the last time you checked.
You need to change how you handle the SDL_PoolEvent() instead a if use while, with this method you will catch all events on pool not only the first one.
For example:
bool quit = false;
SDL_Event event;
while(!quit) {
// Process input
while(SDL_PollEvent(&event) > 0) {
switch(event.type) {
case SDL_QUIT: quit = true; break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym) {
case SDLK_UP: go_up(); break;
case SDLK_DOWN: go_dowm(); break;
// ... other keyboard handling
}
break;
}
}
// Update state
do_game_state_update();
// Render
do_screen_render();
}
I'm trying to make a video player using SDL and ffmpeg in C++. I've created two separate threads, one that renders the video on the SDL window and one that handles window events. When the user clicks and releases on the video I want it to toggle playback/pause. However, it fires multiple times and the event occurs even before I release the mouse which results in unpredictable behavior.
My code:
SDL_Event event;
while (1)
{
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
SDL_DestroyWindow(screen);
SDL_Quit();
break;
case SDL_MOUSEBUTTONUP:
if (event.button.state == SDL_RELEASED)
{
printf("Mouse released\n");
}
break;
}
}
When I click the window and hold down I would expect it wouldn't print Mouse released until I release the button. However, it prints Mouse released the entire time I hold down the mouse button. I don't know if maybe this has to do with me using a touchpad on my laptop.
SDL_PollEvent has a return value, you are ignoring.
[It] returns 1 if there are any pending events, or 0 if there are none available.
Given your code logic, whenever there is no pending event, you keep handling the previous event over and over again, until a new event arrives. This leads to the observed behavior.
The easiest fix would be to wrap the entire event handling inside an if (SDL_PollEvent(&event)) { /* Event handling */ } conditional.
EDIT: My answer is wrong, check IInspectable's answer.
Your error is that you're not checking all the pending events given by pollEvent, just one. Try this code and tell me how many button ups you get.
#include <SDL2/SDL.h>
#include <iostream>
int main(int argc, char *argv[]) {
if(SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("SDL failed to initialize.\n");
}
SDL_Window *window = SDL_CreateWindow("App", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, NULL);
bool done = false;
while(!done) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT) {
done = true;
}
if (event.type == SDL_MOUSEBUTTONUP) {
if (event.button.state == SDL_RELEASED) {
printf("Mouse released\n");
}
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
In SFML 2.1 on c++, is there a sf::Keyboard function that checks if any letter in the alphabet being pressed?
I know how to check if a certain key is pressed, like this:
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Key))
Do I have to type all this:
if(sf::Keyboard::isKeyPressed(sf::Keyboard::A)&&sf::Keyboard::isKeyPressed(sf::Keyboard::B)&&sf::Keyboard::isKeyPressed(sf::Keyboard::C))
...all the way to sf::Keyboard::Z,
Or is there an easier way?
From the SFML website:
Many programmers use the KeyPressed event to get user input, and start to implement crazy algorithms that try to interpret all the possible key combinations to produce correct characters. Don't do that!
sf::Keyboard::isKeyPressed is for checking whether a key is pressed, which is good for things like movement: as long as the player is holding the 'left' key down, we wish to keep moving their character left. A KeyPressed event is for checking which key is pressed at the time it is pressed, which is good for things that happen only once: if the player jumps with the spacebar, we typically don't want them to keep jumping as long as the spacebar is held down.
When it comes to checking entered text, though, neither of these solutions are very good. In the official tutorials, we are advised against using these for checking text input - we have actually been given a different event for exactly this type of situation. The appropriate thing to do is to get the text-based contents of the key via the TextEntered event as described here. Get the text from the event then apply the logic you need to ensure its validity, or (if possible) use the text from the event directly.
Here's a solution with a counter increased every time when a letter key is pressed. window.setKeyRepeatEnabled() must be set to false to avoid getting multiple events while pressing a key:
#include <SFML/Graphics.hpp>
#include <iostream>
int main() {
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML");
int keyCounter = 0;
window.setKeyRepeatEnabled(false);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
else if(event.type == sf::Event::KeyPressed) {
if(event.key.code >= sf::Keyboard::A && event.key.code <= sf::Keyboard::Z) {
keyCounter++;
}
} else if(event.type == sf::Event::KeyReleased) {
if(event.key.code >= sf::Keyboard::A && event.key.code <= sf::Keyboard::Z) {
keyCounter--;
}
} else if(event.type == sf::Event::MouseMoved) {
/* move cursor inside the window to observe how the counter
behaves while pressing the keys */
std::cout << keyCounter << std::endl;
}
}
window.clear();
window.display();
}
return 0;
}
Alternative approach - iterating over enum:
#include <SFML/Graphics.hpp>
#include <iostream>
int main() {
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML");
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
} else if(event.type == sf::Event::MouseButtonPressed) {
/* click inside window to see if there is key pressed */
bool isp = false;
for (int i = sf::Keyboard::A; i <= sf::Keyboard::Z; ++i ) {
if(sf::Keyboard::isKeyPressed(static_cast<sf::Keyboard::Key>(i))) {
isp = true;
break;
}
}
std::cout << isp << std::endl;
}
}
window.clear();
window.display();
}
return 0;
}