Tille - I can see you, read those man pages!   Tille's Site

Catching user input

Users don't like it when they have to stop a program by putting it in the background and then running kill -9 %1, and it is not clean either. So here is a better version of the circle program, which can be closed by clicking the X in the window that it uses. This program gives more meaningful comments, and makes more use of functions to draw all the circles, so that we have a nice short main().

/* Program name: optimized-circles1.c */
/* SDL screen test */

# include <SDL.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <math.h>
# include <time.h>

/* This is a function that draws a pixel in the appropriate colour depth. */
void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B)
{
  /* Don't fail on attempts to draw outside the screen. */
  if (( x >= 640) || (x < 0)) return;
  if (( y >= 480) || (y < 0)) return;

    Uint32 color = SDL_MapRGB(screen->format, R, G, B);
 
    if ( SDL_MUSTLOCK(screen) ) {
        if ( SDL_LockSurface(screen) < 0 ) {
            return;
        }
    }

    switch (screen->format->BytesPerPixel) {
        case 1: { /* Assuming 8-bpp */
            Uint8 *bufp;
 
            bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
            *bufp = color;
        }
        break;
 
        case 2: { /* Probably 15-bpp or 16-bpp */
            Uint16 *bufp;
 
            bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;
            *bufp = color;
        }
        break;
 
        case 3: { /* Slow 24-bpp mode, usually not used */
            Uint8 *bufp;
 
            bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;
            if(SDL_BYTEORDER == SDL_LIL_ENDIAN) {
                bufp[0] = color;
                bufp[1] = color >> 8;
                bufp[2] = color >> 16;
            } else {
                bufp[2] = color;
                bufp[1] = color >> 8;
                bufp[0] = color >> 16;
            }
        }
        break;
 
        case 4: { /* Probably 32-bpp */
            Uint32 *bufp;
 
            bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;
            *bufp = color;
        }
        break;
    }
    if ( SDL_MUSTLOCK(screen) ) {
        SDL_UnlockSurface(screen);
    }
    SDL_UpdateRect(screen, x, y, 1, 1);

}

/* Draw a circle with specified color, size and center.  Uses DrawPixel.*/
void DrawCircle(SDL_Surface *screen, int red, int green, int blue, int radius, int offsetx, int offsety) {
  float i;
  for (i=0; i<2*M_PI; i+=0.01) {
    int x = offsetx + radius*cos(i);
    int y = offsety + radius*sin(i);
    DrawPixel(screen, x, y, red, green, blue );
    }
}

/* Begin random generator between 0 and specified value. */
int randomnumber(int randmax) {
  return rand()/(RAND_MAX/randmax);
}

/* Define a couple of variations on drawing circles. */

void BlueCircle (SDL_Surface *screen) {
    int blue;
    int red = randomnumber(255);
    int green = randomnumber(255);
    int offsetx = randomnumber(640);
    int offsety = randomnumber(480);
    int radius = 1;
    for (blue=0; blue < offsety; blue++) {
      DrawCircle(screen, red, green, blue, radius, offsetx, offsety);
      radius++;
    }
}
void RedCircle (SDL_Surface *screen) {
    int radius = 1;
    int red;
    int offsetx = randomnumber(640);
    int offsety = randomnumber(480);
    int blue = randomnumber(255);
    int green = randomnumber(255);
    for (red=0; red < offsetx; red++) {
      DrawCircle(screen, red, green, blue, radius, offsetx, offsety);
      radius++;
    }
}

void GreenCircle (SDL_Surface *screen) {
    int radius = 1;
    int green;
    int offsetx = randomnumber(640);
    int offsety = randomnumber(480);
    int blue = randomnumber(255);
    int red = randomnumber(255);
    for (green = 0; green < offsety; green++) {
      DrawCircle(screen, red, green, blue, radius, offsetx, offsety);
      radius++;
    }

}

/* What we really want to do. */
void MyEffect (SDL_Surface *screen) {
  BlueCircle(screen);
  RedCircle(screen);
  GreenCircle(screen);
}

int main()
{
  SDL_Surface *screen;

  if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
	fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
	exit(1);
  }
  atexit(SDL_Quit);

  screen = SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE);
    if ( screen == NULL ) {
        fprintf(stderr, "Unable to set 640x480 video: %s\n", SDL_GetError());
        exit(1);
    }

  printf("Set window of 640x480 at %d bits per pixel mode\n", 
	screen->format->BitsPerPixel);
  
/* Set random seed. */
  srandom(time (0));

  SDL_Event event;
