I've been trying to animate in a C program using Xlib and I wanna do something when an event occurs, otherwise I wanna keep animating. Here's an example code snippet of what I am doing currently:
while( 1 )
{
// If an event occurs, stop and do whatever is needed.
// If no event occurs, skip this if statement.
if ( XEventsQueued( display, QueuedAlready ) > 0 )
{
XNextEvent( display, &event )
switch ( event.type )
{
// Don't do anything
case Expose:
while ( event.xexpose.count != 0 )
break;
// Do something, when a button is pressed
case ButtonPress:
...
break;
// Do something, when a key is pressed
case KeyPress:
...
break;
}
}
animate(); // Do animation step i.e. change any drawings...
repaint(); // Paint again with the new changes from animation...
}
So basically, I wanna keep looping if the user hasn't clicked the mouse OR pressed a key in the keyboard yet. When the user presses a key OR clicks the mouse, I wanna stop and do a specific action. The problem in my above code is that, it doesnt stop whenever I do an action. If I remove the if statement, the animation blocks until an event occurs, however I do not want this. It's a simple problem, but I'm kinda new to Xlib/animations so any help would be highly appreciated. Thanks.
Use the file descriptor returned by ConnectionNumber(display) with select() and use the timeout argument. If select() returns 0, then draw some more frames. Remember to call XSync() before you select() so that the X server gets your update.
int fd,r;
struct timeval tv;
FD_SET rfds;
fd=ConnectionNumber(display);
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
memset(&tv,0,sizeof(tv));
tv.tv_usec = 100000; /* delay in microseconds */
r=select(fd+1,&rfds,0,0,&tv);
if(r == 0) { /* draw frame */ }
else if (r < 0) { /* error; try again if errno=EINTR */ }
else { /* pull events out */ }
Related
I'm writing Conways game of life in C with SDL. The game logic and everything works fine but I want to have it when you press p for example that the game automatically updates until p is pressed again and the loop stops. Somehow it only repeats once and doesn't even register the second time p is pressed.
else if (e.key.keysym.sym == SDLK_p){
bool stop = false;
while (!stop){
nextEpoch();
updateGame(window, renderer, r);
msleep(500);
if (e.type == SDL_KEYDOWN){
if (e.key.keysym.sym == SDLK_p){
stop = true;
printf("s\n");
}
}
}
}
It doesn't register that p is pressed when it in the while-loop.
Here's the full code: https://gist.github.com/PhilippRados/2df8760cc55822c2eac62addafb24403
As already pointed out by someone else in the comments section, you are not updating e in the inner loop. If you want to update e with a new event, you must call SDL_PollEvent( &e ) again to fill it with a new event.
In your linked code, you seem to be attempting to implement a new state of your program outside the main event loop, which represents the running state of the program, whereas the main event loop represents the paused state of the program. That way, you effectively have two separate event loops, one for each state of the program. While it is possible to make your program work this way, I don't recommend it.
For example, the way you have designed your program, your program won't respond to SDL_QUIT events in the running state. It will only do so in the paused state.
Therefore, it would make more sense to have a single event loop for both the running and the paused states of your program.
I don't recommend that you call usleep or SDL_Delay for waiting until it is time to render the next frame, as your program will not be responding to user input during this time. Especially since you have a very low frame rate of 2 frames per second, this means that it will take up to half a second for your program to respond to user input (for example if the user resizes the window or attempts to close it). Instead, I recommend that you set up a timer using SDL_AddTimer. You can program the timer callback function to give you a SDL_USEREVENT event twice per second. That way, when you receive this event, you will know that it is time to update the game and render the next frame. While waiting for this event, your program will still be able to respond to other events.
Note that in order to use SDL timers, you must initialize the corresponding subsystem using the SDL_INIT_TIMER flag when calling SDL_Init. Strangely, your linked code does not seem to call SDL_Init at all, so I am surprised that your code works at all. According to the documentation, you should call SDL_Init like this:
SDL_Init( SDL_INIT_TIMER | SDL_INIT_VIDEO );
Also, calling SDL_PollEvent in a loop seems like a big waste of CPU resources, as your CPU usage will likely be 100% although you are effectively doing nothing most of the time. I recommend that you call SDL_WaitEvent instead, so that the thread will sleep until it receives a new event to respond to.
Another thing worth noting is that when handling an SDL_MOUSEBUTTONDOWN event, it does not seem appropriate to use the result of SDL_GetMouseState to determine the coordinates of the mouse click, as that function will return the current mouse coordinates and not the coordinates at the time of the click. Therefore, it would be more appropriate to read these coordinates from the SDL_MouseButtonEvent structure.
Here is an example of how you could rewrite your event loop to use SDL_WaitEvent instead of SDL_PollEvent and to support both a running and a paused state in the main event loop.
Uint32 my_callbackfunc( Uint32 interval, void *param )
{
SDL_Event e;
e.user.type = SDL_USEREVENT;
e.user.code = 0;
e.user.data1 = NULL;
e.user.data2 = NULL;
SDL_PushEvent( &e );
return interval;
}
int main (int argc, char** argv)
{
[...]
//set timer to trigger twice per second
SDL_TimerID timer = SDL_AddTimer( 500, my_callbackfunc, NULL );
if ( timer == 0 ) {
//TODO: handle error
return EXIT_FAILURE;
}
//start game in a paused state
bool paused = true;
while ( SDL_WaitEvent( &e ) ) {
switch ( e.type ) {
case SDL_QUIT:
goto quit_game;
case SDL_WINDOWEVENT:
//rerender in case of window state change
updateGame( window, renderer, r );
break;
case SDL_USEREVENT:
if ( !paused ) {
nextEpoch();
updateGame(window, renderer, r);
}
break;
case SDL_MOUSEBUTTONDOWN:
mouseX = getNearestMultiple( e.button.x ) / RECT_SIZE;
mouseY = getNearestMultiple( e.button.y) / RECT_SIZE;
if ( Field[mouseX][mouseY] ) {
//Deactivate cell
Field[mouseX][mouseY] = false;
updateGame(window,renderer,r);
}
else {
//activate cell at position x,y
Field[mouseX][mouseY] = true;
updateGame(window,renderer,r);
}
break;
case SDL_KEYDOWN:
switch ( e.key.keysym.sym ) {
case SDLK_SPACE:
if ( paused ) {
nextEpoch();
updateGame(window, renderer, r);
}
break;
case SDLK_r:
memset(Field,0,sizeof(Field[0][0]) * WIDTH * HEIGHT);
memset(nextState,0,sizeof(nextState[0][0]) * WIDTH * HEIGHT);
updateGame(window,renderer, r);
break;
case SDLK_p:
paused = !paused;
}
}
}
quit_game:
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
I am working with a micro controller that has a button A. When I press this button and hold it for 2 seconds, its value becomes 0 and the color turns either blue or green, when I release, its value goes back to 1 but the color stays the same unless it was clicked again and the color changes. The problem is, it shouldn't take someone 2 whole seconds to have to change the led light. What can I do to make the value (either 0 or 1) be read faster?
Here is a snippet of the code in the while loop.
// here are the states for reference. Everything is either a 0 or a 1
const int BUTTON_PRESSED = 0;
const int BUTTON_UNPRESSED = 1;
const int GREEN_LED = 0;
const int BLUE_LED = 1;
const struct timespec sleepTime = { 1, 0 };
while (true) {
Value_Type value;
// this function get the input of button a when pressed
GetValue(button_A_fd, &value);
Log_Debug(
"Button value (%d)\n", value);
// Processing the button.
//Turns LED ON; Button not pressed down
if (value == BUTTON_UNPRESSED) {
last_button_state = BUTTON_UNPRESSED;
} else {
// if last button state is 1 then now it is being pressed
if (last_button_state == BUTTON_UNPRESSED) {
// Flip LEDs
if (active_led == BLUE_LED) {
active_led = GREEN_LED;
}
else if (active_led == GREEN_LED) {
active_led = BLUE_LED;
}
last_button_state = BUTTON_PRESSED;
// sets the pointer to the 0 bit of the file to write
lseek(fd_storage, 0, SEEK_SET);
// write current active led to mutable storage and save
write(fd_storage, &active_led, sizeof(active_led));
}
}
// Blinking the active LED.
// reading input only when pressed and turn off other led
if (active_led == GREEN_LED) {
// turn off blue led, then turn on green
SetValue(blue_led_fd, Value_High);
SetValue(green_led_fd, Value_Low);
nanosleep(&sleepTime, NULL);
SetValue(green_led_fd, Value_High);
nanosleep(&sleepTime, NULL);
}
else if (active_led == BLUE_LED) {
// turn off green led, then turn on blue
SetValue(green_led_fd, Value_High);
SetValue(blue_led_fd, Value_Low);
nanosleep(&sleepTime, NULL);
SetValue(blue_led_fd, Value_High);
nanosleep(&sleepTime, NULL);
}
}
}
I tried to put GetValue() in several parts of the code to see if it could maybe get a value faster but it did not work. How can I move from here? I hope I shared enough code to understand the problem. Thank you.
Looks like your code reads the button which is fast, and then immediately sets outputs and sleeps 1 second, sets an output and sleeps another second before going up to check if the button is pressed again.
You should restructure the code to check the state of the button more frequently where you're going to sleep now.
Sleep for shorter periods and check to see if the button is pressed in a loop until your total sleep time has been met or the button state changes.
Upon further inspection, I found these:
The above picture is linked from here , it is the spec sheet for your microcontroller.
You want to look at the "Board Pin Map section" from this link to match it with the spec sheet of the chip itself
how can I move an object in sdl with press one key from a keyboard, then the object move automatically?
for example, I need to move a bullet from a tank in the game (alter tank), so I press a key then the bullet should move automatically.
Generally, if you want to make something like this, what you'd do is use a game loop. In broad strokes, you define functions that check what things need to be updated (the player, enemies, bullets, items, etc), and then update each one.
In this case, you want, for example, an array of bullets. You animate each one every frame. When you press a button, you add a new bullet to the array. Once that bullet is in the array, it will 'move by itself'. If that makes sense.
Just use the SDL events: https://www.libsdl.org/release/SDL-1.2.15/docs/html/guideinputkeyboard.html
Wait for the required keyboard event and call the function that moves the bullet.
This example is from the doc and is slightly adapted.
SDL_Event event;
/* Poll for events. SDL_PollEvent() returns 0 when there are no */
/* more events on the event queue, our while loop will exit when */
/* that occurs. */
while( SDL_PollEvent( &event ) ){
/* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */
switch( event.type ){
case SDL_KEYDOWN:
printf( "Key press detected\n" );
break;
case SDL_KEYUP:
printf( "Key release detected\n" );
move_bullet(); // create your handler here
break;
default:
break;
}
}
I'm programming a robot, and unfortunately in its autonomous mode I'm having some issues.
I need to set an integer to 1 when a button is pressed, but in order for the program to recognize the button, it must be in a while loop. As you can imagine, the program ends up in an infinite loop and the integer values end up somewhere near 4,000.
task autonomous()
{
while(true)
{
if(SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
wait1Msec(0350);
}
}
}
I've managed to get the value by using a wait, but I do NOT want to do this. Is there any other way I can approach this?
assuming that the SensorValue comes from a physical component that is asynchronous to the while loop, and is a push button (i.e. not a toggle button)
task autonomous()
{
while(true)
{
// check whether
if(current_time >= next_detect_time && SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
// no waiting here
next_detect_time = current_time + 0350;
}
// carry on to other tasks
if(enemy_is_near)
{
fight();
}
// current_time
current_time = built_in_now()
}
}
Get the current time either by some built-in function or incrementing an integer and wrap around once reach max value.
Or if you are in another situation:
task autonomous()
{
while(true)
{
// check whether the flag allows incrementing
if(should_detect && SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
// no waiting here
should_detect = false;
}
// carry on to other tasks
if(enemy_is_near)
{
if(fight() == LOSING)
should_detect = true;
}
}
}
Try remembering the current position of the button, and only take action when its state changes from off to on.
Depending on the hardware, you might also get a signal as though it flipped back and forth several times in a millisecond. If that's an issue, you might want to also store the timestamp of the last time the button was activated, and then ignore repeat events during a short window after that.
You could connect the button to an interrupt and then make the necessary change in the interrupt handler.
This might not be the best approach, but it will be the simplest.
From The Vex Robotics catalogue :
(12) Fast digital I/O ports which can be used as interrupts
So, most probably which ever micro-controller of Vex you are using will support Interrupts.
Your question is a bit vague
I m not sure why u need this variable to increment and how things exactly work...but i ll make a try.Explain a bit more how things work for the robot to move...and we will be able to help more.
task autonomous()
{
int buttonPressed=0;
while(true)
{
if(SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect +1;
buttonPressed=1;
}
else{
buttonPressed = 0;
}
//use your variables here
if( buttonPressed == 1){
//Move robot front a little
}
}
}
The general idea is :
First you detect all buttons pressed and then you do things according to them
All these go in your while loop...that will(and should) run forever(at least as long as your robot is alive :) )
Hope this helps!
I write a client for a console game, 1vs1. In the game one player have to catch the other, and every player is rappresented with a char, printed on the console. I use the mvaddch() to delete the old position, and to print the new position on the console.
My code generate 2 process:
Process A: It get the input from the keyboard and update the position on the screen;
Process B: it get the input from the server and update the position of the enemy on the screen;
My problem is the old position of the enemy is not deleted (overwriting with ' '), and so process B generates a snake of charactes on the screen. Process A work good.
initscr();
noecho();
curs_set(0);
//process A is created now
switch ( pid = fork() ) {
case -1: perror("fork() fallita"); exit(0);
case 0: {char c; struct pos old_position = {welcome_position.c, welcome_position.x, welcome_position.y};
struct pos position = {welcome_position.c, welcome_position.x, welcome_position.y};
mvaddch(position.y, position.x, position.c);
while (1) {
switch(c=getch()) {
case SU: if(position.y>0) { position.y-=1; } break;
case GIU: if(position.y<MAXY-1){ position.y+=1; } break;
case SINISTRA: if(position.x>0){ position.x-=1; } break;
case DESTRA: if(position.x<MAXX-1){ position.x+=1; } break;
default: break; }
if ((position.x != old_position.x) || (position.y != old_position.y)) {
send(sock, &position, sizeof(struct pos), 0);
mvaddch(old_position.y, old_position.x, ' ');
mvaddch(position.y, position.x, position.c);
refresh();
old_position.x = position.x;
old_position.y = position.y; }} }
default: break ; }
// Process B is here
struct pos position;
struct pos old_position={' ', -1,-1};
while (1) {
while ( recv(sock, &position, sizeof(struct pos), 0) < 1 )
mvaddch(old_position.y, old_position.x, ' '); // THE PROBLEM
mvaddch(position.y, position.x, position.c); // Works => snake
refresh();
old_position.x = position.x;
old_position.y = position.y;}
endwin();
kill(pid);
printf("\n-----------------------------\n");
}
If you don't want the entire trail showing, you have to keep a record of the previous position of each character (player) and arrange to write a blank at the old position and the correct mark at the new position. If you're feeling fancy, you might use a coloured blank, one colour for each player, so you can see where each has been, even though the current positions are marked differently.
Unfortunately, without the colouration mentioned, that looks like what you're doing.
You should make sure you don't use the -1 coordinates; mvaddch() is probably not error checked and will go trampling out of bounds, doing who knows what damage. Don't risk it. (Consider using 0, 0 as the old position; it won't matter if you write a blank over a blank. The only thing that matters is that the other player is not where you write the blank.)
Note that it is crucial that only one process is doing the drawing. If you have two processes attempting to do so, you will lose one or the other images some of the time. This is one reason reason why it is hard to add a clock, say, to the top right corner of a terminal screen. You do seem to have two processes trying to write to the screen.
Style-wise, you need to use more functions. That is, the different processes should each have their code in a separate function, so that it is easier to see what they are doing. Stacking close braces } three deep on a single line is not good style either.