diff options
Diffstat (limited to 'src/setup/joystick.c')
-rw-r--r-- | src/setup/joystick.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/src/setup/joystick.c b/src/setup/joystick.c new file mode 100644 index 00000000..3d2b713b --- /dev/null +++ b/src/setup/joystick.c @@ -0,0 +1,449 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2007 Simon Howard +// +// 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 2 +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#include <stdio.h> +#include <stdlib.h> + +#include "doomtype.h" +#include "m_config.h" +#include "m_controls.h" +#include "textscreen.h" + +#include "joystick.h" +#include "mode.h" +#include "txt_joybinput.h" + +typedef enum +{ + CALIBRATE_CENTER, + CALIBRATE_LEFT, + CALIBRATE_UP, +} calibration_stage_t; + +// SDL joystick successfully initialised? + +static int joystick_initted = 0; + +// Joystick enable/disable + +static int usejoystick = 0; + +// Joystick to use, as an SDL joystick index: + +int joystick_index = -1; + +// Which joystick axis to use for horizontal movement, and whether to +// invert the direction: + +static int joystick_x_axis = 0; +static int joystick_x_invert = 0; + +// Which joystick axis to use for vertical movement, and whether to +// invert the direction: + +static int joystick_y_axis = 1; +static int joystick_y_invert = 0; + +static txt_button_t *joystick_button; + +static int *all_joystick_buttons[] = { + &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed, + &joybuse, &joybstrafe, &joybjump +}; + +// +// Calibration +// + +static txt_window_t *calibration_window; +static txt_label_t *calibration_label; +static calibration_stage_t calibrate_stage; +static SDL_Joystick **all_joysticks = NULL; + +// Set the label showing the name of the currently selected joystick + +static void SetJoystickButtonLabel(void) +{ + char *name; + + name = "None set"; + + if (joystick_initted + && joystick_index >= 0 && joystick_index < SDL_NumJoysticks()) + { + name = (char *) SDL_JoystickName(joystick_index); + } + + TXT_SetButtonLabel(joystick_button, name); +} + +// Try to open all joysticks visible to SDL. + +static int OpenAllJoysticks(void) +{ + int i; + int num_joysticks; + int result; + + if (!joystick_initted) + { + return 0; + } + + // SDL_JoystickOpen() all joysticks. + + num_joysticks = SDL_NumJoysticks(); + + all_joysticks = malloc(sizeof(SDL_Joystick *) * num_joysticks); + + result = 0; + + for (i=0; i<num_joysticks; ++i) + { + all_joysticks[i] = SDL_JoystickOpen(i); + + // If any joystick is successfully opened, return true. + + if (all_joysticks[i] != NULL) + { + result = 1; + } + } + + // Success? Turn on joystick events. + + if (result) + { + SDL_JoystickEventState(SDL_ENABLE); + } + else + { + free(all_joysticks); + all_joysticks = NULL; + } + + return result; +} + +// Close all the joysticks opened with OpenAllJoysticks() + +static void CloseAllJoysticks(void) +{ + int i; + int num_joysticks; + + num_joysticks = SDL_NumJoysticks(); + + for (i=0; i<num_joysticks; ++i) + { + if (all_joysticks[i] != NULL) + { + SDL_JoystickClose(all_joysticks[i]); + } + } + + SDL_JoystickEventState(SDL_DISABLE); + + free(all_joysticks); + all_joysticks = NULL; +} + +static void SetCalibrationLabel(void) +{ + char *message = "???"; + + switch (calibrate_stage) + { + case CALIBRATE_CENTER: + message = "Move the joystick to the\n" + "center, and press a button."; + break; + case CALIBRATE_UP: + message = "Move the joystick up,\n" + "and press a button."; + break; + case CALIBRATE_LEFT: + message = "Move the joystick to the\n" + "left, and press a button."; + break; + } + + TXT_SetLabel(calibration_label, message); +} + +static void CalibrateAxis(int *axis_index, int *axis_invert) +{ + SDL_Joystick *joystick; + int best_axis; + int best_value; + int best_invert; + Sint16 axis_value; + int i; + + joystick = all_joysticks[joystick_index]; + + // Check all axes to find which axis has the largest value. We test + // for one axis at a time, so eg. when we prompt to push the joystick + // left, whichever axis has the largest value is the left axis. + + best_axis = 0; + best_value = 0; + best_invert = 0; + + for (i=0; i<SDL_JoystickNumAxes(joystick); ++i) + { + axis_value = SDL_JoystickGetAxis(joystick, i); + + if (abs(axis_value) > best_value) + { + best_value = abs(axis_value); + best_invert = axis_value > 0; + best_axis = i; + } + } + + // Save the best values we have found + + *axis_index = best_axis; + *axis_invert = best_invert; +} + +static int CalibrationEventCallback(SDL_Event *event, void *user_data) +{ + if (event->type == SDL_JOYBUTTONDOWN + && (joystick_index == -1 || event->jbutton.which == joystick_index)) + { + switch (calibrate_stage) + { + case CALIBRATE_CENTER: + // Centering stage selects which joystick to use. + joystick_index = event->jbutton.which; + break; + + case CALIBRATE_LEFT: + CalibrateAxis(&joystick_x_axis, &joystick_x_invert); + break; + + case CALIBRATE_UP: + CalibrateAxis(&joystick_y_axis, &joystick_y_invert); + break; + } + + if (calibrate_stage == CALIBRATE_UP) + { + // Final stage; close the window + + TXT_CloseWindow(calibration_window); + } + else + { + // Advance to the next calibration stage + + ++calibrate_stage; + SetCalibrationLabel(); + } + + return 1; + } + + return 0; +} + +static void NoJoystick(void) +{ + txt_window_t *window; + + window = TXT_NewWindow(NULL); + + TXT_AddWidget(window, + TXT_NewLabel("No joysticks could be opened.\n\n" + "Try configuring your joystick from within\n" + "your OS first.")); + + TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL); + TXT_SetWindowAction(window, TXT_HORIZ_CENTER, + TXT_NewWindowEscapeAction(window)); + TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL); + + joystick_index = -1; + SetJoystickButtonLabel(); +} + +static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + CloseAllJoysticks(); + TXT_SDL_SetEventCallback(NULL, NULL); + SetJoystickButtonLabel(); +} + +static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + calibrate_stage = CALIBRATE_CENTER; + + // Try to open all available joysticks. If none are opened successfully, + // bomb out with an error. + + if (!OpenAllJoysticks()) + { + NoJoystick(); + return; + } + + calibration_window = TXT_NewWindow("Joystick calibration"); + + TXT_AddWidgets(calibration_window, + TXT_NewLabel("Please follow the following instructions\n" + "in order to calibrate your joystick."), + TXT_NewStrut(0, 1), + calibration_label = TXT_NewLabel("zzz"), + TXT_NewStrut(0, 1), + NULL); + + TXT_SetWindowAction(calibration_window, TXT_HORIZ_LEFT, NULL); + TXT_SetWindowAction(calibration_window, TXT_HORIZ_CENTER, + TXT_NewWindowAbortAction(calibration_window)); + TXT_SetWindowAction(calibration_window, TXT_HORIZ_RIGHT, NULL); + + TXT_SetWidgetAlign(calibration_label, TXT_HORIZ_CENTER); + TXT_SDL_SetEventCallback(CalibrationEventCallback, NULL); + + TXT_SignalConnect(calibration_window, "closed", CalibrateWindowClosed, NULL); + + // Start calibration + + joystick_index = -1; + calibrate_stage = CALIBRATE_CENTER; + + SetCalibrationLabel(); +} + +void JoyButtonSetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable)) +{ + TXT_CAST_ARG(int, variable); + unsigned int i; + + // Only allow a button to be bound to one action at a time. If + // we assign a key that another action is using, set that other action + // to -1. + + for (i=0; i<arrlen(all_joystick_buttons); ++i) + { + if (variable != all_joystick_buttons[i] + && *variable == *all_joystick_buttons[i]) + { + *all_joystick_buttons[i] = -1; + } + } +} + + +// +// GUI +// + +static void JoystickWindowClosed(TXT_UNCAST_ARG(window), TXT_UNCAST_ARG(unused)) +{ + if (joystick_initted) + { + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + joystick_initted = 0; + } +} + +static void AddJoystickControl(txt_table_t *table, char *label, int *var) +{ + txt_joystick_input_t *joy_input; + + joy_input = TXT_NewJoystickInput(var); + + TXT_AddWidget(table, TXT_NewLabel(label)); + TXT_AddWidget(table, joy_input); + + TXT_SignalConnect(joy_input, "set", JoyButtonSetCallback, var); +} + +void ConfigJoystick(void) +{ + txt_window_t *window; + txt_table_t *button_table; + txt_table_t *joystick_table; + + if (!joystick_initted) + { + joystick_initted = SDL_Init(SDL_INIT_JOYSTICK) >= 0; + } + + window = TXT_NewWindow("Joystick configuration"); + + TXT_AddWidgets(window, + TXT_NewCheckBox("Enable joystick", &usejoystick), + joystick_table = TXT_NewTable(2), + TXT_NewSeparator("Joystick buttons"), + button_table = TXT_NewTable(2), + NULL); + + TXT_SetColumnWidths(joystick_table, 20, 15); + + TXT_AddWidgets(joystick_table, + TXT_NewLabel("Current joystick"), + joystick_button = TXT_NewButton("zzzz"), + NULL); + + TXT_SetColumnWidths(button_table, 20, 15); + + AddJoystickControl(button_table, "Fire/Attack", &joybfire); + AddJoystickControl(button_table, "Use", &joybuse); + + // High values of joybspeed are used to activate the "always run mode" + // trick in Vanilla Doom. If this has been enabled, not only is the + // joybspeed value meaningless, but the control itself is useless. + + if (joybspeed < 20) + { + AddJoystickControl(button_table, "Speed", &joybspeed); + } + + AddJoystickControl(button_table, "Strafe", &joybstrafe); + + AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft); + AddJoystickControl(button_table, "Strafe Right", &joybstraferight); + + if (gamemission == hexen) + { + AddJoystickControl(button_table, "Jump", &joybjump); + } + + TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL); + TXT_SignalConnect(window, "closed", JoystickWindowClosed, NULL); + + SetJoystickButtonLabel(); +} + +void BindJoystickVariables(void) +{ + M_BindVariable("use_joystick", &usejoystick); + M_BindVariable("joystick_index", &joystick_index); + M_BindVariable("joystick_x_axis", &joystick_x_axis); + M_BindVariable("joystick_y_axis", &joystick_y_axis); + M_BindVariable("joystick_x_invert", &joystick_x_invert); + M_BindVariable("joystick_y_invert", &joystick_y_invert); +} + |