/* Loop eternally through these 3 circle draws, while keeping an eye on 
user events.*/
  int quit = 0;
  while ( !quit ) {

  MyEffect(screen);

    while (SDL_PollEvent ( &event )) {
                
                switch( event.type ){

	/* SDL_QUIT event (window close) */
                    case SDL_QUIT:
		printf("SDL Quit sequence pressed, e.g. x on window.\n");
                        quit = 1;
                        break;

                    default:
                        break;
                }

            }

    }

  printf("Quitting SDL.\n");

  return(0);
}

When we change something in one of the functions now, it can only possibly affect functions using the changed function, and not the main function.

Code optimization and style

All very well, this management of pixels, but upgrading the screen (window) takes a long time. It would be like sending packets over the internet one character at the time, making a new connection to send each character. This is not done: we are going to bundle packages and try to send as much as possible in one connection.

So we will make our program much faster, by doing the update only after we draw an individual circle. Therefor, we move the update definitions from the DrawPixel to the DrawCircle function.

Here's the program:

/* Program name: optimized-circles2.c */
/* SDL screen test */

# include <SDL.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <math.h>
# include <time.h>

/* This is a function that draws a pixel in the appropriate colour depth. */
void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B)
{
  /* Don't fail on attempts to draw outside the screen. */
  if (( x >= 640) || (x < 0)) return;
  if (( y >= 480) || (y < 0)) return;

    Uint32 color = SDL_MapRGB(screen->format, R, G, B);
 
    switch (screen->format->BytesPerPixel) {
        case 1: { /* Assuming 8-bpp */
            Uint8 *bufp;
 
            bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
            *bufp = color;
        }
        break;
 
        case 2: { /* Probably 15-bpp or 16-bpp */
            Uint16 *bufp;
 
            bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;
            *bufp = color;
        }
        break;
 
        case 3: { /* Slow 24-bpp mode, usually not used */
            Uint8 *bufp;
 
            bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;
            if(SDL_BYTEORDER == SDL_LIL_ENDIAN) {
                bufp[0] = color;
                bufp[1] = color >> 8;
                bufp[2] = color >> 16;
            } else {
                bufp[2] = color;
                bufp[1] = color >> 8;
                bufp[0] = color >> 16;
            }
        }
        break;
 
        case 4: { /* Probably 32-bpp */
            Uint32 *bufp;
 
            bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;
            *bufp = color;
        }
        break;
    }
}

/* Draw a circle with specified color, size and center.  Uses DrawPixel.*/
void DrawCircle(SDL_Surface *screen, int red, int green, int blue, int radius, int offsetx, int offsety) {
    if ( SDL_MUSTLOCK(screen) ) {
        if ( SDL_LockSurface(screen) < 0 ) {
            return;
        }
    }

  float i;
  for (i=0; i<2*M_PI; i+=0.1/(float)radius) {
    int x = offsetx + radius*cos(i);
    int y = offsety + radius*sin(i);
    DrawPixel(screen, x, y, red, green, blue );
    }
  if ( SDL_MUSTLOCK(screen) ) {
        SDL_UnlockSurface(screen);
    }
    int min_xcord, min_ycord, width, height;
/* Check that these coordinates are all within the region of the specified 
screen. */
    if ( offsetx - radius < 0 )
	min_xcord = 0;
	else 
	min_xcord = offsetx - radius;
    if ( offsety - radius < 0 )
	min_ycord = 0;
	else
	min_ycord = offsety - radius;
    if ( offsetx + radius > 639 )
	width = 639 - min_xcord;
	else
	width = offsetx + radius - min_xcord;
    if ( offsety + radius > 479 )
	height = 479 - min_ycord;
	else
	height = offsety + radius - min_ycord;

    SDL_UpdateRect(screen, min_xcord, min_ycord, width, height);
}

/* Begin random generator between 0 and specified value. */
int randomnumber(int randmax) {
  return rand()/(RAND_MAX/randmax);
}

/* Define a couple of variations on drawing circles. */

void BlueCircle (SDL_Surface *screen) {
    int blue;
    int red = randomnumber(255);
    int green = randomnumber(255);
    int offsetx = randomnumber(640);
    int offsety = randomnumber(480);
    int radius = 1;
    for (blue=0; blue < offsety; blue++) {
      DrawCircle(screen, red, green, blue, radius, offsetx, offsety);
      radius++;
    }
}
void RedCircle (SDL_Surface *screen) {
    int radius = 1;
    int red;
    int offsetx = randomnumber(640);
    int offsety = randomnumber(480);
    int blue = randomnumber(255);
    int green = randomnumber(255);
    for (red=0; red < offsetx; red++) {
      DrawCircle(screen, red, green, blue, radius, offsetx, offsety);
      radius++;
    }
}

