Merge pull request #849 from protocultor/gyro_aiming

Gyro aiming
This commit is contained in:
Yamagi 2022-06-17 13:07:25 +02:00 committed by GitHub
commit c6f6a17176
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 478 additions and 42 deletions

View file

@ -146,13 +146,6 @@ Set `0` by default.
during gameplay and released otherwise (in menu, videos, console or if during gameplay and released otherwise (in menu, videos, console or if
game is paused). game is paused).
* **in_sdlbackbutton**: Defines which button is used in the gamepad or
joystick as the `Esc` key, that is, to be able to access the menu
and 'cancel'/'go back' on its options. When set to `0` (the default)
the Back/Select/Minus button is used. Set this to `1` to use the
Start/Menu/Plus button, and to `2` to use the Guide/Home/PS button.
Requires a game restart when changed.
* **singleplayer**: Only available in the dedicated server. Vanilla * **singleplayer**: Only available in the dedicated server. Vanilla
Quake II enforced that either `coop` or `deathmatch` is set to `1` Quake II enforced that either `coop` or `deathmatch` is set to `1`
when running the dedicated server. That made it impossible to play when running the dedicated server. That made it impossible to play
@ -467,6 +460,34 @@ Set `0` by default.
* **sw_colorlight**: enable experimental color lighting. * **sw_colorlight**: enable experimental color lighting.
## Game Controller
* **in_sdlbackbutton**: Defines which button is used in the gamepad or
joystick as the `Esc` key, to access the main menu and 'cancel' /
'go back' on its options. Default is `0`, which corresponds to the
Back/Select/Minus button. Set to `1` to use Start/Menu/Plus, and to
`2` to use the Guide/Home/PS button. Requires a game restart
(or controller replug) when changed.
* **gyro_mode**: Operation mode for the gyroscope sensor of the game
controller. Options are `0` = always off, `1` = off with the
`+gyroaction` bind to enable, `2` = on with `+gyroaction` to
disable (default), `3` = always on.
* **gyro_turning_axis**: Sets which gyro axis will be used for turning.
The default `0` is "yaw" (turn), for people who prefer to hold their
controller flat, like using a pointing device. `1` is "roll" (lean),
for people who hold the controller upright, or use a device with the
controller attached to the screen, e.g. Steam Deck.
* **gyro_calibration_(x/y/z)**: Offset values on each axis of the gyro
which helps it reach true "zero movement", complete stillness. These
values are wrong if you see your in-game view "drift" when leaving
the controller alone. As these vary by device, it's better to use
'calibrate' in the 'gamepad' -> 'gyro' menu to set them.
## cvar operations ## cvar operations
cvar operations are special commands that allow the programmatic cvar operations are special commands that allow the programmatic

View file

@ -301,6 +301,10 @@ extern cvar_t *joy_pitchsensitivity;
extern cvar_t *joy_forwardsensitivity; extern cvar_t *joy_forwardsensitivity;
extern cvar_t *joy_sidesensitivity; extern cvar_t *joy_sidesensitivity;
extern cvar_t *joy_upsensitivity; extern cvar_t *joy_upsensitivity;
extern cvar_t *gyro_mode;
extern cvar_t *gyro_turning_axis;
extern cvar_t *gyro_yawsensitivity;
extern cvar_t *gyro_pitchsensitivity;
extern cvar_t *m_pitch; extern cvar_t *m_pitch;
extern cvar_t *m_yaw; extern cvar_t *m_yaw;
extern cvar_t *m_forward; extern cvar_t *m_forward;

View file

