From bbc098c3ee39ed3167d020275bf1df039df8fd41 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Fri, 16 May 2014 01:12:36 -0400 Subject: setup: Factor out axis configuration to widget. Move code for configuring joystick axes into a separate widget, and add axis widgets to the configuration window for all possible movement. --- src/setup/Makefile.am | 1 + src/setup/joystick.c | 410 +++++-------------------------------- src/setup/txt_joyaxis.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++++ src/setup/txt_joyaxis.h | 90 +++++++++ 4 files changed, 672 insertions(+), 356 deletions(-) create mode 100644 src/setup/txt_joyaxis.c create mode 100644 src/setup/txt_joyaxis.h (limited to 'src') diff --git a/src/setup/Makefile.am b/src/setup/Makefile.am index 27de0add..1a1e1ccb 100644 --- a/src/setup/Makefile.am +++ b/src/setup/Makefile.am @@ -18,6 +18,7 @@ SOURCE_FILES = \ multiplayer.c multiplayer.h \ sound.c sound.h \ execute.c execute.h \ + txt_joyaxis.c txt_joyaxis.h \ txt_joybinput.c txt_joybinput.h \ txt_keyinput.c txt_keyinput.h \ txt_mouseinput.c txt_mouseinput.h diff --git a/src/setup/joystick.c b/src/setup/joystick.c index 1b2a00f3..41bde51c 100644 --- a/src/setup/joystick.c +++ b/src/setup/joystick.c @@ -25,19 +25,9 @@ #include "execute.h" #include "joystick.h" #include "mode.h" +#include "txt_joyaxis.h" #include "txt_joybinput.h" -typedef enum -{ - CALIBRATE_CENTER, - CALIBRATE_LEFT, - CALIBRATE_UP, - - // These are only used when defining button axes: - CALIBRATE_RIGHT, - CALIBRATE_DOWN, -} calibration_stage_t; - typedef struct { char *name; // Config file name @@ -69,15 +59,6 @@ int joystick_index = -1; static int calibrate_button = -1; -// "Bad" joystick axes. Sometimes an axis can be stuck or "bad". An -// example I found is that if you unplug the nunchuck extension from -// a Wii remote, the axes from the nunchuck can be stuck at one of -// the maximum values. These have to be ignored, so when we ask the -// user to center the joystick, we look for bad axes that are not -// close to zero. - -static boolean *bad_axis = NULL; - // Which joystick axis to use for horizontal movement, and whether to // invert the direction: @@ -101,14 +82,14 @@ int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = { }; static txt_button_t *joystick_button; +static txt_joystick_axis_t *x_axis_widget; +static txt_joystick_axis_t *y_axis_widget; // // 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; // Known controllers. @@ -468,262 +449,13 @@ static void CloseAllJoysticks(void) all_joysticks = NULL; } -static void SetCalibrationLabel(void) -{ - char *message = "???"; - - switch (calibrate_stage) - { - case CALIBRATE_CENTER: - message = "Move the D-pad or joystick to the\n" - "center, and press a button."; - break; - case CALIBRATE_UP: - message = "Push the D-pad or joystick up,\n" - "and press the button."; - break; - case CALIBRATE_LEFT: - message = "Push the D-pad or joystick to the\n" - "left, and press the button."; - break; - case CALIBRATE_DOWN: - message = "Push the D-pad or joystick down,\n" - "and press the button."; - break; - case CALIBRATE_RIGHT: - message = "Push the D-pad or joystick to the\n" - "right, and press the button."; - break; - } - - TXT_SetLabel(calibration_label, message); -} - -// Search all axes on joystick being configured; find a button that is -// pressed (other than the calibrate button). Returns the button number. - -static int FindPressedAxisButton(void) -{ - SDL_Joystick *joystick; - int i; - - joystick = all_joysticks[joystick_index]; - - for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i) - { - if (i == calibrate_button) - { - continue; - } - - if (SDL_JoystickGetButton(joystick, i)) - { - return i; - } - } - - return -1; -} - -// Look for a hat that isn't centered. Returns the encoded hat axis. - -static int FindUncenteredHat(int *axis_invert) -{ - SDL_Joystick *joystick; - int i, hatval; - - joystick = all_joysticks[joystick_index]; - - for (i = 0; i < SDL_JoystickNumHats(joystick); ++i) - { - hatval = SDL_JoystickGetHat(joystick, i); - - switch (hatval) - { - case SDL_HAT_LEFT: - case SDL_HAT_RIGHT: - *axis_invert = hatval != SDL_HAT_LEFT; - return CREATE_HAT_AXIS(i, HAT_AXIS_HORIZONTAL); - - case SDL_HAT_UP: - case SDL_HAT_DOWN: - *axis_invert = hatval != SDL_HAT_UP; - return CREATE_HAT_AXIS(i, HAT_AXIS_VERTICAL); - - // If the hat is centered, or is not pointing in a - // definite direction, then ignore it. We don't accept - // the hat being pointed to the upper-left for example, - // because it's ambiguous. - case SDL_HAT_CENTERED: - default: - break; - } - } - - // None found. - return -1; -} - -static boolean 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 best_value) - { - best_value = abs(axis_value); - best_invert = axis_value > 0; - best_axis = i; - } - } - - // Did we find one axis that had a significant value? - - if (best_value > 32768 / 4) - { - // Save the best values we have found - - *axis_index = best_axis; - *axis_invert = best_invert; - return true; - } - - // Otherwise, maybe this is a "button axis", like the PS3 SIXAXIS - // controller that exposes the D-pad as four individual buttons. - // Search for a button. - - i = FindPressedAxisButton(); - - if (i >= 0) - { - *axis_index = CREATE_BUTTON_AXIS(i, 0); - *axis_invert = 0; - return true; - } - - // Maybe it's a D-pad that is presented as a hat. This sounds weird - // but gamepads like this really do exist; an example is the - // Nyko AIRFLO Ex. - - i = FindUncenteredHat(axis_invert); - - if (i >= 0) - { - *axis_index = i; - return true; - } - - // User pressed the button without pushing the joystick anywhere. - return false; -} - -static boolean SetButtonAxisPositive(int *axis_index) +static void CalibrateXAxis(void) { - int button; - - button = FindPressedAxisButton(); - - if (button >= 0) - { - *axis_index |= CREATE_BUTTON_AXIS(0, button); - return true; - } - - return false; -} - -static int NextCalibrateStage(void) -{ - switch (calibrate_stage) - { - case CALIBRATE_CENTER: - return CALIBRATE_LEFT; - - // After pushing to the left, there are two possibilities: - // either it is a button axis, in which case we need to find - // the other button, or we can just move on to the next axis. - case CALIBRATE_LEFT: - if (IS_BUTTON_AXIS(joystick_x_axis)) - { - return CALIBRATE_RIGHT; - } - else - { - return CALIBRATE_UP; - } - - case CALIBRATE_RIGHT: - return CALIBRATE_UP; - - case CALIBRATE_UP: - if (IS_BUTTON_AXIS(joystick_y_axis)) - { - return CALIBRATE_DOWN; - } - else - { - // Finished. - return CALIBRATE_CENTER; - } - - case CALIBRATE_DOWN: - // Finished. - return CALIBRATE_CENTER; - } -} - -static void IdentifyBadAxes(void) -{ - SDL_Joystick *joystick; - int i, val; - - free(bad_axis); - - joystick = all_joysticks[joystick_index]; - bad_axis = calloc(SDL_JoystickNumAxes(joystick), sizeof(boolean)); - - // Look for uncentered axes. - - for (i = 0; i < SDL_JoystickNumAxes(joystick); ++i) - { - val = SDL_JoystickGetAxis(joystick, i); - - bad_axis[i] = abs(val) > (32768 / 5); - - if (bad_axis[i]) - { - printf("Ignoring uncentered joystick axis #%i: %i\n", i, val); - } - } + TXT_ConfigureJoystickAxis(x_axis_widget, calibrate_button, NULL); } static int CalibrationEventCallback(SDL_Event *event, void *user_data) { - boolean advance; - if (event->type != SDL_JOYBUTTONDOWN) { return 0; @@ -732,75 +464,28 @@ static int CalibrationEventCallback(SDL_Event *event, void *user_data) // At this point, we have a button press. // In the first "center" stage, we're just trying to work out which // joystick is being configured and which button the user is pressing. - if (calibrate_stage == CALIBRATE_CENTER) - { - joystick_index = event->jbutton.which; - calibrate_button = event->jbutton.button; - IdentifyBadAxes(); + joystick_index = event->jbutton.which; + calibrate_button = event->jbutton.button; - // If the joystick is a known one, auto-load default - // config for it. - if (IsKnownJoystick(joystick_index)) - { - LoadKnownConfiguration(); - usejoystick = 1; - TXT_CloseWindow(calibration_window); - } - else - { - // Advance to next stage. - calibrate_stage = CALIBRATE_LEFT; - SetCalibrationLabel(); - } - - return 1; + // If the joystick is a known one, auto-load default + // config for it. Otherwise, proceed with calibration. + if (IsKnownJoystick(joystick_index)) + { + LoadKnownConfiguration(); + usejoystick = 1; + TXT_CloseWindow(calibration_window); } - - // In subsequent stages, the user is asked to push in a specific - // direction and press the button. They must push the same button - // as they did before; this is necessary to support button axes. - if (event->jbutton.which == joystick_index - && event->jbutton.button == calibrate_button) + else { - switch (calibrate_stage) - { - default: - case CALIBRATE_LEFT: - advance = CalibrateAxis(&joystick_x_axis, &joystick_x_invert); - break; - - case CALIBRATE_RIGHT: - advance = SetButtonAxisPositive(&joystick_x_axis); - break; - - case CALIBRATE_UP: - advance = CalibrateAxis(&joystick_y_axis, &joystick_y_invert); - break; - - case CALIBRATE_DOWN: - advance = SetButtonAxisPositive(&joystick_y_axis); - break; - } - - // Advance to the next calibration stage? + TXT_CloseWindow(calibration_window); - if (advance) - { - calibrate_stage = NextCalibrateStage(); - SetCalibrationLabel(); - - // Finished? - if (calibrate_stage == CALIBRATE_CENTER) - { - usejoystick = 1; - TXT_CloseWindow(calibration_window); - } - - return 1; - } + // Calibrate joystick axes: Y axis first, then X axis once + // completed. + TXT_ConfigureJoystickAxis(y_axis_widget, calibrate_button, + CalibrateXAxis); } - return 0; + return 1; } static void NoJoystick(void) @@ -823,8 +508,6 @@ static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused) 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. @@ -837,10 +520,9 @@ static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) calibration_window = TXT_NewWindow("Gamepad/Joystick calibration"); TXT_AddWidgets(calibration_window, - TXT_NewLabel("Please follow the following instructions\n" - "in order to calibrate your controller."), TXT_NewStrut(0, 1), - calibration_label = TXT_NewLabel("zzz"), + TXT_NewLabel("Center the D-pad or joystick,\n" + "and press a button."), TXT_NewStrut(0, 1), NULL); @@ -849,7 +531,6 @@ static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) 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); @@ -857,9 +538,6 @@ static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) // Start calibration joystick_index = -1; - calibrate_stage = CALIBRATE_CENTER; - - SetCalibrationLabel(); } // @@ -888,10 +566,10 @@ static void AddJoystickControl(txt_table_t *table, char *label, int *var) void ConfigJoystick(void) { txt_window_t *window; - txt_table_t *button_table; + txt_table_t *button_table, *axis_table; txt_table_t *joystick_table; - if (!joystick_initted) + if (!joystick_initted) { joystick_initted = SDL_Init(SDL_INIT_JOYSTICK) >= 0; } @@ -901,8 +579,27 @@ void ConfigJoystick(void) TXT_AddWidgets(window, TXT_NewCheckBox("Enable gamepad/joystick", &usejoystick), joystick_table = TXT_NewTable(2), + TXT_NewSeparator("Axes"), + axis_table = TXT_NewTable(2), TXT_NewSeparator("Buttons"), - button_table = TXT_NewTable(2), + button_table = TXT_NewTable(4), + NULL); + + TXT_SetColumnWidths(axis_table, 20, 15); + + TXT_AddWidgets(axis_table, + TXT_NewLabel("Forward/backward"), + y_axis_widget = TXT_NewJoystickAxis(&joystick_y_axis, + &joystick_y_invert, + JOYSTICK_AXIS_VERTICAL), + TXT_NewLabel("Turn left/right"), + x_axis_widget = TXT_NewJoystickAxis(&joystick_x_axis, + &joystick_x_invert, + JOYSTICK_AXIS_HORIZONTAL), + TXT_NewLabel("Strafe left/right"), + TXT_NewJoystickAxis(&joystick_strafe_axis, + &joystick_strafe_invert, + JOYSTICK_AXIS_HORIZONTAL), NULL); TXT_SetColumnWidths(joystick_table, 20, 15); @@ -912,10 +609,18 @@ void ConfigJoystick(void) joystick_button = TXT_NewButton("zzzz"), NULL); - TXT_SetColumnWidths(button_table, 20, 15); + TXT_SetColumnWidths(button_table, 16, 12, 14, 11); AddJoystickControl(button_table, "Fire/Attack", &joybfire); + AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft); + AddJoystickControl(button_table, "Use", &joybuse); + AddJoystickControl(button_table, "Strafe Right", &joybstraferight); + + AddJoystickControl(button_table, "Previous weapon", &joybprevweapon); + AddJoystickControl(button_table, "Strafe", &joybstrafe); + + AddJoystickControl(button_table, "Next weapon", &joybnextweapon); // 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 @@ -926,13 +631,6 @@ void ConfigJoystick(void) AddJoystickControl(button_table, "Speed", &joybspeed); } - AddJoystickControl(button_table, "Strafe", &joybstrafe); - - AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft); - AddJoystickControl(button_table, "Strafe Right", &joybstraferight); - AddJoystickControl(button_table, "Previous weapon", &joybprevweapon); - AddJoystickControl(button_table, "Next weapon", &joybnextweapon); - if (gamemission == hexen || gamemission == strife) { AddJoystickControl(button_table, "Jump", &joybjump); diff --git a/src/setup/txt_joyaxis.c b/src/setup/txt_joyaxis.c new file mode 100644 index 00000000..927c0473 --- /dev/null +++ b/src/setup/txt_joyaxis.c @@ -0,0 +1,527 @@ +// +// Copyright(C) 2014 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. +// + +#include +#include +#include + +#include "SDL.h" + +#include "joystick.h" +#include "i_joystick.h" +#include "i_system.h" +#include "m_controls.h" +#include "m_misc.h" + +#include "textscreen.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_joyaxis.h" + +#define JOYSTICK_AXIS_WIDTH 24 + +static char *CalibrationLabel(txt_joystick_axis_t *joystick_axis) +{ + switch (joystick_axis->config_stage) + { + case CONFIG_CENTER: + return "Center the D-pad or joystick,\n" + "and press a button."; + + case CONFIG_STAGE1: + if (joystick_axis->dir == JOYSTICK_AXIS_VERTICAL) + { + return "Push the D-pad or joystick up,\n" + "and press the button."; + } + else + { + return "Push the D-pad or joystick to the\n" + "left, and press the button."; + } + + case CONFIG_STAGE2: + if (joystick_axis->dir == JOYSTICK_AXIS_VERTICAL) + { + return "Push the D-pad or joystick down,\n" + "and press the button."; + } + else + { + return "Push the D-pad or joystick to the\n" + "right, and press the button."; + } + } +} + +static void SetCalibrationLabel(txt_joystick_axis_t *joystick_axis) +{ + TXT_SetLabel(joystick_axis->config_label, CalibrationLabel(joystick_axis)); +} + +// Search all axes on joystick being configured; find a button that is +// pressed (other than the calibrate button). Returns the button number. + +static int FindPressedAxisButton(txt_joystick_axis_t *joystick_axis) +{ + int i; + + for (i = 0; i < SDL_JoystickNumButtons(joystick_axis->joystick); ++i) + { + if (i == joystick_axis->config_button) + { + continue; + } + + if (SDL_JoystickGetButton(joystick_axis->joystick, i)) + { + return i; + } + } + + return -1; +} + +// Look for a hat that isn't centered. Returns the encoded hat axis. + +static int FindUncenteredHat(SDL_Joystick *joystick, int *axis_invert) +{ + int i, hatval; + + for (i = 0; i < SDL_JoystickNumHats(joystick); ++i) + { + hatval = SDL_JoystickGetHat(joystick, i); + + switch (hatval) + { + case SDL_HAT_LEFT: + case SDL_HAT_RIGHT: + *axis_invert = hatval != SDL_HAT_LEFT; + return CREATE_HAT_AXIS(i, HAT_AXIS_HORIZONTAL); + + case SDL_HAT_UP: + case SDL_HAT_DOWN: + *axis_invert = hatval != SDL_HAT_UP; + return CREATE_HAT_AXIS(i, HAT_AXIS_VERTICAL); + + // If the hat is centered, or is not pointing in a + // definite direction, then ignore it. We don't accept + // the hat being pointed to the upper-left for example, + // because it's ambiguous. + case SDL_HAT_CENTERED: + default: + break; + } + } + + // None found. + return -1; +} + +static boolean CalibrateAxis(txt_joystick_axis_t *joystick_axis) +{ + int best_axis; + int best_value; + int best_invert; + Sint16 axis_value; + int i; + + // 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_axis->joystick); ++i) + { + //if (bad_axis[i]) + //{ + // continue; + //} + + axis_value = SDL_JoystickGetAxis(joystick_axis->joystick, i); + + if (abs(axis_value) > best_value) + { + best_value = abs(axis_value); + best_invert = axis_value > 0; + best_axis = i; + } + } + + // Did we find one axis that had a significant value? + + if (best_value > 32768 / 4) + { + // Save the best values we have found + + *joystick_axis->axis = best_axis; + *joystick_axis->invert = best_invert; + return true; + } + + // Otherwise, maybe this is a "button axis", like the PS3 SIXAXIS + // controller that exposes the D-pad as four individual buttons. + // Search for a button. + + i = FindPressedAxisButton(joystick_axis); + + if (i >= 0) + { + *joystick_axis->axis = CREATE_BUTTON_AXIS(i, 0); + *joystick_axis->invert = 0; + return true; + } + + // Maybe it's a D-pad that is presented as a hat. This sounds weird + // but gamepads like this really do exist; an example is the + // Nyko AIRFLO Ex. + + i = FindUncenteredHat(joystick_axis->joystick, joystick_axis->invert); + + if (i >= 0) + { + *joystick_axis->axis = i; + return true; + } + + // User pressed the button without pushing the joystick anywhere. + return false; +} + +static boolean SetButtonAxisPositive(txt_joystick_axis_t *joystick_axis) +{ + int button; + + button = FindPressedAxisButton(joystick_axis); + + if (button >= 0) + { + *joystick_axis->axis |= CREATE_BUTTON_AXIS(0, button); + return true; + } + + return false; +} + +static void IdentifyBadAxes(txt_joystick_axis_t *joystick_axis) +{ + int i, val; + + free(joystick_axis->bad_axis); + + joystick_axis->bad_axis + = calloc(SDL_JoystickNumAxes(joystick_axis->joystick), + sizeof(boolean)); + + // Look for uncentered axes. + + for (i = 0; i < SDL_JoystickNumAxes(joystick_axis->joystick); ++i) + { + val = SDL_JoystickGetAxis(joystick_axis->joystick, i); + + joystick_axis->bad_axis[i] = abs(val) > (32768 / 5); + + if (joystick_axis->bad_axis[i]) + { + printf("Ignoring uncentered joystick axis #%i: %i\n", i, val); + } + } +} + +static int NextCalibrateStage(txt_joystick_axis_t *joystick_axis) +{ + switch (joystick_axis->config_stage) + { + case CONFIG_CENTER: + return CONFIG_STAGE1; + + // After pushing to the left, there are two possibilities: + // either it is a button axis, in which case we need to find + // the other button, or we can just move on to the next axis. + case CONFIG_STAGE1: + if (IS_BUTTON_AXIS(*joystick_axis->axis)) + { + return CONFIG_STAGE2; + } + else + { + return CONFIG_CENTER; + } + + case CONFIG_STAGE2: + return CONFIG_CENTER; + } +} + +static int EventCallback(SDL_Event *event, TXT_UNCAST_ARG(joystick_axis)) +{ + TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); + boolean advance; + + if (event->type != SDL_JOYBUTTONDOWN) + { + return 0; + } + + // At this point, we have a button press. + // In the first "center" stage, we're just trying to work out which + // joystick is being configured and which button the user is pressing. + if (joystick_axis->config_stage == CONFIG_CENTER) + { + joystick_index = event->jbutton.which; + joystick_axis->config_button = event->jbutton.button; + IdentifyBadAxes(joystick_axis); + + // Advance to next stage. + joystick_axis->config_stage = CONFIG_STAGE1; + SetCalibrationLabel(joystick_axis); + + return 1; + } + + // In subsequent stages, the user is asked to push in a specific + // direction and press the button. They must push the same button + // as they did before; this is necessary to support button axes. + if (event->jbutton.which == joystick_index + && event->jbutton.button == joystick_axis->config_button) + { + switch (joystick_axis->config_stage) + { + default: + case CONFIG_STAGE1: + advance = CalibrateAxis(joystick_axis); + break; + + case CONFIG_STAGE2: + advance = SetButtonAxisPositive(joystick_axis); + break; + } + + // Advance to the next calibration stage? + + if (advance) + { + joystick_axis->config_stage = NextCalibrateStage(joystick_axis); + SetCalibrationLabel(joystick_axis); + + // Finished? + if (joystick_axis->config_stage == CONFIG_CENTER) + { + TXT_CloseWindow(joystick_axis->config_window); + + if (joystick_axis->callback != NULL) + { + joystick_axis->callback(); + } + } + + return 1; + } + } + + return 0; +} + +static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(joystick_axis)) +{ + TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); + + free(joystick_axis->bad_axis); + joystick_axis->bad_axis = NULL; + + SDL_JoystickClose(joystick_axis->joystick); + SDL_JoystickEventState(SDL_DISABLE); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + TXT_SDL_SetEventCallback(NULL, NULL); +} + +void TXT_ConfigureJoystickAxis(txt_joystick_axis_t *joystick_axis, + int using_button, + txt_joystick_axis_callback_t callback) +{ + // Open the joystick first. + if (SDL_Init(SDL_INIT_JOYSTICK) < 0) + { + return; + } + + joystick_axis->joystick = SDL_JoystickOpen(joystick_index); + if (joystick_axis->joystick == NULL) + { + // TODO: OpenErrorWindow(); + return; + } + + SDL_JoystickEventState(SDL_ENABLE); + + // Build the prompt window. + + joystick_axis->config_window + = TXT_NewWindow("Gamepad/Joystick calibration"); + TXT_AddWidgets(joystick_axis->config_window, + TXT_NewStrut(0, 1), + joystick_axis->config_label = TXT_NewLabel(""), + TXT_NewStrut(0, 1), + NULL); + + TXT_SetWindowAction(joystick_axis->config_window, TXT_HORIZ_LEFT, NULL); + TXT_SetWindowAction(joystick_axis->config_window, TXT_HORIZ_CENTER, + TXT_NewWindowAbortAction(joystick_axis->config_window)); + TXT_SetWindowAction(joystick_axis->config_window, TXT_HORIZ_RIGHT, NULL); + TXT_SetWidgetAlign(joystick_axis->config_window, TXT_HORIZ_CENTER); + + if (using_button >= 0) + { + joystick_axis->config_stage = CONFIG_STAGE1; + joystick_axis->config_button = using_button; + IdentifyBadAxes(joystick_axis); + } + else + { + joystick_axis->config_stage = CONFIG_CENTER; + } + + SetCalibrationLabel(joystick_axis); + + // Close the joystick and shut down joystick subsystem when the window + // is closed. + TXT_SignalConnect(joystick_axis->config_window, "closed", + CalibrateWindowClosed, joystick_axis); + + TXT_SDL_SetEventCallback(EventCallback, joystick_axis); + + // When successfully calibrated, invoke this callback: + joystick_axis->callback = callback; +} + +static void TXT_JoystickAxisSizeCalc(TXT_UNCAST_ARG(joystick_axis)) +{ + TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); + + // All joystickinputs are the same size. + + joystick_axis->widget.w = JOYSTICK_AXIS_WIDTH; + joystick_axis->widget.h = 1; +} + +static void TXT_JoystickAxisDrawer(TXT_UNCAST_ARG(joystick_axis)) +{ + TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); + char buf[JOYSTICK_AXIS_WIDTH + 1]; + int i; + + if (*joystick_axis->axis < 0) + { + M_StringCopy(buf, "(None)", sizeof(buf)); + } + else if (IS_BUTTON_AXIS(*joystick_axis->axis)) + { + int neg, pos; + + neg = BUTTON_AXIS_NEG(*joystick_axis->axis); + pos = BUTTON_AXIS_POS(*joystick_axis->axis); + M_snprintf(buf, sizeof(buf), "BUTTONS #%i+#%i", neg, pos); + } + else if (IS_HAT_AXIS(*joystick_axis->axis)) + { + int hat, dir; + + hat = HAT_AXIS_HAT(*joystick_axis->axis); + dir = HAT_AXIS_DIRECTION(*joystick_axis->axis); + + M_snprintf(buf, sizeof(buf), "HAT #%i (%s)", hat, + dir == HAT_AXIS_HORIZONTAL ? "horizontal" : "vertical"); + } + else + { + M_snprintf(buf, sizeof(buf), "AXIS #%i", *joystick_axis->axis); + } + + TXT_SetWidgetBG(joystick_axis); + TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); + + TXT_DrawString(buf); + + for (i=strlen(buf); iaxis = -1; + } + + return 0; +} + +static void TXT_JoystickAxisMousePress(TXT_UNCAST_ARG(widget), + int x, int y, int b) +{ + TXT_CAST_ARG(txt_joystick_axis_t, widget); + + // Clicking is like pressing enter + + if (b == TXT_MOUSE_LEFT) + { + TXT_JoystickAxisKeyPress(widget, KEY_ENTER); + } +} + +txt_widget_class_t txt_joystick_axis_class = +{ + TXT_AlwaysSelectable, + TXT_JoystickAxisSizeCalc, + TXT_JoystickAxisDrawer, + TXT_JoystickAxisKeyPress, + TXT_JoystickAxisDestructor, + TXT_JoystickAxisMousePress, + NULL, +}; + +txt_joystick_axis_t *TXT_NewJoystickAxis(int *axis, int *invert, + txt_joystick_axis_direction_t dir) +{ + txt_joystick_axis_t *joystick_axis; + + joystick_axis = malloc(sizeof(txt_joystick_axis_t)); + + TXT_InitWidget(joystick_axis, &txt_joystick_axis_class); + joystick_axis->axis = axis; + joystick_axis->invert = invert; + joystick_axis->dir = dir; + joystick_axis->bad_axis = NULL; + + return joystick_axis; +} + diff --git a/src/setup/txt_joyaxis.h b/src/setup/txt_joyaxis.h new file mode 100644 index 00000000..1f41d0e8 --- /dev/null +++ b/src/setup/txt_joyaxis.h @@ -0,0 +1,90 @@ +// +// Copyright(C) 2005-2014 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. +// + +#ifndef TXT_JOY_AXIS_H +#define TXT_JOY_AXIS_H + +typedef struct txt_joystick_axis_s txt_joystick_axis_t; + +typedef enum +{ + JOYSTICK_AXIS_HORIZONTAL, + JOYSTICK_AXIS_VERTICAL, +} txt_joystick_axis_direction_t; + +typedef enum +{ + CONFIG_CENTER, // "Center the joystick and press a button..." + CONFIG_STAGE1, // "Top or left and press a button..." + CONFIG_STAGE2, // [Optional] "Bottom or right and press a button..." +} txt_joystick_axis_stage_t; + +// Callback invoked when calibration is completed. +typedef void (*txt_joystick_axis_callback_t)(void); + +#include "txt_widget.h" +#include "txt_window.h" + +#include "SDL.h" + +// +// A joystick axis. +// + +struct txt_joystick_axis_s +{ + txt_widget_t widget; + int *axis, *invert; + txt_joystick_axis_direction_t dir; + + // Only used when configuring: + + // Configuration prompt window and label. + txt_window_t *config_window; + txt_label_t *config_label; + + // SDL joystick handle for reading joystick state. + SDL_Joystick *joystick; + + // "Bad" joystick axes. Sometimes an axis can be stuck or "bad". An + // example I found is that if you unplug the nunchuck extension from + // a Wii remote, the axes from the nunchuck can be stuck at one of + // the maximum values. These have to be ignored, so when we ask the + // user to center the joystick, we look for bad axes that are not + // close to zero. + boolean *bad_axis; + + // Stage we have reached in configuring joystick axis. + txt_joystick_axis_stage_t config_stage; + + // Button to press to advance to next stage. + int config_button; + + // Callback invoked when the axis is calibrated. + txt_joystick_axis_callback_t callback; +}; + +txt_joystick_axis_t *TXT_NewJoystickAxis(int *axis, int *invert, + txt_joystick_axis_direction_t dir); + +// Configure a joystick axis widget. +// axis: The axis widget to configure. +// using_button: If non-negative, use this joystick button as the button +// to expect from the user. Otherwise, ask. +void TXT_ConfigureJoystickAxis(txt_joystick_axis_t *axis, int using_button, + txt_joystick_axis_callback_t callback); + +#endif /* #ifndef TXT_JOY_AXIS_H */ + + -- cgit v1.2.3