JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
joysticks: configurable, working, buttons enable joystick
authorJason Woofenden <jason@jasonwoof.com>
Thu, 5 May 2011 05:32:11 +0000 (01:32 -0400)
committerJason Woofenden <jason@jasonwoof.com>
Thu, 5 May 2011 05:32:16 +0000 (01:32 -0400)
It works!

Press a joystick button to enable that joystick.

Here's why you need to enable the joystick: We don't want your ship to drift
when you have a joystick plugged in and you're not using it, and it doesn't
center perfectly.

You can use the -j/--joystick option to specify which axes you'd like to use
(I've tested it with a PlayStation 3 controller, I can use the tilt sensor to
control VoR like this:

vor -j 4,5

For those with axes but not any buttons (perhaps a handheld computer with an
accelerometer) you can enable a joystick with by adding the joystick number (0
unless you have multiple joysticks) as the third parameter to the -j/--joystick
flag, like this:

vor --joystick=0,1,0

The argument parser is passed the following argv value unless there's a =. When
parsing for your argument, you should barf if you get an invalid value that
looks like it might be a flag (and tell the user they forgot to put in your
argument.) The current parser will handle these forms:

vor -j 0,1
vor --joystick=0,1
vor --joystick 0,1

Still need to make sensitivity adjustable and enable reversing axes.

args.c
args.h
main.c

diff --git a/args.c b/args.c
index 5056239..3ff1412 100644 (file)
--- a/args.c
+++ b/args.c
@@ -8,6 +8,10 @@
 // Look and Feel
 int opt_fullscreen;
 int opt_sound;
+int opt_joystick_enabled;
+int opt_joystick_number;
+int opt_joystick_x_axis;
+int opt_joystick_y_axis;
 
 int opt_autopilot;
 
@@ -17,19 +21,108 @@ show_help(void)
        puts("Dodge the rocks until you die.");
        putchar('\n');
        puts("  -f, --full-screen");
-       puts("  -s, --silent               No explosion sounds or music");
-       puts("  -V, --version              Print program version");
-       puts("  -?, --help                 Give this help list");
+       puts("  -s, --silent             No explosion sounds or music");
+       puts("  -j x,y, --joystick=x,y   set the axis numbers. defaults to 0,1");
+       puts("                           press a joystick button to activate that joystick");
+       puts("  -V, --version            Print program version");
+       puts("  -?, --help               Give this help list");
        putchar('\n');
        puts("Report bugs at http://jasonwoof.com/contact.html");
 }
 