@ -30,8 +30,8 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "header/input.h" #include "header/input.h"
#include "../../client/header/keyboard.h" #include "../header/keyboard.h"
#include "../../client/header/client.h" #include "../header/client.h"
// ---- // ----
@ -43,6 +43,15 @@
// ---- // ----
typedef enum
{
REASON_NONE,
REASON_CONTROLLERINIT,
REASON_GYROCALIBRATION
} updates_countdown_reasons;
// ----
// These are used to communicate the events collected by // These are used to communicate the events collected by
// IN_Update() called at the beginning of a frame to the // IN_Update() called at the beginning of a frame to the
// actual movement functions called at a later time. // actual movement functions called at a later time.
@ -51,6 +60,7 @@ static int sdl_back_button = SDL_CONTROLLER_BUTTON_BACK;
static float joystick_yaw, joystick_pitch; static float joystick_yaw, joystick_pitch;
static float joystick_forwardmove, joystick_sidemove; static float joystick_forwardmove, joystick_sidemove;
static float joystick_up; static float joystick_up;
static float gyro_yaw, gyro_pitch;
static qboolean mlooking; static qboolean mlooking;
// The last time input events were processed. // The last time input events were processed.
@ -130,9 +140,42 @@ static cvar_t *joy_axis_triggerright_threshold;
// Joystick haptic // Joystick haptic
static cvar_t *joy_haptic_magnitude; static cvar_t *joy_haptic_magnitude;
// Support for hot plugging of game controller // Gyro mode (0=off, 3=on, 1-2=uses button to enable/disable)
cvar_t *gyro_mode;
cvar_t *gyro_turning_axis; // yaw or roll
// Gyro sensitivity
cvar_t *gyro_yawsensitivity;
cvar_t *gyro_pitchsensitivity;
// Gyro availability
qboolean gyro_hardware = false;
// Gyro is being used in this very moment
static qboolean gyro_active = false;
// Gyro calibration
static float gyro_accum[3];
static unsigned int num_samples;
static cvar_t *gyro_calibration_x;
static cvar_t *gyro_calibration_y;
static cvar_t *gyro_calibration_z;
// To ignore SDL_JOYDEVICEADDED at game init. Allows for hot plugging of game controller afterwards.
static qboolean first_init = true; static qboolean first_init = true;
static int init_delay = 30;
// Countdown of calls to IN_Update(), needed for controller init and gyro calibration
static unsigned int updates_countdown = 30;
// Reason for the countdown
static updates_countdown_reasons countdown_reason = REASON_CONTROLLERINIT;
// Factors used to transform from SDL input to Q2 "view angle" change
#define NORMALIZE_SDL_AXIS (1.0f/32768.0f)
static float normalize_sdl_gyro = 1.0f / M_PI; // can change depending on hardware
extern void CalibrationFinishedCallback(void);
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
@ -187,7 +230,6 @@ IN_TranslateSDLtoQ2Key(unsigned int keysym)
key = K_RIGHTARROW; key = K_RIGHTARROW;
break; break;
case SDLK_RALT: case SDLK_RALT:
case SDLK_LALT: case SDLK_LALT:
key = K_ALT; key = K_ALT;
@ -265,7 +307,6 @@ IN_TranslateSDLtoQ2Key(unsigned int keysym)
key = K_F15; key = K_F15;
break; break;
case SDLK_KP_7: case SDLK_KP_7:
key = K_KP_HOME; key = K_KP_HOME;
break; break;
@ -778,6 +819,41 @@ IN_Update(void)
break; break;
} }
#if SDL_VERSION_ATLEAST(2, 0, 16) // support for controller sensors (gyro, accelerometer)
case SDL_CONTROLLERSENSORUPDATE:
if (event.csensor.sensor != SDL_SENSOR_GYRO)
{
break;
}
if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown)
{
gyro_accum[0] += event.csensor.data[0];
gyro_accum[1] += event.csensor.data[1];
gyro_accum[2] += event.csensor.data[2];
num_samples++;
break;
}
if (!gyro_active || !gyro_mode->value)
{
gyro_yaw = gyro_pitch = 0;
}
else
{
if (!gyro_turning_axis->value)
{
gyro_yaw = event.csensor.data[1] - gyro_calibration_y->value; // yaw
}
else
{
gyro_yaw = -(event.csensor.data[2] - gyro_calibration_z->value); // roll
}
gyro_yaw *= gyro_yawsensitivity->value * cl_yawspeed->value;
gyro_pitch = (event.csensor.data[0] - gyro_calibration_x->value)
* gyro_pitchsensitivity->value * cl_pitchspeed->value;
}
break;
#endif // SDL_VERSION_ATLEAST(2, 0, 16)
case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMOVED:
if (!controller) if (!controller)
{ {
@ -793,7 +869,8 @@ IN_Update(void)
if (!controller) if (!controller)
{ {
// This should be lower, but some controllers just don't want to get detected by the OS // This should be lower, but some controllers just don't want to get detected by the OS
init_delay = 100; updates_countdown = 100;
countdown_reason = REASON_CONTROLLERINIT;
} }
break; break;
@ -825,20 +902,42 @@ IN_Update(void)
// Hot plugging delay handling, to not be "overwhelmed" because some controllers // Hot plugging delay handling, to not be "overwhelmed" because some controllers
// present themselves as two different devices, triggering SDL_JOYDEVICEADDED // present themselves as two different devices, triggering SDL_JOYDEVICEADDED
// too many times. They could trigger it even at game initialization. // too many times. They could trigger it even at game initialization.
if (init_delay) // Also used to keep time of the 'controller gyro calibration' pause.
if (updates_countdown)
{ {
init_delay--; updates_countdown--;
if (!init_delay) if (!updates_countdown) // Countdown finished, apply needed action by reason
{ {
if (!first_init) switch (countdown_reason)
{ {
IN_Controller_Shutdown(false); case REASON_CONTROLLERINIT:
IN_Controller_Init(true); if (!first_init)
} {
else IN_Controller_Shutdown(false);
{ IN_Controller_Init(true);
first_init = false; }
else
{
first_init = false;
}
break;
case REASON_GYROCALIBRATION: // finish and save calibration
{
const float inverseSamples = 1.f / num_samples;
Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples);
Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples);
Cvar_SetValue("gyro_calibration_z", gyro_accum[2] * inverseSamples);
Com_Printf("Calibration results:\n X=%f Y=%f Z=%f\n",
gyro_calibration_x->value, gyro_calibration_y->value, gyro_calibration_z->value);
CalibrationFinishedCallback();
break;
}
default:
break; // avoiding compiler warning
} }
countdown_reason = REASON_NONE;
} }
} }
} }
@ -931,7 +1030,9 @@ IN_Move(usercmd_t *cmd)
// 1/32768 is to normalize the input values from SDL (they're between -32768 and // 1/32768 is to normalize the input values from SDL (they're between -32768 and
// 32768 and we want -1 to 1) for movement this is not needed, as those are // 32768 and we want -1 to 1) for movement this is not needed, as those are
// absolute values independent of framerate // absolute values independent of framerate
float joyViewFactor = (1.0f/32768.0f) * (cls.rframetime/0.01666f); float frametime_ratio = cls.rframetime/0.01666f;
float joyViewFactor = NORMALIZE_SDL_AXIS * frametime_ratio;
float gyroViewFactor = normalize_sdl_gyro * frametime_ratio;
if (joystick_yaw) if (joystick_yaw)
{ {
@ -957,6 +1058,16 @@ IN_Move(usercmd_t *cmd)
{ {
cmd->upmove -= (m_up->value * joystick_up) / 32768; cmd->upmove -= (m_up->value * joystick_up) / 32768;
} }
if (gyro_yaw)
{
cl.viewangles[YAW] += (m_yaw->value * gyro_yaw) * gyroViewFactor;
}
if (gyro_pitch)
{
cl.viewangles[PITCH] -= (m_pitch->value * gyro_pitch) * gyroViewFactor;
}
} }
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
@ -992,6 +1103,32 @@ IN_JoyAltSelectorUp(void)
joy_altselector_pressed = false; joy_altselector_pressed = false;
} }
static void
IN_GyroActionDown(void)
{
switch ((int)gyro_mode->value)
{
case 1:
gyro_active = true;
return;
case 2:
gyro_active = false;
}
}
static void
IN_GyroActionUp(void)
{
switch ((int)gyro_mode->value)
{
case 1:
gyro_active = false;
return;
case 2:
gyro_active = true;
}
}
/* /*
* Removes all pending events from SDLs queue. * Removes all pending events from SDLs queue.
*/ */
@ -1233,6 +1370,26 @@ Haptic_Feedback(char *name, int effect_volume, int effect_duration,
} }
} }
/*
* Gyro calibration functions, called from menu
*/
void
StartCalibration(void)
{
gyro_accum[0] = 0.0;
gyro_accum[1] = 0.0;
gyro_accum[2] = 0.0;
num_samples = 0;
updates_countdown = 300;
countdown_reason = REASON_GYROCALIBRATION;
}
qboolean
IsCalibrationZero(void)
{
return (!gyro_calibration_x->value && !gyro_calibration_y->value && !gyro_calibration_z->value);
}
/* /*
* Game Controller * Game Controller
*/ */
@ -1373,6 +1530,35 @@ IN_Controller_Init(qboolean notify_user)
IN_Haptic_Effects_Info(); IN_Haptic_Effects_Info();
} }
#if SDL_VERSION_ATLEAST(2, 0, 16) // support for controller sensors
if ( SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO)
&& !SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) )
{
float gyro_data_rate = SDL_GameControllerGetSensorDataRate(controller, SDL_SENSOR_GYRO);
#ifndef _WIN32 // Switch controllers behave differently on Linux & Mac, so sensitivity has to be readjusted
if (gyro_data_rate <= 200.0f)
{
normalize_sdl_gyro = 1.0f / 4.5f;
}
#endif // _WIN32
gyro_hardware = true;
Com_Printf("Gyro sensor enabled at %.2f Hz\n", gyro_data_rate);
}
else
{
Com_Printf("Gyro sensor not found.\n");
}
if ( SDL_GameControllerHasLED(controller) )
{
SDL_GameControllerSetLED(controller, 0, 80, 0); // green light
}
#endif // SDL_VERSION_ATLEAST(2, 0, 16)
break; break;
} }
} }
@ -1388,6 +1574,7 @@ IN_Init(void)
mouse_x = mouse_y = 0; mouse_x = mouse_y = 0;
joystick_yaw = joystick_pitch = joystick_forwardmove = joystick_sidemove = 0; joystick_yaw = joystick_pitch = joystick_forwardmove = joystick_sidemove = 0;
gyro_yaw = gyro_pitch = 0;
exponential_speedup = Cvar_Get("exponential_speedup", "0", CVAR_ARCHIVE); exponential_speedup = Cvar_Get("exponential_speedup", "0", CVAR_ARCHIVE);
freelook = Cvar_Get("freelook", "1", CVAR_ARCHIVE); freelook = Cvar_Get("freelook", "1", CVAR_ARCHIVE);
@ -1424,6 +1611,20 @@ IN_Init(void)
joy_axis_triggerleft_threshold = Cvar_Get("joy_axis_triggerleft_threshold", "0.15", CVAR_ARCHIVE); joy_axis_triggerleft_threshold = Cvar_Get("joy_axis_triggerleft_threshold", "0.15", CVAR_ARCHIVE);
joy_axis_triggerright_threshold = Cvar_Get("joy_axis_triggerright_threshold", "0.15", CVAR_ARCHIVE); joy_axis_triggerright_threshold = Cvar_Get("joy_axis_triggerright_threshold", "0.15", CVAR_ARCHIVE);
gyro_calibration_x = Cvar_Get("gyro_calibration_x", "0.0", CVAR_ARCHIVE);
gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE);
gyro_calibration_z = Cvar_Get("gyro_calibration_z", "0.0", CVAR_ARCHIVE);
gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "1.0", CVAR_ARCHIVE);
gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "1.0", CVAR_ARCHIVE);
gyro_turning_axis = Cvar_Get("gyro_turning_axis", "0", CVAR_ARCHIVE);
gyro_mode = Cvar_Get("gyro_mode", "2", CVAR_ARCHIVE);
if ((int)gyro_mode->value == 2)
{
gyro_active = true;
}
windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE); windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE);
Cmd_AddCommand("+mlook", IN_MLookDown); Cmd_AddCommand("+mlook", IN_MLookDown);
@ -1431,6 +1632,8 @@ IN_Init(void)
Cmd_AddCommand("+joyaltselector", IN_JoyAltSelectorDown); Cmd_AddCommand("+joyaltselector", IN_JoyAltSelectorDown);
Cmd_AddCommand("-joyaltselector", IN_JoyAltSelectorUp); Cmd_AddCommand("-joyaltselector", IN_JoyAltSelectorUp);
Cmd_AddCommand("+gyroaction", IN_GyroActionDown);
Cmd_AddCommand("-gyroaction", IN_GyroActionUp);
SDL_StartTextInput(); SDL_StartTextInput();
@ -1467,7 +1670,10 @@ IN_Controller_Shutdown(qboolean notify_user)
if (controller) if (controller)
{ {
SDL_GameControllerClose(controller); SDL_GameControllerClose(controller);
controller = NULL; controller = NULL;
gyro_hardware = false;
gyro_yaw = gyro_pitch = 0;
normalize_sdl_gyro = 1.0f / M_PI;
} }
} }
@ -1478,6 +1684,11 @@ IN_Shutdown(void)
Cmd_RemoveCommand("+mlook"); Cmd_RemoveCommand("+mlook");
Cmd_RemoveCommand("-mlook"); Cmd_RemoveCommand("-mlook");
Cmd_RemoveCommand("+joyaltselector");
Cmd_RemoveCommand("-joyaltselector");
Cmd_RemoveCommand("+gyroaction");
Cmd_RemoveCommand("-gyroaction");
Com_Printf("Shutting down input.\n"); Com_Printf("Shutting down input.\n");
IN_Controller_Shutdown(false); IN_Controller_Shutdown(false);

