Resolving inconsistent SDL2 key press detection? - c++

I am trying to detect when a key is pressed down using the SDL_Event system.
I am using this code to detect the key presses:
void GameLoop::update(SDL_Event eventHandler, Camera &camera){
Vector2 move = Vector2();
if (eventHandler.type == SDL_KEYDOWN){
switch (eventHandler.key.keysym.sym){
case SDLK_w:
move = Vector2(0, -1);
break;
case SDLK_s:
move = Vector2(0, 1);
break;
case SDLK_a:
move = Vector2(-1, 0);
break;
case SDLK_d:
move = Vector2(1, 0);
break;
}
}
camera.translateCamera(move);
}
If I switch out 'WASD' inputs to 'UP DOWN LEFT RIGHT' The system works and if I remove the line:
if (eventHandler.type == SDL_KEYDOWN){
It works due to the SDL_KEYUP type, using 'WASD' or 'UP DOWN LEFT RIGHT'
What is causing this behaviour and how to fix it?

SDL literally checks IF the keyboard is pressed, it just so happens that the program reads faster than the actual press which will result in numerous presses, I'm not sure about your up, down, left, right buttons, maybe they are meant to be read by SDL but their physical connection might not be the same with the letters in the keyboard.
Now if you change KEYDOWN to KEYUP, all it would read is IF it is up, this isn't really good.
What you would want to do is use booleans and ticks
if (eventHandler.type == SDL_KEYDOWN && !clicked){
ticking2 = SDL_GetTicks();
switch (eventHandler.key.keysym.sym){
case SDLK_w:
clicked = true;
break;
case SDLK_s:
clicked = true;
break;
}
}
if (eventHandler.type == SDL_KEYUP && clicked){
ticking = SDL_GetTicks();
switch (eventHandler.key.keysym.sym){
case SDLK_w:
if(ticking-ticking2 >= 3000)
{
move = Vector2(0, -1);
clicked = false;
}
break;
case SDLK_s:
if(ticking-ticking2 >= 3000)
{
move = Vector2(0, 1);
clicked = false;
}
break;
}
}
Upon pressing a button, it gets the time and pushes it into ticking2 but then after upon a click, it turns the bool clicked into true which then makes it stop reading the loop, thus, freezing the value of ticking 2.
Upon releasing a button, it checks if clicked is true so that the actions will only be made IF the button was actually pressed.
It gets the time and pushes it into ticking, ticking-ticking2 should equal to something around 0, I made those to ensure that effects won't be instant since it might cause some glitches.

Related

Some problem with touch events with cef and SDL2

I'm experiencing some problem to getting working touch events with CEF in offscreen rendering using SDL2 in some situation:
Im writing an SDL-CEF application that need to execute some online game into a videolottery. so I start the application from tty using startx and not in DE. Morever, I can't use the mouse but is only possible use touchscreen to interact with the machine.The touch event work pretty well in the most os cases, but with some game the click event is not recognized by the web application.
For pass the correct event to the CEF browser I have written some specialized handler functions that catchs the SDL_Event and converts their to CEF_Event. Fo example for processing tauch events I have a function called handleFingerEvent(SDL_Event, CEF_Browser*). This specialized function is called from a generic handleEvents(SDL_Event, VEF_Browser*) handler function of highter level .
// The Touch Event Hendler function
void handleFingerEvent(SDL_Event &e, CefBrowser* browser) {
CefMouseEvent event;
switch (e.type) {
case SDL_FINGERDOWN:
// W_SIZE and H_SIZE are the dimension of the screen in px
if (e.tfinger.pressure > 0.9) {
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseClickEvent(event,
MBT_LEFT,
false, 1);
}
break;
case SDL_FINGERUP:
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseClickEvent(event, MBT_LEFT, true, 1);
break;
case SDL_FINGERMOTION: {
int scrollX = static_cast<int>(e.tfinger.dx);
int scrollY = static_cast<int>(e.tfinger.dy);
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseWheelEvent(event, scrollX, scrollY);
break;
}
default:
break;
}
}
void handleEvents(SDL_Event &e, CefBrowser *browser) {
switch (e.type) {
// Key events
case SDL_KEYDOWN:
case SDL_KEYUP:
handleKeyEvent(e, browser);
break;
// Window events
case SDL_WINDOWEVENT:
handleWindowEvent(e, browser);
break;
//Mouse events
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEWHEEL:
handleMouseEvent(e, browser);
break;
// Touch events
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
handleFingerEvent(e, browser);
break;
default:
break;
}
}
I call it from a SDL_PollEvent() while loop like this:
browser = CefBrowserHost::CreateBrowserSync(window_info,
browserClient, //My BrowserClient implementation
"https://www.something.sometother",
browserSettings, // The cef setting
nullptr, nullptr);
while(!browserClient->closeAllowed()) {
//Send events to the browser
SDL_Event e;
while(SDL_PollEvent(&e) != 0){
//Quit and window resize
if(e.type == SDL_QUIT) {
browser->GetHost()->CloseBrowser(false);
}
else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
renderHandler->resize(e.window.data1, e.window.data2);
browser->GetHost()->WasResized();
}
else {
handleEvents(e, browser.get());
}
}
// Cef main work loop
cefApp->doCefWork();
//Set background color
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
//Clear window/renderer
SDL_RenderClear(renderer);
renderHandler->render();
//Update screen contents
SDL_RenderPresent(renderer);
}
Now I really don't know why in some game the event isn't caught by the browser. Morever if I open the game in a machine with mouse connected and a DM, and so I use the mouse pointer for interact with the game, it works without any problem. Maybe is there a better approach to send a click event to CefBrowser?

