+#endif
+
+float VID_JoyState_GetAxis(const vid_joystate_t *joystate, int axis, float sensitivity, float deadzone)
+{
+ float value;
+ value = (axis >= 0 && axis < MAXJOYAXIS) ? joystate->axis[axis] : 0.0f;
+ value = value > deadzone ? (value - deadzone) : (value < -deadzone ? (value + deadzone) : 0.0f);
+ value *= deadzone > 0 ? (1.0f / (1.0f - deadzone)) : 1.0f;
+ value = bound(-1, value, 1);
+ return value * sensitivity;
+}
+
+qboolean VID_JoyBlockEmulatedKeys(int keycode)
+{
+ int j;
+ vid_joystate_t joystate;
+
+ if (!joy_axiskeyevents.integer)
+ return false;
+ if (vid_joystate.is360)
+ return false;
+ if (keycode != K_UPARROW && keycode != K_DOWNARROW && keycode != K_RIGHTARROW && keycode != K_LEFTARROW)
+ return false;
+
+ // block system-generated key events for arrow keys if we're emulating the arrow keys ourselves
+ VID_BuildJoyState(&joystate);
+ for (j = 32;j < 36;j++)
+ if (vid_joystate.button[j] || joystate.button[j])
+ return true;
+
+ return false;
+}
+
+void VID_Shared_BuildJoyState_Begin(vid_joystate_t *joystate)
+{
+#ifdef WIN32
+ xinput_state_t xinputstate;
+#endif
+ memset(joystate, 0, sizeof(*joystate));
+#ifdef WIN32
+ if (vid_xinputindex >= 0 && qXInputGetState && qXInputGetState(vid_xinputindex, &xinputstate) == S_OK)
+ {
+ joystate->is360 = true;
+ joystate->button[ 0] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0;
+ joystate->button[ 1] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0;
+ joystate->button[ 2] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0;
+ joystate->button[ 3] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0;
+ joystate->button[ 4] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_START) != 0;
+ joystate->button[ 5] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) != 0;
+ joystate->button[ 6] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0;
+ joystate->button[ 7] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0;
+ joystate->button[ 8] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0;
+ joystate->button[ 9] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0;
+ joystate->button[10] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0;
+ joystate->button[11] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0;
+ joystate->button[12] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0;
+ joystate->button[13] = (xinputstate.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0;
+ joystate->button[14] = xinputstate.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
+ joystate->button[15] = xinputstate.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
+ joystate->button[16] = xinputstate.Gamepad.sThumbLY < -16384;
+ joystate->button[17] = xinputstate.Gamepad.sThumbLY > 16384;
+ joystate->button[18] = xinputstate.Gamepad.sThumbLX < -16384;
+ joystate->button[19] = xinputstate.Gamepad.sThumbLX > 16384;
+ joystate->button[20] = xinputstate.Gamepad.sThumbRY < -16384;
+ joystate->button[21] = xinputstate.Gamepad.sThumbRY > 16384;
+ joystate->button[22] = xinputstate.Gamepad.sThumbRX < -16384;
+ joystate->button[23] = xinputstate.Gamepad.sThumbRX > 16384;
+ joystate->axis[ 4] = xinputstate.Gamepad.bLeftTrigger * (1.0f / 255.0f);
+ joystate->axis[ 5] = xinputstate.Gamepad.bRightTrigger * (1.0f / 255.0f);
+ joystate->axis[ 0] = xinputstate.Gamepad.sThumbLX * (1.0f / 32767.0f);
+ joystate->axis[ 1] = xinputstate.Gamepad.sThumbLY * (1.0f / 32767.0f);
+ joystate->axis[ 2] = xinputstate.Gamepad.sThumbRX * (1.0f / 32767.0f);
+ joystate->axis[ 3] = xinputstate.Gamepad.sThumbRY * (1.0f / 32767.0f);
+ }
+#endif
+}
+
+void VID_Shared_BuildJoyState_Finish(vid_joystate_t *joystate)
+{
+ float f, r;
+ if (joystate->is360)
+ return;
+ // emulate key events for thumbstick
+ f = VID_JoyState_GetAxis(joystate, joy_axisforward.integer, 1, joy_axiskeyevents_deadzone.value) * joy_sensitivityforward.value;
+ r = VID_JoyState_GetAxis(joystate, joy_axisside.integer , 1, joy_axiskeyevents_deadzone.value) * joy_sensitivityside.value;
+#if MAXJOYBUTTON != 36
+#error this code must be updated if MAXJOYBUTTON changes!
+#endif
+ joystate->button[32] = f > 0.0f;
+ joystate->button[33] = f < 0.0f;
+ joystate->button[34] = r > 0.0f;
+ joystate->button[35] = r < 0.0f;
+}
+
+static void VID_KeyEventForButton(qboolean oldbutton, qboolean newbutton, int key, double *timer)
+{
+ if (oldbutton)
+ {
+ if (newbutton)
+ {
+ if (realtime >= *timer)
+ {
+ Key_Event(key, 0, true);
+ *timer = realtime + 0.1;
+ }
+ }
+ else
+ {
+ Key_Event(key, 0, false);
+ *timer = 0;
+ }
+ }
+ else
+ {
+ if (newbutton)
+ {
+ Key_Event(key, 0, true);
+ *timer = realtime + 0.5;
+ }
+ }
+}
+
+#if MAXJOYBUTTON != 36
+#error this code must be updated if MAXJOYBUTTON changes!
+#endif
+static int joybuttonkey[MAXJOYBUTTON][2] =
+{
+ {K_JOY1, K_ENTER}, {K_JOY2, K_ESCAPE}, {K_JOY3, 0}, {K_JOY4, 0}, {K_JOY5, 0}, {K_JOY6, 0}, {K_JOY7, 0}, {K_JOY8, 0}, {K_JOY9, 0}, {K_JOY10, 0}, {K_JOY11, 0}, {K_JOY12, 0}, {K_JOY13, 0}, {K_JOY14, 0}, {K_JOY15, 0}, {K_JOY16, 0},
+ {K_AUX1, 0}, {K_AUX2, 0}, {K_AUX3, 0}, {K_AUX4, 0}, {K_AUX5, 0}, {K_AUX6, 0}, {K_AUX7, 0}, {K_AUX8, 0}, {K_AUX9, 0}, {K_AUX10, 0}, {K_AUX11, 0}, {K_AUX12, 0}, {K_AUX13, 0}, {K_AUX14, 0}, {K_AUX15, 0}, {K_AUX16, 0},
+ {K_JOY_UP, K_UPARROW}, {K_JOY_DOWN, K_DOWNARROW}, {K_JOY_RIGHT, K_RIGHTARROW}, {K_JOY_LEFT, K_LEFTARROW},
+};
+
+static int joybuttonkey360[][2] =
+{
+ {K_X360_DPAD_UP, K_UPARROW},
+ {K_X360_DPAD_DOWN, K_DOWNARROW},
+ {K_X360_DPAD_LEFT, K_LEFTARROW},
+ {K_X360_DPAD_RIGHT, K_RIGHTARROW},
+ {K_X360_START, K_ESCAPE},
+ {K_X360_BACK, K_ESCAPE},
+ {K_X360_LEFT_THUMB, 0},
+ {K_X360_RIGHT_THUMB, 0},
+ {K_X360_LEFT_SHOULDER, 0},
+ {K_X360_RIGHT_SHOULDER, 0},
+ {K_X360_A, K_ENTER},
+ {K_X360_B, K_ESCAPE},
+ {K_X360_X, 0},
+ {K_X360_Y, 0},
+ {K_X360_LEFT_TRIGGER, 0},
+ {K_X360_RIGHT_TRIGGER, 0},
+ {K_X360_LEFT_THUMB_DOWN, K_DOWNARROW},
+ {K_X360_LEFT_THUMB_UP, K_UPARROW},
+ {K_X360_LEFT_THUMB_LEFT, K_LEFTARROW},
+ {K_X360_LEFT_THUMB_RIGHT, K_RIGHTARROW},
+ {K_X360_RIGHT_THUMB_DOWN, 0},
+ {K_X360_RIGHT_THUMB_UP, 0},
+ {K_X360_RIGHT_THUMB_LEFT, 0},
+ {K_X360_RIGHT_THUMB_RIGHT, 0},
+};
+
+double vid_joybuttontimer[MAXJOYBUTTON];
+void VID_ApplyJoyState(vid_joystate_t *joystate)
+{
+ int j;
+ int c = joy_axiskeyevents.integer != 0;
+ if (joystate->is360)
+ {
+#if 0
+ // keystrokes (chatpad)
+ // DOES NOT WORK - no driver support in xinput1_3.dll :(
+ xinput_keystroke_t keystroke;
+ while (qXInputGetKeystroke && qXInputGetKeystroke(XUSER_INDEX_ANY, 0, &keystroke) == S_OK)
+ Con_Printf("XInput KeyStroke: VirtualKey %i, Unicode %i, Flags %x, UserIndex %i, HidCode %i\n", keystroke.VirtualKey, keystroke.Unicode, keystroke.Flags, keystroke.UserIndex, keystroke.HidCode);
+#endif
+
+ // emit key events for buttons
+ for (j = 0;j < (int)(sizeof(joybuttonkey360)/sizeof(joybuttonkey360[0]));j++)
+ VID_KeyEventForButton(vid_joystate.button[j] != 0, joystate->button[j] != 0, joybuttonkey360[j][c], &vid_joybuttontimer[j]);
+
+ // axes
+ cl.cmd.forwardmove += VID_JoyState_GetAxis(joystate, joy_x360_axisforward.integer, joy_x360_sensitivityforward.value, joy_x360_deadzoneforward.value) * cl_forwardspeed.value;
+ cl.cmd.sidemove += VID_JoyState_GetAxis(joystate, joy_x360_axisside.integer, joy_x360_sensitivityside.value, joy_x360_deadzoneside.value) * cl_sidespeed.value;
+ cl.cmd.upmove += VID_JoyState_GetAxis(joystate, joy_x360_axisup.integer, joy_x360_sensitivityup.value, joy_x360_deadzoneup.value) * cl_upspeed.value;
+ cl.viewangles[0] += VID_JoyState_GetAxis(joystate, joy_x360_axispitch.integer, joy_x360_sensitivitypitch.value, joy_x360_deadzonepitch.value) * cl.realframetime * cl_pitchspeed.value;
+ cl.viewangles[1] += VID_JoyState_GetAxis(joystate, joy_x360_axisyaw.integer, joy_x360_sensitivityyaw.value, joy_x360_deadzoneyaw.value) * cl.realframetime * cl_yawspeed.value;
+ //cl.viewangles[2] += VID_JoyState_GetAxis(joystate, joy_x360_axisroll.integer, joy_x360_sensitivityroll.value, joy_x360_deadzoneroll.value) * cl.realframetime * cl_rollspeed.value;
+ }
+ else
+ {
+ // emit key events for buttons
+ for (j = 0;j < MAXJOYBUTTON;j++)
+ VID_KeyEventForButton(vid_joystate.button[j] != 0, joystate->button[j] != 0, joybuttonkey[j][c], &vid_joybuttontimer[j]);
+
+ // axes
+ cl.cmd.forwardmove += VID_JoyState_GetAxis(joystate, joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value) * cl_forwardspeed.value;
+ cl.cmd.sidemove += VID_JoyState_GetAxis(joystate, joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value) * cl_sidespeed.value;
+ cl.cmd.upmove += VID_JoyState_GetAxis(joystate, joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value) * cl_upspeed.value;
+ cl.viewangles[0] += VID_JoyState_GetAxis(joystate, joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value) * cl.realframetime * cl_pitchspeed.value;
+ cl.viewangles[1] += VID_JoyState_GetAxis(joystate, joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value) * cl.realframetime * cl_yawspeed.value;
+ //cl.viewangles[2] += VID_JoyState_GetAxis(joystate, joy_axisroll.integer, joy_sensitivityroll.value, joy_deadzoneroll.value) * cl.realframetime * cl_rollspeed.value;
+ }
+
+ vid_joystate = *joystate;
+}