I'm having a little problem catching a key released event to stop my character from walking in my game..
i'm trying to do this:
switch (xev.type)
{
case Expose:
{
XGetWindowAttributes(dpy, win, &gwa);
glViewport(0, 0, gwa.width, gwa.height);
}
break;
case KeyPress:
{
int key = XLookupKeysym(&xev.xkey, 0);
if (key == XK_Escape)
{
glXMakeCurrent(dpy, None, NULL);
glXDestroyContext(dpy, glc);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
running = false;
return 0;
}
else
{
input->setKey(key, true);
}
}
break;
case KeyRelease:
{
unsigned short is_retriggered = 0;
if (XEventsQueued(dpy, QueuedAfterReading))
{
XEvent nev;
XPeekEvent(dpy, &nev);
if (nev.type == KeyPress && nev.xkey.time
== xev.xkey.time && nev.xkey.keycode
== xev.xkey.keycode)
{
// delete retriggered KeyPress event
XNextEvent(dpy, &xev);
is_retriggered = 1;
}
}
if (!is_retriggered)
input->setKey(XLookupKeysym(&xev.xkey, 0), false);
}
break;
}
But I only get the re-triggered key release events, which I don't want. (even though a release/re-press would have the same result, but in the future it might give problems)
When i physically release the key, no event is caught.
oh, and input->setKey() basically sets a bool to true (or false) in a std::map, nothing special
This is a common gotcha. If you don't register for specific events (or all), you will not be notified.
All of us, doesn't matter the experience, will fall in this one day... :)
Some usefull links:
XSelectInput #Xorg
XSelectInput #tronche
Anatomy of the most basic Xlib program
Registering the KeyReleaseMask solved the problem.
XSelectInput(dis, win, KeyPressMask | KeyReleaseMask);
Related
I have problems deactivating fullscreen mode with my program. Entering fullscreen happens correctly, but trying to go back to windowed mode doesn't work, the only effect is that the cursor gets shown again.
Here's the MCVE/SSCCE that reproduces the issue for me:
void ToggleFullscreen(SDL_Window* Window) {
Uint32 FullscreenFlag = SDL_WINDOW_FULLSCREEN;
bool IsFullscreen = SDL_GetWindowFlags(Window) & FullscreenFlag;
SDL_SetWindowFullscreen(Window, IsFullscreen ? 0 : FullscreenFlag);
SDL_ShowCursor(IsFullscreen);
}
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* Window = SDL_CreateWindow("",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
bool Exit = false;
for (SDL_Event Event; !Exit;) {
SDL_WaitEvent(&Event);
if (Event.type == SDL_KEYDOWN) {
switch (Event.key.keysym.sym) {
case SDLK_f: ToggleFullscreen(Window); break;
case SDLK_q: Exit = true; break;
}
}
}
SDL_DestroyWindow(Window);
SDL_Quit();
}
SDL_SetWindowFullscreen returns 0, as if the operation was successful. What am I doing wrong? (I'm using SDL 2.0.3 on OS X 10.10.3.)
It looks like a known issue. Hopefully the SDL developers will fix it. I found the following bug report.
https://github.com/libsdl-org/SDL/issues/1428
Even now there still appears to be a problem with SDL_SetWindowFullscreen. I tried to add fullscreen functionality to my video player with this function. However, it would randomly crash when transitioning between fullscreen and windowed mode.
I found a temporary work around that appears to be working correctly for now.
SDL_DisplayMode dm;
if (SDL_GetDesktopDisplayMode(0, &dm))
{
printf("Error getting desktop display mode\n");
return -1;
}
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYUP:
switch (event.key.keysym.sym)
{
case SDLK_f:
SDL_RestoreWindow(screen); //Incase it's maximized...
SDL_SetWindowSize(screen, dm.w, dm.h + 10);
SDL_SetWindowPosition(screen, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
break;
}
}
It's basically "fake" fullscreen. It resizes the window so the client area covers the whole screen and the minimize, maximize, exit buttons are off screen.
Hope this helps.
I know this may be 5 years after date, but I was facing the same problem, and I found your code way more complex then need be. So I just wanted to add for however comes here:
//somewhere outside the loop
bool isFullScreen = true;
void toggleFullScreen(SDL_Window* window, bool currentState)
{
isFullScreen = !currentState;
SDL_SetWindowFullscreen(window, !currentState);
SDL_ShowCursor(currentState);
}
just place the isFullscreen at the currenState and you have a good toggle with a keypress event.
This works fine for me in SDL2(pressing f toggles it on/off and q quits):
SDL_Window* window = SDL_CreateWindow("Fullscreen demo", 0, 0, 640, 480, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Event event;
bool fullScreen = false;
SDL_bool running = SDL_TRUE;
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = SDL_FALSE;
break;
case SDL_KEYDOWN:
if(event.key.keysym.scancode == SDL_SCANCODE_F){
fullScreen = !fullScreen;
if(fullScreen){
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
}
else{
SDL_SetWindowFullscreen(window, 0);
}
}
if(event.key.keysym.scancode == SDL_SCANCODE_Q){
running = SDL_FALSE;
}
break;
default: break;
}
}
// drawing stuff here...
// swap and display buffer
SDL_RenderPresent(renderer);
}
As the manual says, the function SDL_PollEvent "Returns 1 if there is a pending event or 0 if there are none available." , that's why we use the test SDL_PollEvent(&e)!=0 (where e is a SDL_Event).
But, what about use this test: !SDL_PollEvent(&e)? It should work,but apparently it cause some problem.
Here an example of code:
#include <SDL2/SDL.h>
#include <stdio.h>
int main(int argc, char* args[]){
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Hello",SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 100, 100, SDL_WINDOW_SHOWN);
SDL_Event e;
int quit=0;
while(!quit){
//Here the test
while (!SDL_PollEvent(&e)){
if (e.type==SDL_QUIT)
quit=1;
else if ( e.type == SDL_KEYDOWN )
printf( "Hello\n" );
}
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
What this code should do is open a new window and print "Hello" in the console every time a key is pressed. This code works fine whit the test SDL_PollEvent(&e)!=0 but it doesn't read the SDL_KEYDOWN event when I use the test !SDL_PollEvent(&e) (but it DOES enter in the while and process the SDL_QUIT event without any problem).
Why this behaviour?
while (!SDL_PollEvent(&e))
needs to be:
while (SDL_PollEvent(&e))
if it should be the same as SDL_PollEvent(&e) != 0
because !SDL_PollEvent(&e) is the same as calling while(0)
(1 != 0) is true, but (!1) is false.
You probably should use SDL_WaitEvent instead anyway.
Addendum
One may think it's more clear to do something like this, since it ensures that user input is reacted upon. However, this is currently an OS dependent and possibly crazy CPU hog and will max out your CPU for this thread (on Windows). So you may think that you can just put a 1 second wait there, but then your window will become completely unresponsive or very laggy at best...
If you can live with a little CPU overhead for doing nothing, then you can compromise with using an 20-100 [ms] delay.
...
// Set the event handler...
...
bool isRunning = true;
while (isRunning) {
// Do the main thing here...
...
while(SDL_PollEvent(&windowEvent)) {
switch (windowEvent.type) {
case SDL_QUIT: isRunning = false;
case SDL_KEYDOWN: isRunning = false;
case SDL_MOUSEBUTTONDOWN: isRunning = false;
break;
}
SDL_Delay(100); // Wrong!
}
}
// End the program
So clearly this is not the correct way either. We need to use some other mechanism. Looking at the SDL repo, we find a long but very relevant discussion for the open issue:
SDL_WaitEvent causes high cpu usage
To Fix the issue
Make sure you put the delay in the outer loop, before the SDL_PollEvent().
// main loop
...
while(SDL_PollEvent(&windowEvent)) {
switch (windowEvent.type) {
case SDL_QUIT: isRunning = false;
case SDL_KEYDOWN: isRunning = false;
case SDL_MOUSEBUTTONDOWN: isRunning = false;
break;
}
}
SDL_Delay(100); // Right!
}
What is a good way of doing SDL event handling?
You usually have the:
while (SDL_PollEvent(&event)) {
//Handles all the events when in the menu screen...
eventsMenu(event);
}
Problem is, when you get going with a game, there's usually a mess of a list of controls that you can do, like up and down detetion for many different keys. And I'm wondering weither the method I'm using is efficient and clean. Or if I should approach it diffrently...
I have a function pointer in the mainLoop, that can be assigned to quickly change how the events will be handled. (Say I swap to another function, and it will use those events)
However, I think the list of events get messy anyways, so I tried adding regions to break it up. Is that a good idea? And yeah, just want some input if I'm on the right path to a readable code.
void Window::eventsMenu(SDL_Event event) {
switch (event.type) {
#pragma region "Button Down"
case SDL_MOUSEBUTTONDOWN: {
//printf("Mouse button down!\n");
glClearColor(0.1, 0.1, 0.1, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = true;
}
break;
}
#pragma endregion;
#pragma region "Button Up"
case SDL_MOUSEBUTTONUP: {
//printf("Mouse button up!\n");
glClearColor(0, 0, 0, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = false;
}
break;
}
#pragma endregion;
#pragma region "Mouse Motion"
case SDL_MOUSEMOTION: {
//printf("Mouse moved!\n");
if (mouseButtonLeft) {
rotX += event.motion.xrel;
rotY += event.motion.yrel;
}
break;
}
#pragma endregion;
#pragma region "Mouse Wheel"
case SDL_MOUSEWHEEL: {
if (event.wheel.y != 0) {
musicVolume += ((event.wheel.y > 0) ? 1 : -1);
if (musicVolume > 100) {
musicVolume = 100;
}
else if (musicVolume < 0) {
musicVolume = 0;
}
Mix_VolumeMusic(musicVolume);
printf("Volume: %i%c\n", musicVolume, '%');
}
if (event.wheel.y > 0) {
//printf("Scroll forward!\n");
}
else {
//printf("Scroll backward!\n");
}
break;
}
#pragma endregion;
#pragma region "Key Down"
case SDL_KEYDOWN: {
printf("Button [%s] pressed\n", SDL_GetKeyName(event.key.keysym.sym));
switch (event.key.keysym.sym) {
case SDLK_1: {
Mix_PlayChannel(-1, sound1, 0);
break;
}
case SDLK_2: {
Mix_PlayChannel(-1, sound2, 0);
break;
}
}
break;
}
#pragma endregion;
case SDL_QUIT: {
running = false;
}
}
}
Two suggestions :
Remove the braces ( { and } ) around the case labels. You don't need them unless you need a new stack.
My second suggestion is to split things into function. Even if it will only be called from within switch. Putting things into several functions helps makes the code easier to read and understand.
So for instance :
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
HandeMouseButton( event );
break;
void HandeMouseButton( const SDL_Event &event )
{
if ( event.type == MOUSEBUTTONDOWN )
{
glClearColor(0.1, 0.1, 0.1, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = true;
}
else if ( event.type == MOUSEBUTTONUP )
glClearColor(0, 0, 0, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = false;
}
}
}
And generally ( slightly opinion based ) ; if you need to use #pragma once to make the code readable, it can ( and should ) be split into more function
I've got a little Problem with SDL:
When starting my Program (on Archlinux 64bit), SDL_event gives me some SDL_KEYDOWN events. The annoying thing with that is, that my Program Quits, when pressing ESCAPE.
Thats the Code:
void Input::update() {
[...]
while(SDL_PollEvent(event)) {
if (event != NULL) {
handleInput();
}
}
// SDL_PumpEvents(); Commented out, because of jrok's suggestion.
SDL_GetMouseState(&mousex, &mousey);
}
And:
void Input::handleInput() {
switch(event->type) {
case SDL_KEYDOWN:
setKey(event->key, true);
break;
case SDL_KEYUP:
setKey(event->key, false);
break;
case SDL_QUIT:
setQuit();
break;
default:
break;
}
}
setKey() sets an Element of an bool-Array to the given value (true/false).
Also, its not only the Escape-key. Often the other keys are pressed too.
Okey, sorry. SDL has not got any bugs, and the code, i have shown to you is right.
The problem was, that I didn't initialize my boolean Array. So there were Random values left...
That helped:
for (int i = 0; i < MAX_VAL; i++) {
keys[i] = false;
}
Take a look at this piece of code here:
void game::startLoop()
{
while(QUIT == false)
{
getRoomUpdate();
applySurface(-15, 280, zombie_lefthand, buffer);
applySurface(455, 280, zombie_righthand, buffer);
SDL_Flip(buffer);
while(SDL_PollEvent(&gameEvent))
{
if(gameEvent.type == SDL_QUIT)
{
QUIT = true;
}
}
while(SDL_WaitEvent(&keyEvent))
{
switch(keyEvent.type)
{
case SDL_KEYDOWN:
switch(keyEvent.key.keysym.sym)
{
//blahkeypress
}
}
}
}
}
I'm trying to figure out how to allow SDL_QUIT to work while we're waiting for a keypress. Is there a way to do this or do you guys have a better idea?
I'm a bit of a newbie so please be specific. :D
The name keyEvent is misleading. SDL_WaitEvent will wait for any sort of event, including QUIT.
SDL_Event event;
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT:
quit = true;
break;
/* cases for keyboard events, etc. */
}
Minimal changes:
You could add if (QUIT) break; after the inner while loop that sets QUIT.
Or, you could move the outer while loop to a separate function and add a return; after QUIT = true;.
Better changes:
Refactor your code similar to many examples available on the web (at sourceforge, or at molly rocket, or just google it).