View file

@ -86,6 +86,22 @@ typedef struct
menulayer_t m_layers[MAX_MENU_DEPTH]; menulayer_t m_layers[MAX_MENU_DEPTH];
int m_menudepth; int m_menudepth;
static float
ClampCvar(float min, float max, float value)
{
if (value < min)
{
return min;
}
if (value > max)
{
return max;
}
return value;
}
static qboolean static qboolean
M_IsGame(const char *gamename) M_IsGame(const char *gamename)
{ {
@ -1294,6 +1310,7 @@ char *controller_bindnames[][2] =
{"invprev", "prev item"}, {"invprev", "prev item"},
{"invnext", "next item"}, {"invnext", "next item"},
{"cmd help", "help computer"}, {"cmd help", "help computer"},
{"+gyroaction", "gyro off / on"},
{"+joyaltselector", "alt buttons modifier"} {"+joyaltselector", "alt buttons modifier"}
}; };
#define NUM_CONTROLLER_BINDNAMES (sizeof controller_bindnames / sizeof controller_bindnames[0]) #define NUM_CONTROLLER_BINDNAMES (sizeof controller_bindnames / sizeof controller_bindnames[0])
@ -1596,6 +1613,184 @@ M_Menu_ControllerAltButtons_f(void)
M_PushMenu(ControllerAltButtons_MenuDraw, ControllerAltButtons_MenuKey); M_PushMenu(ControllerAltButtons_MenuDraw, ControllerAltButtons_MenuKey);
} }
/*
* GYRO OPTIONS MENU
*/
static menuframework_s s_gyro_menu;
static menulist_s s_gyro_mode_box;
static menulist_s s_turning_axis_box;
static menuslider_s s_gyro_yawsensitivity_slider;
static menuslider_s s_gyro_pitchsensitivity_slider;
static menuseparator_s s_calibrating_text[2];
static menuaction_s s_calibrate_gyro;
extern qboolean gyro_hardware;
extern void StartCalibration(void);
extern qboolean IsCalibrationZero(void);
static void
CalibrateGyroFunc(void *unused)
{
if (!gyro_hardware)
{
return;
}
m_popup_string = "Calibrating, please wait...";
m_popup_endtime = cls.realtime + 4650;
M_Popup();
R_EndFrame();
StartCalibration();
}
void
CalibrationFinishedCallback(void)
{
Menu_SetStatusBar(&s_gyro_menu, NULL);
m_popup_string = "Calibration complete!";
m_popup_endtime = cls.realtime + 1900;
M_Popup();
R_EndFrame();
}
static void
GyroModeFunc(void *unused)
{
Cvar_SetValue("gyro_mode", (int)s_gyro_mode_box.curvalue);
}
static void
TurningAxisFunc(void *unused)
{
Cvar_SetValue("gyro_turning_axis", (int)s_turning_axis_box.curvalue);
}
static void
GyroYawSensitivityFunc(void *unused)
{
Cvar_SetValue("gyro_yawsensitivity", s_gyro_yawsensitivity_slider.curvalue / 10.0F);
}
static void
GyroPitchSensitivityFunc(void *unused)
{
Cvar_SetValue("gyro_pitchsensitivity", s_gyro_pitchsensitivity_slider.curvalue / 10.0F);
}
static void
Gyro_MenuInit(void)
{
static const char *gyro_modes[] =
{
"always off",
"off, button enables",
"on, button disables",
"always on",
0
};
static const char *axis_choices[] =
{
"yaw (turn)",
"roll (lean)",
0
};
int y = 0;
float scale = SCR_GetMenuScale();
s_gyro_menu.x = (int)(viddef.width * 0.50f);
s_gyro_menu.nitems = 0;
s_gyro_mode_box.generic.type = MTYPE_SPINCONTROL;
s_gyro_mode_box.generic.x = 0;
s_gyro_mode_box.generic.y = y;
s_gyro_mode_box.generic.name = "gyro mode";
s_gyro_mode_box.generic.callback = GyroModeFunc;
s_gyro_mode_box.itemnames = gyro_modes;
s_gyro_mode_box.curvalue = ClampCvar(0, 3, gyro_mode->value);
s_turning_axis_box.generic.type = MTYPE_SPINCONTROL;
s_turning_axis_box.generic.x = 0;
s_turning_axis_box.generic.y = (y += 10);
s_turning_axis_box.generic.name = "turning axis";
s_turning_axis_box.generic.callback = TurningAxisFunc;
s_turning_axis_box.itemnames = axis_choices;
s_turning_axis_box.curvalue = ClampCvar(0, 1, gyro_turning_axis->value);
s_gyro_yawsensitivity_slider.generic.type = MTYPE_SLIDER;
s_gyro_yawsensitivity_slider.generic.x = 0;
s_gyro_yawsensitivity_slider.generic.y = (y += 20);
s_gyro_yawsensitivity_slider.generic.name = "yaw sensitivity";
s_gyro_yawsensitivity_slider.generic.callback = GyroYawSensitivityFunc;
s_gyro_yawsensitivity_slider.minvalue = 1;
s_gyro_yawsensitivity_slider.maxvalue = 80;
s_gyro_yawsensitivity_slider.curvalue = gyro_yawsensitivity->value * 10;
s_gyro_pitchsensitivity_slider.generic.type = MTYPE_SLIDER;
s_gyro_pitchsensitivity_slider.generic.x = 0;
s_gyro_pitchsensitivity_slider.generic.y = (y += 10);
s_gyro_pitchsensitivity_slider.generic.name = "pitch sensitivity";
s_gyro_pitchsensitivity_slider.generic.callback = GyroPitchSensitivityFunc;
s_gyro_pitchsensitivity_slider.minvalue = 1;
s_gyro_pitchsensitivity_slider.maxvalue = 80;
s_gyro_pitchsensitivity_slider.curvalue = gyro_pitchsensitivity->value * 10;
s_calibrating_text[0].generic.type = MTYPE_SEPARATOR;
s_calibrating_text[0].generic.x = 48 * scale + 30;
s_calibrating_text[0].generic.y = (y += 20);
s_calibrating_text[0].generic.name = "place the controller on a flat,";
s_calibrating_text[1].generic.type = MTYPE_SEPARATOR;
s_calibrating_text[1].generic.x = 48 * scale + 30;
s_calibrating_text[1].generic.y = (y += 10);
s_calibrating_text[1].generic.name = "stable surface to...";
s_calibrate_gyro.generic.type = MTYPE_ACTION;
s_calibrate_gyro.generic.x = 0;
s_calibrate_gyro.generic.y = (y += 15);
s_calibrate_gyro.generic.name = "calibrate";
s_calibrate_gyro.generic.callback = CalibrateGyroFunc;
Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_mode_box);
Menu_AddItem(&s_gyro_menu, (void *)&s_turning_axis_box);
Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_yawsensitivity_slider);
Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_pitchsensitivity_slider);
Menu_AddItem(&s_gyro_menu, (void *)&s_calibrating_text[0]);
Menu_AddItem(&s_gyro_menu, (void *)&s_calibrating_text[1]);
Menu_AddItem(&s_gyro_menu, (void *)&s_calibrate_gyro);
if (IsCalibrationZero())
{
Menu_SetStatusBar(&s_gyro_menu, "Calibration required");
}
Menu_Center(&s_gyro_menu);
}
static void
Gyro_MenuDraw(void)
{
Menu_AdjustCursor(&s_gyro_menu, 1);
Menu_Draw(&s_gyro_menu);
M_Popup();
}
static const char *
Gyro_MenuKey(int key)
{
return Default_MenuKey(&s_gyro_menu, key);
}
static void
M_Menu_Gyro_f(void)
{
Gyro_MenuInit();
M_PushMenu(Gyro_MenuDraw, Gyro_MenuKey);
}
/* /*
* JOY MENU * JOY MENU
*/ */
@ -1606,6 +1801,7 @@ static menuslider_s s_joy_forwardsensitivity_slider;
static menuslider_s s_joy_sidesensitivity_slider; static menuslider_s s_joy_sidesensitivity_slider;
static menuslider_s s_joy_upsensitivity_slider; static menuslider_s s_joy_upsensitivity_slider;
static menuslider_s s_joy_haptic_slider; static menuslider_s s_joy_haptic_slider;
static menuaction_s s_joy_gyro_action;
static menuaction_s s_joy_customize_buttons_action; static menuaction_s s_joy_customize_buttons_action;
static menuaction_s s_joy_customize_alt_buttons_action; static menuaction_s s_joy_customize_alt_buttons_action;
@ -1621,6 +1817,12 @@ CustomizeControllerAltButtonsFunc(void *unused)
M_Menu_ControllerAltButtons_f(); M_Menu_ControllerAltButtons_f();
} }
static void
ConfigGyroFunc(void *unused)
{
M_Menu_Gyro_f();
}
static void static void
HapticMagnitudeFunc(void *unused) HapticMagnitudeFunc(void *unused)
{ {
@ -1759,6 +1961,19 @@ Joy_MenuInit(void)
Menu_AddItem(&s_joy_menu, (void *)&s_joy_haptic_slider); Menu_AddItem(&s_joy_menu, (void *)&s_joy_haptic_slider);
} }
if (gyro_hardware)
{
y += 10;
s_joy_gyro_action.generic.type = MTYPE_ACTION;
s_joy_gyro_action.generic.x = 0;
s_joy_gyro_action.generic.y = y;
y += 10;
s_joy_gyro_action.generic.name = "gyro options";
s_joy_gyro_action.generic.callback = ConfigGyroFunc;
Menu_AddItem(&s_joy_menu, (void *)&s_joy_gyro_action);
}
y += 10; y += 10;
s_joy_customize_buttons_action.generic.type = MTYPE_ACTION; s_joy_customize_buttons_action.generic.type = MTYPE_ACTION;
@ -1857,22 +2072,6 @@ MouseSpeedFunc(void *unused)
Cvar_SetValue("sensitivity", s_options_sensitivity_slider.curvalue / 2.0F); Cvar_SetValue("sensitivity", s_options_sensitivity_slider.curvalue / 2.0F);
} }
static float
ClampCvar(float min, float max, float value)
{
if (value < min)
{
return min;
}
if (value > max)
{
return max;
}
return value;
}
static void static void
ControlsSetMenuItemValues(void) ControlsSetMenuItemValues(void)
{ {
@ -5525,6 +5724,7 @@ M_Init(void)
Cmd_AddCommand("menu_options", M_Menu_Options_f); Cmd_AddCommand("menu_options", M_Menu_Options_f);
Cmd_AddCommand("menu_keys", M_Menu_Keys_f); Cmd_AddCommand("menu_keys", M_Menu_Keys_f);
Cmd_AddCommand("menu_joy", M_Menu_Joy_f); Cmd_AddCommand("menu_joy", M_Menu_Joy_f);
Cmd_AddCommand("menu_gyro", M_Menu_Gyro_f);
Cmd_AddCommand("menu_buttons", M_Menu_ControllerButtons_f); Cmd_AddCommand("menu_buttons", M_Menu_ControllerButtons_f);
Cmd_AddCommand("menu_altbuttons", M_Menu_ControllerAltButtons_f); Cmd_AddCommand("menu_altbuttons", M_Menu_ControllerAltButtons_f);
Cmd_AddCommand("menu_quit", M_Menu_Quit_f); Cmd_AddCommand("menu_quit", M_Menu_Quit_f);