+
+/*
+===============
+IN_StartupJoystick
+===============
+*/
+static void IN_StartupJoystick (void)
+{
+ int numdevs;
+ JOYCAPS jc;
+ MMRESULT mmr;
+ mmr = 0;
+
+ // assume no joystick
+ joy_avail = false;
+
+ // abort startup if user requests no joystick
+// COMMANDLINEOPTION: Windows Input: -nojoy disables joystick support, may be a small speed increase
+ if (COM_CheckParm ("-nojoy"))
+ return;
+
+ // verify joystick driver is present
+ if ((numdevs = joyGetNumDevs ()) == 0)
+ {
+ Con_Print("\njoystick not found -- driver not present\n\n");
+ return;
+ }
+
+ // cycle through the joystick ids for the first valid one
+ for (joy_id=0 ; joy_id<numdevs ; joy_id++)
+ {
+ memset (&ji, 0, sizeof(ji));
+ ji.dwSize = sizeof(ji);
+ ji.dwFlags = JOY_RETURNCENTERED;
+
+ if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
+ break;
+ }
+
+ // abort startup if we didn't find a valid joystick
+ if (mmr != JOYERR_NOERROR)
+ {
+ Con_Printf("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
+ return;
+ }
+
+ // get the capabilities of the selected joystick
+ // abort startup if command fails
+ memset (&jc, 0, sizeof(jc));
+ if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
+ {
+ Con_Printf("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr);
+ return;
+ }
+
+ // save the joystick's number of buttons and POV status
+ joy_numbuttons = jc.wNumButtons;
+ joy_haspov = (jc.wCaps & JOYCAPS_HASPOV) != 0;
+
+ // old button and POV states default to no buttons pressed
+ joy_oldbuttonstate = joy_oldpovstate = 0;
+
+ // mark the joystick as available and advanced initialization not completed
+ // this is needed as cvars are not available during initialization
+
+ joy_avail = true;
+ joy_advancedinit = false;
+
+ Con_Print("\njoystick detected\n\n");
+}
+
+
+/*
+===========
+RawValuePointer
+===========
+*/
+static PDWORD RawValuePointer (int axis)
+{
+ switch (axis)
+ {
+ case JOY_AXIS_X:
+ return &ji.dwXpos;
+ case JOY_AXIS_Y:
+ return &ji.dwYpos;
+ case JOY_AXIS_Z:
+ return &ji.dwZpos;
+ case JOY_AXIS_R:
+ return &ji.dwRpos;
+ case JOY_AXIS_U:
+ return &ji.dwUpos;
+ case JOY_AXIS_V:
+ return &ji.dwVpos;
+ }
+ return NULL; // LordHavoc: hush compiler warning
+}
+
+
+/*
+===========
+Joy_AdvancedUpdate_f
+===========
+*/
+static void Joy_AdvancedUpdate_f (void)
+{
+
+ // called once by IN_ReadJoystick and by user whenever an update is needed
+ // cvars are now available
+ int i;
+ DWORD dwTemp;
+
+ // initialize all the maps
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ {
+ dwAxisMap[i] = AxisNada;
+ dwControlMap[i] = JOY_ABSOLUTE_AXIS;
+ pdwRawValue[i] = RawValuePointer(i);
+ }
+
+ if( joy_advanced.integer == 0)
+ {
+ // default joystick initialization
+ // 2 axes only with joystick control
+ dwAxisMap[JOY_AXIS_X] = AxisTurn;
+ // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
+ dwAxisMap[JOY_AXIS_Y] = AxisForward;
+ // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
+ }
+ else
+ {
+ if (strcmp (joy_name.string, "joystick") != 0)
+ {
+ // notify user of advanced controller
+ Con_Printf("\n%s configured\n\n", joy_name.string);
+ }
+
+ // advanced initialization here
+ // data supplied by user via joy_axisn cvars
+ dwTemp = (DWORD) joy_advaxisx.value;
+ dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisy.value;
+ dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisz.value;
+ dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisr.value;
+ dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisu.value;
+ dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
+ dwTemp = (DWORD) joy_advaxisv.value;
+ dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
+ dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
+ }
+
+ // compute the axes to collect from DirectInput
+ joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ {
+ if (dwAxisMap[i] != AxisNada)
+ {
+ joy_flags |= dwAxisFlags[i];
+ }
+ }
+}
+
+/*
+===============
+IN_ReadJoystick
+===============
+*/
+static qboolean IN_ReadJoystick (void)
+{
+
+ memset (&ji, 0, sizeof(ji));
+ ji.dwSize = sizeof(ji);
+ ji.dwFlags = joy_flags;
+
+ if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
+ {
+ // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
+ // rather than having 32768 be the zero point, they have the zero point at 32668
+ // go figure -- anyway, now we get the full resolution out of the device
+ if (joy_wwhack1.integer != 0.0)
+ {
+ ji.dwUpos += 100;
+ }
+ return true;
+ }
+ else
+ {
+ // read error occurred
+ // turning off the joystick seems too harsh for 1 read error,
+ // but what should be done?
+ return false;
+ }
+}
+
+/*
+===========
+ IN_JoystickGetAxisNum
+===========
+*/
+
+int IN_JoystickGetAxisNum(int ControlListType)
+{
+ int i;
+
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ if (dwAxisMap[i] == (DWORD) ControlListType)
+ return i;
+ return -1;
+}
+
+/*
+===========
+ IN_JoystickGetAxis
+===========
+*/
+static double IN_JoystickGetAxis(int axis, double sensitivity, double deadzone)
+{
+ float fAxisValue, fTemp;
+
+ if (!joy_avail || axis < 0 || axis >= JOY_MAX_AXES)
+ return 0; // no such axis on this joystick
+
+ // get the floating point zero-centered, potentially-inverted data for the current axis
+ fAxisValue = (float) *pdwRawValue[axis];
+
+ // move centerpoint to zero
+ fAxisValue -= 32768.0;
+
+ if (joy_wwhack2.integer != 0.0)
+ {
+ if (dwAxisMap[axis] == AxisTurn)
+ {
+ // this is a special formula for the Logitech WingMan Warrior
+ // y=ax^b; where a = 300 and b = 1.3
+ // also x values are in increments of 800 (so this is factored out)
+ // then bounds check result to level out excessively high spin rates
+ fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
+ if (fTemp > 14000.0)
+ fTemp = 14000.0;
+ // restore direction information
+ fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
+ }
+ }
+
+ // convert range from -32768..32767 to -1..1
+ fAxisValue /= 32768.0;
+
+ // deadzone around center
+ if (fabs(fAxisValue) < deadzone)
+ return 0;
+
+ // apply sensitivity
+ return fAxisValue * sensitivity;
+}
+
+/*
+===========
+ IN_JoystickKeyeventForAxis
+===========
+*/
+
+static void IN_JoystickKeyeventForAxis(int axis, int key_pos, int key_neg)
+{
+ double joytime;
+
+ if (axis < 0 || axis >= JOY_MAX_AXES)
+ return; // no such axis on this joystick
+
+ joytime = Sys_DoubleTime();
+ // no key event, continuous keydown event
+ if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
+ {
+ if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
+ {
+ //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
+ Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
+ joy_axescache[axis].keytime = joytime + 0.5 / 20;
+ }
+ return;
+ }
+ // generate key up event
+ if (joy_axescache[axis].oldmove)
+ {
+ //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg), 1, cl.time);
+ Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, 0, 0);
+ }
+ // generate key down event
+ if (joy_axescache[axis].move)
+ {
+ //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
+ Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
+ joy_axescache[axis].keytime = joytime + 0.5;
+ }
+}
+
+/*
+===========
+ IN_JoystickBlockDoubledKeyEvents
+===========
+*/
+
+static qboolean IN_ReadJoystick (void);
+static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
+{
+ int axis;
+
+ if (!joy_axiskeyevents.integer)
+ return false;
+
+ // block keyevent if it's going to be provided by joystick keyevent system
+ if (joy_avail)
+ {
+ // collect the joystick data, if possible
+ if (IN_ReadJoystick() != true)
+ return false;
+ axis = IN_JoystickGetAxisNum(AxisForward);
+ if (keycode == K_UPARROW || keycode == K_DOWNARROW)
+ if (IN_JoystickGetAxis(axis, 1, 0.01) || joy_axescache[axis].move || joy_axescache[axis].oldmove)
+ return true;
+ axis = IN_JoystickGetAxisNum(AxisSide);
+ if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW)
+ if (IN_JoystickGetAxis(axis, 1, 0.01) || joy_axescache[axis].move || joy_axescache[axis].oldmove)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+===========
+ IN_JoyMove
+===========
+*/
+static void IN_JoyMove (void)
+{
+ float speed, aspeed;
+ float fAxisValue;
+ int i, mouselook = (in_mlook.state & 1) || freelook.integer, AxisForwardIndex = -1, AxisSideIndex = -1;
+
+ // complete initialization if first time in
+ // this is needed as cvars are not available at initialization time
+ if( joy_advancedinit != true )
+ {
+ Joy_AdvancedUpdate_f();
+ joy_advancedinit = true;
+ }
+
+ if (joy_avail)
+ {
+ int i, key_index;
+ DWORD buttonstate, povstate;
+
+ // loop through the joystick buttons
+ // key a joystick event or auxillary event for higher number buttons for each state change
+ buttonstate = ji.dwButtons;
+ for (i=0 ; i < (int) joy_numbuttons ; i++)
+ {
+ if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
+ {
+ key_index = (i < 16) ? K_JOY1 : K_AUX1;
+ Key_Event (key_index + i, 0, true);
+ }
+ if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
+ {
+ key_index = (i < 16) ? K_JOY1 : K_AUX1;
+ Key_Event (key_index + i, 0, false);
+ }
+ }
+ joy_oldbuttonstate = buttonstate;
+
+ if (joy_haspov)
+ {
+ // convert POV information into 4 bits of state information
+ // this avoids any potential problems related to moving from one
+ // direction to another without going through the center position
+ povstate = 0;
+ if(ji.dwPOV != JOY_POVCENTERED)
+ {
+ if (ji.dwPOV == JOY_POVFORWARD)
+ povstate |= 0x01;
+ if (ji.dwPOV == JOY_POVRIGHT)
+ povstate |= 0x02;
+ if (ji.dwPOV == JOY_POVBACKWARD)
+ povstate |= 0x04;
+ if (ji.dwPOV == JOY_POVLEFT)
+ povstate |= 0x08;
+ }
+ // determine which bits have changed and key an auxillary event for each change
+ for (i=0 ; i < 4 ; i++)
+ {
+ if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
+ {
+ Key_Event (K_AUX29 + i, 0, true);
+ }
+
+ if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
+ {
+ Key_Event (K_AUX29 + i, 0, false);
+ }
+ }
+ joy_oldpovstate = povstate;
+ }
+ }
+
+ // verify joystick is available and that the user wants to use it
+ if (!joy_avail || !in_joystick.integer)
+ {
+ return;
+ }
+
+ // collect the joystick data, if possible
+ if (IN_ReadJoystick () != true)
+ {
+ return;
+ }
+
+ if (in_speed.state & 1)
+ speed = cl_movespeedkey.value;
+ else
+ speed = 1;
+ // LordHavoc: viewzoom affects sensitivity for sniping
+ aspeed = speed * cl.realframetime * cl.viewzoom;
+
+ // loop through the axes
+ for (i = 0; i < JOY_MAX_AXES; i++)
+ {
+ // convert axis to real move
+ switch (dwAxisMap[i])
+ {
+ case AxisForward:
+ if (AxisForwardIndex < 0)
+ AxisForwardIndex = i;
+ if ((joy_advanced.integer == 0) && mouselook)
+ {
+ // user wants forward control to become look control
+ fAxisValue = IN_JoystickGetAxis(i, joy_pitchsensitivity.value, joy_pitchthreshold.value);
+ if (fAxisValue != 0)
+ {
+ // if mouse invert is on, invert the joystick pitch value
+ // only absolute control support here (joy_advanced is false)
+ if (m_pitch.value < 0.0)
+ cl.viewangles[PITCH] -= fAxisValue * aspeed * cl_pitchspeed.value;
+ else
+ cl.viewangles[PITCH] += fAxisValue * aspeed * cl_pitchspeed.value;
+ V_StopPitchDrift();
+ }
+ else
+ {
+ // no pitch movement
+ // disable pitch return-to-center unless requested by user
+ // *** this code can be removed when the lookspring bug is fixed
+ // *** the bug always has the lookspring feature on
+ if (lookspring.value == 0.0)
+ V_StopPitchDrift();
+ }
+ }
+ else
+ {
+ // user wants forward control to be forward control
+ fAxisValue = IN_JoystickGetAxis(i, joy_forwardsensitivity.value, joy_forwardthreshold.value);
+ cl.cmd.forwardmove += fAxisValue * speed * cl_forwardspeed.value;
+ }
+ break;
+
+ case AxisSide:
+ if (AxisSideIndex < 0)
+ AxisSideIndex = i;
+ fAxisValue = IN_JoystickGetAxis(i, joy_sidesensitivity.value, joy_sidethreshold.value);
+ cl.cmd.sidemove += fAxisValue * speed * cl_sidespeed.value;
+ break;
+
+ case AxisTurn:
+ if ((in_strafe.state & 1) || (lookstrafe.integer && mouselook))
+ {
+ // user wants turn control to become side control
+ fAxisValue = IN_JoystickGetAxis(i, joy_sidesensitivity.value, joy_sidethreshold.value);
+ cl.cmd.sidemove -= fAxisValue * speed * cl_sidespeed.value;
+ }
+ else
+ {
+ // user wants turn control to be turn control
+ fAxisValue = IN_JoystickGetAxis(i, joy_yawsensitivity.value, joy_yawthreshold.value);
+ if (dwControlMap[i] == JOY_ABSOLUTE_AXIS)
+ cl.viewangles[YAW] += fAxisValue * aspeed * cl_yawspeed.value;
+ else
+ cl.viewangles[YAW] += fAxisValue * speed * 180.0;
+ }
+ break;
+
+ case AxisLook:
+ fAxisValue = IN_JoystickGetAxis(i, joy_pitchsensitivity.value, joy_pitchthreshold.value);
+ if (mouselook)
+ {
+ if (fAxisValue != 0)
+ {
+ // pitch movement detected and pitch movement desired by user
+ if (dwControlMap[i] == JOY_ABSOLUTE_AXIS)
+ cl.viewangles[PITCH] += fAxisValue * aspeed * cl_pitchspeed.value;
+ else
+ cl.viewangles[PITCH] += fAxisValue * speed * 180.0;
+ V_StopPitchDrift();
+ }
+ else
+ {
+ // no pitch movement
+ // disable pitch return-to-center unless requested by user
+ // *** this code can be removed when the lookspring bug is fixed
+ // *** the bug always has the lookspring feature on
+ if(lookspring.integer == 0)
+ V_StopPitchDrift();
+ }
+ }
+ break;
+
+ default:
+ fAxisValue = IN_JoystickGetAxis(i, 1, 0.01);
+ break;
+ }
+
+ // cache for keyevents
+ joy_axescache[i].oldmove = joy_axescache[i].move;
+ joy_axescache[i].move = IN_JoystickGetAxis(i, 1, 0.01);
+ }
+
+ // run keyevents
+ if (joy_axiskeyevents.integer)
+ {
+ IN_JoystickKeyeventForAxis(AxisForwardIndex, K_DOWNARROW, K_UPARROW);
+ IN_JoystickKeyeventForAxis(AxisSideIndex, K_RIGHTARROW, K_LEFTARROW);
+ }
+}
+
+static void IN_Init(void)
+{
+ uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
+
+ // joystick variables
+ Cvar_RegisterVariable (&in_joystick);
+ Cvar_RegisterVariable (&joy_name);
+ Cvar_RegisterVariable (&joy_advanced);
+ Cvar_RegisterVariable (&joy_advaxisx);
+ Cvar_RegisterVariable (&joy_advaxisy);
+ Cvar_RegisterVariable (&joy_advaxisz);
+ Cvar_RegisterVariable (&joy_advaxisr);
+ Cvar_RegisterVariable (&joy_advaxisu);
+ Cvar_RegisterVariable (&joy_advaxisv);
+ Cvar_RegisterVariable (&joy_forwardthreshold);
+ Cvar_RegisterVariable (&joy_sidethreshold);
+ Cvar_RegisterVariable (&joy_pitchthreshold);
+ Cvar_RegisterVariable (&joy_yawthreshold);
+ Cvar_RegisterVariable (&joy_forwardsensitivity);
+ Cvar_RegisterVariable (&joy_sidesensitivity);
+ Cvar_RegisterVariable (&joy_pitchsensitivity);
+ Cvar_RegisterVariable (&joy_yawsensitivity);
+ Cvar_RegisterVariable (&joy_wwhack1);
+ Cvar_RegisterVariable (&joy_wwhack2);
+ Cvar_RegisterVariable (&joy_axiskeyevents);
+ Cvar_RegisterVariable (&vid_forcerefreshrate);
+ Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f, "applies current joyadv* cvar settings to the joystick driver");
+}
+
+static void IN_Shutdown(void)
+{
+#ifdef SUPPORTDIRECTX
+ if (g_pMouse)
+ IDirectInputDevice_Release(g_pMouse);
+ g_pMouse = NULL;
+
+ if (g_pdi)
+ IDirectInput_Release(g_pdi);
+ g_pdi = NULL;
+#endif
+}
+
+size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
+{
+ int i;
+ size_t k;
+ DEVMODE thismode;
+
+ thismode.dmSize = sizeof(thismode);
+ thismode.dmDriverExtra = 0;
+ k = 0;
+ for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
+ {
+ if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
+ {
+ Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
+ continue;
+ }
+ if(k >= maxcount)
+ break;
+ modes[k].width = thismode.dmPelsWidth;
+ modes[k].height = thismode.dmPelsHeight;
+ modes[k].bpp = thismode.dmBitsPerPel;
+ modes[k].refreshrate = thismode.dmDisplayFrequency;
+ modes[k].pixelheight_num = 1;
+ modes[k].pixelheight_denom = 1; // Win32 apparently does not provide this (FIXME)
+ ++k;