void GreenCircle (SDL_Surface *screen) {
    int radius = 1;
    int green;
    int offsetx = randomnumber(640);
    int offsety = randomnumber(480);
    int blue = randomnumber(255);
    int red = randomnumber(255);
    for (green = 0; green < offsety; green++) {
      DrawCircle(screen, red, green, blue, radius, offsetx, offsety);
      radius++;
    }

}

/* What we really want to do. */
void MyEffect (SDL_Surface *screen) {
  BlueCircle(screen);
  RedCircle(screen);
  GreenCircle(screen);
}

int main()
{
  SDL_Surface *screen;

  if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
	fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
	exit(1);
  }
  atexit(SDL_Quit);

  screen = SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE);
    if ( screen == NULL ) {
        fprintf(stderr, "Unable to set 640x480 video: %s\n", SDL_GetError());
        exit(1);
    }

  printf("Set window of 640x480 at %d bits per pixel mode\n", 
	screen->format->BitsPerPixel);
  
/* Set random seed. */
  srandom(time (0));

  SDL_Event event;
/* Loop eternally through these 3 circle draws, while keeping an eye on 
user events.*/
  int quit = 0;
  while ( !quit ) {

  MyEffect(screen);

    while (SDL_PollEvent ( &event )) {
                
                switch( event.type ){

	/* SDL_QUIT event (window close) */
                    case SDL_QUIT:
		printf("SDL Quit sequence pressed, e.g. x on window.\n");
                        quit = 1;
                        break;

                    default:
                        break;
                }

            }

    }

  printf("Quitting SDL.\n");

  return(0);
}

Now compile this and see how much faster it goes.

Being nice to the system

More function optimization: we replace the RedCircle, GreenCircle and Bluecircle with a randomized case statement, using the randomnumber function.

Apart from that we introduce a waiting period after each ball completes, which is better for the load on your system.

So, this is the replacement function RandomCircle, and the MyEffect function:

/* Define a couple of variations on drawing circles.  Uses randomnumber. */
void RandomCircle(SDL_Surface *screen) {
  int offsetx = randomnumber(640);
  int offsety = randomnumber(480);
  int red = randomnumber(255);
  int green = randomnumber(255);
  int blue = randomnumber(255);
  int radius = 1;
  int switchcase = randomnumber(3);
  switch (switchcase) {
        int i;
        case 0:
              for (i=0; i < red; i++) {
                DrawCircle(screen, i, green, blue, radius, offsetx, offsety);
                radius++;
                }
                break;
        case 1:
                for ( i = 0; i < green; i++) {
                DrawCircle(screen, red, i, blue, radius, offsetx, offsety);
                radius++;
                }
                break;
        case 2:
                for (i=0; i < blue; i++) {
                DrawCircle(screen, red, green, i, radius, offsetx, offsety);
                radius++;
                }
                break;
        }
}
                                                                                
/* What we really want to do. */
void MyEffect (SDL_Surface *screen) {
  RandomCircle(screen);
  sleep (3);
}

If you are having trouble with the randomized functions, troubleshoot by taking out the randomization and defining a value for each variable, and adding some printf statements to see what is happening, like this:

void RandomCircle(SDL_Surface *screen) {
  int offsetx = randomnumber(640);
  int offsety = randomnumber(480);
/*  int red = randomnumber(255);
  int green = randomnumber(255);
  int blue = randomnumber(255);*/
/* color prob
                                                                                
  int red = 100;
  int blue = 100;
  int green = 100;*/
                                                                                
  Uint8 red = 100;
  Uint8 blue = 100;
  Uint8 green = 100;
                                                                                
  int radius = 1;
  int switchcase = randomnumber(3);
  switch (0) {
        int i;
        case 0:
              for (i=0; i < red; i++) {
                printf("%d %d %d %d %d %d\n", i, green, blue, radius, offsetx, offsety);
                DrawCircle(screen, i, green, blue, radius, offsetx, offsety);
                radius++;
		sleep(1);
                }
        }
}

I thought I was having problems with the colors, because some balls switched color rather suddenly with some balls, instead of gradually fading toward the outer edges. So I first tried to use the "official" format for a color value, which according to the include files should actually be Uint8. But that didn't solve a thing.

It turned out I forgot to break out of each case statement, so the program looped through each case starting from the randomly determined one, instead of choosing only one case. I was able to determine this by adding a sleep statement to each draw of a circle, so printout of variable values wouldn't scroll by too fast.

Now we're going fullscreen.

Home
© 1995-2010 Machtelt Garrels - tille - Powered by vIm - Best viewed with your eyes - Validated by W3C - Last update 20100511