SDL2 and measuring the time a key is pressed

I have written the follow (typical) code snippet for handling key events in SDL2:
#include <SDL.h>
#include <iostream>
using std::cout;
using std::endl;
// Custom key types
typedef enum Keys {
Back,
Reset
} KeyType;
// Structure that holds the type and pressed state of custom keys
typedef struct Button {
KeyType type;
bool pressed;
} Button;
int main(int argc, char ** argv)
{
// Variables
bool quit = false;
SDL_Event event;
Button buttons[] = {
{Back, false},
{Reset, false}
};
// Initialize SDL sub-systems, window, renderer and texture
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * window = SDL_CreateWindow("SDL2 Keyboard/Mouse events",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 640, 0);
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Loop while quit not disabled
while (!quit)
{
// Pool events
while(SDL_PollEvent(&event))
{
// Filter events
switch (event.type)
{
case SDL_QUIT: // Window closed
quit = true;
break;
case SDL_KEYDOWN: // Key pressed
switch (event.key.keysym.sym)
{
case SDLK_ESCAPE:
quit = true;
break;
case SDLK_LEFT:
buttons[Back].pressed = true; // Toggle Back button to pressed
cout << "Back held" << endl;
// TODO Measure elapsed time and artificially toggle the pressed state to false if > 5s
break;
case SDLK_DOWN:
buttons[Reset].pressed = true; // Toggle Reset button to pressed
break;
}
break;
case SDL_KEYUP: // Key released
switch (event.key.keysym.sym)
{
case SDLK_LEFT:
if (buttons[Back].pressed) {
// TODO Measure elapsed time and print "Back emitted" if less than 5s
buttons[Back].pressed = false; // Toggle Back button to unpressed and emit Back event
}
break;
case SDLK_DOWN:
buttons[Reset].pressed = false; // Toggle Reset button to unpressed
cout << "Reset emitted" << endl;
break;
}
break;
case SDL_USEREVENT:
break;
}
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
}
// Cleanup resources
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
// Stop all SDL sub-systems
SDL_Quit();
return 0;
}
I'm trying to figure out a way to measure the time between the moment when buttons[Back].pressed is set to true (even when holding the key down only the exact begin of the "holding" is what I'm interested in) and then to false. The "key released" part is easy since this a single event anyway.
I want to distinguish between a short (single "key pressed" event received or holding the key for less than let's say 5s) and long (more than 5s holding the key down) strokes which is basically mapping two actions to the same key based on the time a key is held by the user.
I can't figure out how to do that. Any ideas? I need to handle the time event in two location (imho):
Given key is released - if elapsed time is less/equal to 5s detect a short key stroke else long key stroke
While given key is in being pressed - if the timeout kicks in, the pressed state needs to be artificially toggled to false in order to prevent a short key stroke to be detected when the user actually releases the key. In this case a long key stroke is detected
I expanded upon #keltar 's suggestion and it works although not that precise (but in my case it's not required.
I added elapsedTime in the Button struct to hold the time information and initialized it to 0:
typedef struct Button {
KeyType type;
bool pressed;
uint32_t elapsedTime;
} Button;
Whenever the given key is pressed down I check if the elapsedTime == 0 and if so I call SDL_GetTicks(). Upon releasing the key I get the currentTime which is just another call of SDL_GetTicks() at that point in time. I calculate the difference between elapsedTime and currenTime and check if a specific time interval has elapsed and then make a decision what function is called.
This solution can be expanded by making the calculation above during every loop iteration and making the exact same check. This makes it possible to check the elapsed time not only upon releasing the given key and it is a more accurate answer to my initial question. However the first solution (without the extra check) works fine. I just had to adapt my scenario a little bit.

SDL: how to get the mouse motion while the left mouse button is down using case statement

I am currently working with SDL2 and am fairly new to it. I am trying to use case statments to get the mouse motion coordinates only while the left mouse button is pressed down.
In the end, I need to be able to click on a object and find how much the mouse is dragged from that selected object.
So far I have been able to get the mouse press and the mouse motion working separately, but not at the same time.
Here is my code for the mouse press event:
void SDL::OnEvent(SDL_Event *_event)
{
Mallet mallet;
switch (_event->type)
{
case SDL_QUIT:
m_running = false;
break;
default:
break;
case SDL_KEYUP:
switch (_event->key.keysym.sym)
{
case SDLK_SPACE:
if(m_playerTurn == 1)
m_playerTurn = 2;
else
m_playerTurn = 1;
std::cout<<"player turn = "<<m_playerTurn<<std::endl;
break;
}
case SDL_MOUSEBUTTONDOWN:
switch(_event->button.button)
{
case SDL_BUTTON_LEFT:
int x = _event->button.x;
int y = _event->button.y;
if(m_playerTurn == 1)
{
bool collision = checkCollision(x, y, m_player1->getTeamMallets(), mallet);
if(collision)
std::cout<<"collision with P1"<<std::endl;
}
if(m_playerTurn == 2)
{
bool collision = checkCollision(x, y, m_player2->getTeamMallets(), mallet);
if(collision)
std::cout<<"collision with P2"<<std::endl;
}
break;
}
}
}
Can anyone help.
Many thanks in advance.
Will
on SDL_MOUSEBUTTONDOWN set variable click = true ans save x,y coordinates,
on SDL_MOUSEMOTION check if click == true and update x,y coordinates,
on SDL_MOUSEBUTTONUP set click = false and calculate distance.
http://lazyfoo.net/tutorials/SDL/17_mouse_events/index.php

How do I display the motion of a sprite after taking a set of all the inputs using SDL2?

I'm a beginner in c++ and games programming and am trying to create a game which accepts a series of keyboard inputs from the user and on pressing enter should display the movements of a sprite in a sequence based on the keys pressed.
I am using SDL 2 and have been able to move my sprite as soon as each key is pressed but I would like to wait for all the inputs and then show all the movements successively once the user presses the ENTER key. So far I came up with the following code shown below.
(I have used a counter variable to save the number of times any arrow key is pressed and a sprite class which stores its position and velocity on screen).
SDL_Event e;
bool quit = false;
bool startSprite = false;
Sprite sprite1;
while (!quit)
{
while (SDL_PollEvent(&e) && startSprite== false)
{
if (e.type == SDL_QUIT)
{
quit = true;
}
if (e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
{
//true if ENTER key is pressed
if (e.key.keysym.sym == SDLK_RETURN)
{
startSprite = true;
}
//Handle input for the sprite
sprite1.handleEvent(e);
}
}
//if the ENTER key is pressed
if(startSprite==true)
{
//Clear the window
SDL_RenderClear(renderer);
drawTiles(xTiles, yTiles, background, renderer);
//if the counter > 0, loop through and display each movement until the
//total count number is reached
if(count>0)
{
for(int i=0;i<count;i++)
{
sprite1.move();
//display the image
drawImages(image, image2, image3, sprite1, renderer);
//Update the screen
SDL_RenderPresent(renderer);
}
count=0;
}
//startSprite==false;
}
The code for handling the sprite event
void Sprite::handleEvent( SDL_Event& e )
{
//If a key was pressed
if( e.type == SDL_KEYDOWN ) //&& e.key.repeat == 0
{
//Adjust the velocity
switch( e.key.keysym.sym )
{
case SDLK_UP: mVelY -= SPRITE_VEL; break;
case SDLK_DOWN: mVelY += SPRITE_VEL; break;
case SDLK_LEFT: mVelX -= SPRITE_VEL; break;
case SDLK_RIGHT: mVelX += SPRITE_VEL; break;
case SDLK_RETURN: startSprite=true; break;
}
count++;
}
//If a key was released
else if( e.type == SDL_KEYUP ) //&& e.key.repeat == 0
{
//Adjust the velocity
switch( e.key.keysym.sym )
{
case SDLK_UP: mVelY += SPRITE_VEL; break;
case SDLK_DOWN: mVelY -= SPRITE_VEL; break;
case SDLK_LEFT: mVelX += SPRITE_VEL; break;
case SDLK_RIGHT: mVelX -= SPRITE_VEL; break;
case SDLK_RETURN: startSprite=true; break;
}
}
}
The move function of the sprite class
void Sprite::move()
{
//Move the sprite left or right
mPosX += mVelX;
//If the sprite went too far to the left or right
if( ( mPosX < 0 ) || ( mPosX + SPRITE_WIDTH > SCREEN_WIDTH ) )
{
//Move back
mPosX -= mVelX;
}
//Move the sprite up or down
mPosY += mVelY;
//If the sprite went too far up or down
if( ( mPosY < 0 ) || ( mPosY + SPRITE_HEIGHT > SCREEN_HEIGHT ) )
{
//Move back
mPosY -= mVelY;
}
}
This doesn't work however and my code never leaves the while loop because of startSprite not setting to true for some reason.
If I remove the condition from the while loop statement, then, after pressing the arrow keys in order and then pressing enter nothing happens
and then if I press an arrow key to move the sprite, the sprite moves with each key press. I am completely confused and have been at this all day. I can't seem to figure out where I am going wrong.
I was going to write this in a comment, but it became too long. It might not be the answer you're looking for...
If you think of the logic in your code, it seems what will happen when you press a key that is not enter is that the handleEvent will run and the velocity will be adjusted, and when you finally press enter the move loop takes over and starts moving the sprite, but... if you pressed up once and down once, the velocity will be unchanged as the moves counteract each other, and the move will be two times nothing.
What you need to do is to defer the handling of events until you press return altogether, so that you start processing the events only after you hit enter.
If you add the events to a std::queue<SDL_Event> (using queue.push_back(e)) you can retrieve them in a loop after you hit enter and for each event call the handleEvent and move functions, possibly with a small delay if you actually want to see the moves performed stepwise.
it could look something like this:
while (!queue.empty())
{
SDL_Event e = queue.front();
sprite1.handleEvent(e);
SDL_RenderClear(renderer);
drawTiles(xTiles, yTiles, background, renderer);
sprite1.move();
drawImages(image, image2, image3, sprite1, renderer);
SDL_Delay(200); // possible delay.
SDL_RenderPresent(renderer);
queue.pop();
}

event check only when clicked on a button

I have a simple test script, which it meant to change a boolean when the user clicks within the dimensions of the button but it is not working.
I approached it like so:
while( SDL_PollEvent( &event ) ) {
switch( event.type ){
case SDL_QUIT: quit = true; break;
case SDL_MOUSEMOTION: mouseX = event.motion.x; mouseY = event.motion.y; break;
case SDL_MOUSEBUTTONDOWN: click = true;
}
}
Button btn_settings(btn_x,btn_y);
if(btn_settings.IsIn(mouseX,mouseY)){
btn_settings.RenderImg(menu,screen,"button_on.png","Settings");
if(click){
quit = true;
}
} else {
btn_settings.RenderImg(menu,screen,"button.png","Settings");
}
The problem is if i click any where then click equals true, then if the mouse is over the quit button it exits even if the button wasn't pressed when over the button.
I'm confused how i can make it work properly.
Try handling your "click" event on mouse button up instead. I've never used SDL but I suspect there is a SDL_MOUSEBUTTONUP defined. Otherwise you don't know if they want to perform a drag operation or if they moved the mouse someplace else before letting go of the mouse button.