+// Advances buf while reading a positive int, terminated by [^0-9]
+// buf is left pointing at first [^0-9]
+// If digits are found, the parsed number is written to *out and 0 is returned.
+// Otherwise *buf and *out are left unchanged, and non-zero is returned.
+int
+parse_next_int(char **buf, int* out) {
+       int ret = 0, mul = 1;
+       if(!*buf) {
+               return 1;
+       }
+       // make sure *buf starts -?[0-9]
+       if(**buf == '-') {
+               mul = -1;
+               if((*buf)[1] < '0' || (*buf)[1] > '9') {
+                       return 2;
+               }
+               *buf += 1;
+       } else {
+               if(**buf < '0' || **buf > '9') {
+                       return 2;
+               }
+       }
+       while(**buf >= '0' && **buf <= '9') {
+               ret *= 10;
+               ret += **buf - '0';
+               *buf += 1;
+       }
+       *out = ret * mul;
+
+       return 0;
+}
+
+// returns 1 on success. return 0 causes usage message
+int
+parse_joystick_opts(char *arg) {
+       char *arg_was = arg;
+       static char* bad_arg = "Error: invalid argument to -j/--joystick.";
+
+       // argument is required
+       if(arg == NULL) {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // read x axis
+       if(parse_next_int(&arg, &opt_joystick_x_axis)) {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // skip comma
+       if(*arg != ',') {
+               puts(bad_arg);
+               return 0;
+       }
+       arg += 1;
+
+       // read y axis
+       if(parse_next_int(&arg, &opt_joystick_y_axis)) {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // optionally joystick number
+       if(*arg == ',') {
+               arg += 1; // skip comma
+               if(parse_next_int(&arg, &opt_joystick_number)) {
+                       puts(bad_arg);
+                       return 0;
+               }
+               opt_joystick_enabled = 1;
+       }
+
+       // end with a comma or end of string
+       if(*arg != 0 && *arg != ',') {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // mark arg as consumed (so it won't be parsed as a commandline switch)
+       arg_was[0] = 0;
+
+       // return success
+       return 1;
+}
 int
 short_opt(char c, char *arg)
 {
        switch(c) {
                case 'f': opt_fullscreen = 1; break;
                case 's': opt_sound = 0; break;
+               case 'j':
+                       return parse_joystick_opts(arg);
                case 'V':
                                  printf("Variations on Rockdodger %s\n", PACKAGE_VERSION);
                                  exit(0);
@@ -50,9 +143,9 @@ parse_short_opts(const char *s, char *arg)
        return short_opt(*s, arg);
 }
 
-static char *long_opts[] = { "full-screen", "silent", "version", "help", "autopilot" };
+static char *long_opts[] = { "full-screen", "silent", "joystick", "version", "help", "autopilot" };
 
-static char short_opts[] = { 'f', 's', 'V', 'h', 'a' };
+static char short_opts[] = { 'f', 's', 'j', 'V', 'h', 'a' };
 
 int
 parse_long_opt(const char *s, char *arg)
@@ -71,6 +164,10 @@ init_opts(void)
 {
        opt_fullscreen = 0;
        opt_sound = 1;
+       opt_joystick_enabled = 0; // can also be enabled by pressing one of its buttons
+       opt_joystick_number = 0;
+       opt_joystick_x_axis = 0;
+       opt_joystick_y_axis = 1;
        opt_autopilot = 0;
 }
 
@@ -83,6 +180,8 @@ parse_opts(int argc, char *argv[])
        init_opts();
        for(i=1; i<argc; i++) {
                char *s, *arg;
+               // args that are consumed as arguments to an option (such as the zero
+               // in: -j 0) have their first byte set to null. So skip them:
                s = argv[i]; if(!*s) continue;
                if(*s++ != '-') {
                        fputs("not an option\n\n", stderr);
@@ -92,7 +191,12 @@ parse_opts(int argc, char *argv[])
 
                arg = NULL;
                for(r=s; *r; r++) if(*r == '=') { *r = 0; arg = r+1; break; }
-               if(!arg && (i+1 < argc)) arg = argv[i+1];
+
+               if(arg == NULL && i + 1 < argc) {
+                       arg = argv[i+1];
+                       // if this is used, it's first byte will be set to null, and
+                       // it'll be skipped. See above.
+               }
 
                if(*s == '-') {
                        if(!parse_long_opt(s+1, arg)) { show_help(); return 0; }
diff --git a/args.h b/args.h
index 4b2cd13..3f906e9 100644 (file)
--- a/args.h
+++ b/args.h
@@ -4,6 +4,10 @@
 // Look and Feel
 extern int opt_fullscreen;
 extern int opt_sound;
+extern int opt_joystick_enabled;
+extern int opt_joystick_number;
+extern int opt_joystick_x_axis;
+extern int opt_joystick_y_axis;
 
 extern int opt_autopilot;
 
diff --git a/main.c b/main.c
index 86807c3..9227c91 100644 (file)
--- a/main.c
+++ b/main.c
@@ -103,7 +103,8 @@ float fadetimer = 0;
 
 int paused = 0;
 
-SDL_Joystick *joy = NULL;
+int num_joysticks = 0;
+SDL_Joystick **joysticks = NULL;
 
 // bangdot start (bd1) and end (bd2) position:
 int bd1 = 0, bd2 = 0;
@@ -473,9 +474,14 @@ init(void) {
                exit(1);
        }
 
-       if(SDL_NumJoysticks() > 0)
+       num_joysticks = SDL_NumJoysticks();
+printf("num_joysticks: %i\n", num_joysticks);
+       if(num_joysticks)
        {
-               NULLERROR(joy = SDL_JoystickOpen(0));
+               joysticks = (SDL_Joystick **)malloc(num_joysticks * sizeof(SDL_Joystick *));
+               for(i = 0; i < num_joysticks; ++i) {
+                       NULLERROR(joysticks[i] = SDL_JoystickOpen(i));
+               }
        }
 
        init_dots();
@@ -686,8 +692,8 @@ gameloop() {
        SDL_Event e;
        Uint8 *keystate;
        Sint16 x_move, y_move;
-       char button_pressed;
-       short i;
+       char button_pressed = 0;
+       short i, j;
        float tmp;
 
        for(;;) {
@@ -771,16 +777,40 @@ gameloop() {
                        }
                }
                keystate = SDL_GetKeyState(NULL);
-               SDL_JoystickUpdate();
-               x_move = SDL_JoystickGetAxis(joy, 4);
-               y_move = SDL_JoystickGetAxis(joy, 5);
-               button_pressed = 0;
-               for(i = 1; i <= SDL_JoystickNumButtons(joy); i++)
-               {
-                       if(SDL_JoystickGetButton(joy, i) == 1)
-                       {
-                               button_pressed = 1;
-                               break;
+               if(num_joysticks) {
+                       SDL_JoystickUpdate();
+                       if(opt_joystick_enabled) {
+                               x_move = SDL_JoystickGetAxis(joysticks[opt_joystick_number], opt_joystick_x_axis);
+                               y_move = SDL_JoystickGetAxis(joysticks[opt_joystick_number], opt_joystick_y_axis);
+                               button_pressed = 0;
+                               for(i = 1; i <= SDL_JoystickNumButtons(joysticks[opt_joystick_number]); i++)
+                               {
+                                       if(SDL_JoystickGetButton(joysticks[opt_joystick_number], i) == 1)
+                                       {
+                                               button_pressed = 1;
+                                               break;
+                                       }
+                               }
+                       } else { // there is at least one joystick, but it hasn't been enabled yet
+                               // if any joystick has a button down, enable that joystick
+                               for(j = 0; j <= num_joysticks; j++) {
+                                       for(i = 1; i <= SDL_JoystickNumButtons(joysticks[j]); i++)
+                                       {
+                                               if(SDL_JoystickGetButton(joysticks[j], i) == 1)
+                                               {
+                                                       opt_joystick_enabled = 1;
+                                                       opt_joystick_number = j;
+printf("enabled joystick #%i\n", opt_joystick_number);
+                                                       if(state != GAMEPLAY) {
+                                                               // first (enabling) press of the joystick
+                                                               // button should start a game, but should
+                                                               // not pause a running game
+                                                               button_pressed = 1;
+                                                       }
+                                                       break;
+                                               }
+                                       }
+                               }
                        }
                }
 
@@ -804,10 +834,12 @@ gameloop() {
                                if(keystate[SDLK_UP]    || keystate[SDLK_KP8]) {
                                        ship.dy -= THRUSTER_STRENGTH*t_frame; ship.jets |= 1<<3;
                                }
-                               if(x_move < -3000) { ship.dx += x_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<0;}
-                               if(y_move >  3000) { ship.dy += y_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<1;}
-                               if(x_move >  3000) { ship.dx += x_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<2;}
-                               if(y_move < -3000) { ship.dy += y_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<3;}
+                               if(opt_joystick_enabled) {
+                                       if(x_move < -3000) { ship.dx += x_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<0;}
+                                       if(y_move >  3000) { ship.dy += y_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<1;}
+                                       if(x_move >  3000) { ship.dx += x_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<2;}
+                                       if(y_move < -3000) { ship.dy += y_move*THRUSTER_STRENGTH*t_frame/22768; ship.jets |= 1<<3;}
+                               }
                                if(ship.jets) {
                                        ship.dx = fconstrain2(ship.dx, -50, 50);
                                        ship.dy = fconstrain2(ship.dy, -50, 50);
@@ -920,7 +952,7 @@ main(int argc, char **argv) {
        frames = 0;
        gameloop();
        end = SDL_GetTicks();
-       // printf("%ld frames in %ld ms, %.2f fps.\n", frames, end-start, frames * 1000.0 / (end-start));
+       printf("%ld frames in %ld ms, %.2f fps.\n", frames, end-start, frames * 1000.0 / (end-start));
 
        return 0;
 }