/*
* Triplane Classic - a side-scrolling dogfighting game.
* Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* tjt@users.sourceforge.net
*/
#include
#include
#include
#include
#include "sdl_compat.h"
#include "io/joystick.h"
#include "settings.h"
#include "util/wutil.h"
/*
* min/max value given by joystick axis is multiplied by this to give
* the threshold for when a direction is active
*/
#define JOYSTICK_THRESHOLD_MULTIPLIER 0.8
joystick_configuration joystick_config[2];
SDL_Joystick *joydev[2] = { NULL, NULL };
/**
* Initializes joystick configuration.
* @return Bitwise or of JOY1X, JOY1Y, JOY2X and JOY2Y as
* appropriate. 1 bit means that a device providing the axis is available
*/
short init_joysticks(void) {
/* set default config, used if the configuration file does not exist */
joystick_config[0].up.type = 2;
joystick_config[0].up.n = 1;
joystick_config[0].up.threshold = (Sint16) (32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
joystick_config[0].down.type = 2;
joystick_config[0].down.n = 1;
joystick_config[0].down.threshold = (Sint16) (-32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
joystick_config[0].power.type = 2;
joystick_config[0].power.n = 0;
joystick_config[0].power.threshold = (Sint16) (32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
joystick_config[0].brake.type = 2;
joystick_config[0].brake.n = 0;
joystick_config[0].brake.threshold = (Sint16) (-32767 * JOYSTICK_THRESHOLD_MULTIPLIER);
joystick_config[0].guns.type = 1;
joystick_config[0].guns.n = 0;
joystick_config[0].guns.threshold = 0;
joystick_config[0].bombs.type = 1;
joystick_config[0].bombs.n = 1;
joystick_config[0].bombs.threshold = 0;
joystick_config[0].roll.type = 0;
joystick_config[0].roll.n = 0;
joystick_config[0].roll.threshold = 0;
/*
* use these for noautoroll:
* joystick_config[0].roll.type = 1;
* joystick_config[0].roll.n = 2;
* joystick_config[0].roll.threshold = 0;
*/
memcpy(&joystick_config[1], &joystick_config[0], sizeof(joystick_configuration));
if (SDL_NumJoysticks() >= 2)
return JOY1X | JOY1Y | JOY2X | JOY2Y;
else if (SDL_NumJoysticks() == 1)
return JOY1X | JOY1Y;
else
return 0;
}
/**
* Loads saved joystic presence and calibration data.
* @return 1 if succeeded in loading something
*/
int load_joysticks_data(const char *filename) {
FILE *fp;
fp = settings_open(filename, "rb");
if (fp != NULL) {
fread(&joystick_config, sizeof(joystick_configuration), 2, fp);
fclose(fp);
return 1;
}
return 0;
}
/**
* Saves joystic precense and calibration data.
*/
void save_joysticks_data(const char *filename) {
FILE *fp = settings_open(filename, "wb");
fwrite(&joystick_config, sizeof(joystick_configuration), 2, fp);
fclose(fp);
}
/**
* Open and close joystick devices according to the arguments.
* @param joy1 = 1 if joystick 1 should be opened
* @param joy2 = 1 if joystick 2 should be opened
*/
void open_close_joysticks(int joy1, int joy2) {
if (SDL_NumJoysticks() >= 1) {
if (!joy1 && SDL_JoystickOpened(0)) {
SDL_JoystickClose(joydev[0]);
joydev[0] = NULL;
}
if (joy1 && !SDL_JoystickOpened(0)) {
joydev[0] = SDL_JoystickOpen(0);
}
}
if (SDL_NumJoysticks() >= 2) {
if (!joy2 && SDL_JoystickOpened(1)) {
SDL_JoystickClose(joydev[1]);
joydev[1] = NULL;
}
if (joy2 && !SDL_JoystickOpened(1)) {
joydev[1] = SDL_JoystickOpen(1);
}
}
}
static int is_joystick_action_active(int t, const joystick_action * a) {
if (a->type == 1) {
return SDL_JoystickGetButton(joydev[t], a->n);
} else if (a->type == 2) {
Sint16 v = SDL_JoystickGetAxis(joydev[t], a->n);
if (a->threshold_dir == 1) /* upper bound */
return (v < a->threshold);
else
return (v > a->threshold);
} else {
return 0;
}
}
/**
* Updates player action {*down,*up,*power,*roll,*guns,*bombs}
* according to current state of joystick t
* @param t index of joystick (0 or 1)
* @param inmenu = 1 if the player is in a menu (e.g. hangar menu)
*/
void get_joystick_action(int t, int inmenu, int *down, int *up, int *power, int *roll, int *guns, int *bombs) {
/*
* Special joystick actions for hangar menu are disabled here because
* they are unintuitive on gamepads and other non-default joystick
* configurations. Comment out next line to re-enable.
* FIXME perhaps make this a command-line option or something?
*/
inmenu = 0;
if (inmenu) {
*down = is_joystick_action_active(t, &joystick_config[t].down);
*up = is_joystick_action_active(t, &joystick_config[t].up);
*power = is_joystick_action_active(t, &joystick_config[t].guns);
*roll = is_joystick_action_active(t, &joystick_config[t].bombs);
*guns = is_joystick_action_active(t, &joystick_config[t].power);
*bombs = is_joystick_action_active(t, &joystick_config[t].brake);
} else {
*roll = is_joystick_action_active(t, &joystick_config[t].roll);
*guns = is_joystick_action_active(t, &joystick_config[t].guns);
*bombs = is_joystick_action_active(t, &joystick_config[t].bombs);
if (is_joystick_action_active(t, &joystick_config[t].brake)) {
*power = 0;
*down = 1;
*up = 1;
} else {
*power = is_joystick_action_active(t, &joystick_config[t].power);
*down = is_joystick_action_active(t, &joystick_config[t].down);
*up = is_joystick_action_active(t, &joystick_config[t].up);
}
}
}
/** Does joystick t have a roll button? */
int joystick_has_roll_button(int t) {
return (joystick_config[t].roll.type != 0);
}
/** Allocate enough memory to hold state of axes of given joy. */
Sint16 *allocate_axis_state(int joy) {
return (Sint16 *) walloc(SDL_JoystickNumAxes(joydev[joy]) * sizeof(Sint16));
}
/** Save state of all axes of a given joystick */
void save_axis_state(Sint16 * axes, int joy) {
int i, num;
num = SDL_JoystickNumAxes(joydev[joy]);
assert(num >= 0);
for (i = 0; i < num; i++) {
axes[i] = SDL_JoystickGetAxis(joydev[joy], i);
}
}
/* Find axis that has changed most between idle and current state */
void find_changed_axis(struct joystick_action *act, Sint16 * idle, Sint16 * current, int joy) {
int i, num, max_index, max_value;
num = SDL_JoystickNumAxes(joydev[joy]);
assert(num >= 0);
max_index = 0;
max_value = abs(idle[0] - current[0]);
for (i = 1; i < num; i++) {
int val;
val = abs(idle[i] - current[i]);
if (val > max_value) {
max_value = val;
max_index = i;
}
}
act->type = 2;
act->n = max_index;
if (idle[max_index] < current[max_index]) {
act->threshold_dir = 0; /* lower bound */
} else {
act->threshold_dir = 1; /* upper bound */
}
act->threshold = ((Sint16) (idle[max_index] + JOYSTICK_THRESHOLD_MULTIPLIER * ((double) current[max_index] - (double) idle[max_index])));
}
/** Find button that is down. Returns 0 if no buttons are pressed. */
int find_changed_button(struct joystick_action *act, int joy) {
int i, num;
num = SDL_JoystickNumButtons(joydev[joy]);
assert(num >= 0);
for (i = 0; i < num; i++) {
if (SDL_JoystickGetButton(joydev[joy], i)) {
act->type = 1;
act->n = i;
return 1;
}
}
return 0;
}
void set_disabled_action(struct joystick_action *act) {
act->type = 0;
}
char *get_joy_action_string(struct joystick_action *act) {
char *buf = (char *) walloc(15);
if (act->type == 0) {
strcpy(buf, "-");
} else if (act->type == 1) {
sprintf(buf, "B%d", act->n);
} else if (act->type == 2) {
sprintf(buf, "A%d%c", act->n, act->threshold_dir == 0 ? '+' : '-');
}
return buf;
}