jedi-academy/codemp/win32/win_input_xbox.cpp
2013-04-23 15:21:39 +10:00

309 lines
7 KiB
C++

// win_input.c -- win32 mouse and joystick code
// 02/21/97 JCB Added extended DirectInput code to support external controllers.
// leave this as first line for PCH reasons...
//
// #include "../server/exe_headers.h"
#include <xtl.h>
#include "glw_win_dx8.h"
#include "../client/client.h"
#include "../qcommon/qcommon.h"
#ifdef _JK2MP
#include "../ui/keycodes.h"
#else
#include "../client/keycodes.h"
#endif
#include "win_local.h"
#include "win_input.h"
#define IN_MAX_CONTROLLERS 4
void IN_UIEmptyQueue();
struct inputstate_t
{
struct controller_t
{
HANDLE handle;
XINPUT_STATE state;
XINPUT_FEEDBACK feedback;
};
controller_t controllers[IN_MAX_CONTROLLERS];
};
inputstate_t *in_state = NULL;
/*
=========================================================================
JOYSTICK
=========================================================================
*/
// Process all the insertions and removals, updating handles and such
void IN_ProcessChanges(DWORD dwInsert, DWORD dwRemove)
{
for(int port = 0; port < IN_MAX_CONTROLLERS; ++port)
{
// Close removals.
if( ((1 << port) & dwRemove) && in_state->controllers[port].handle )
{
XInputClose( in_state->controllers[port].handle );
in_state->controllers[port].handle = 0;
IN_PadUnplugged(port);
}
// Open insertions.
if( (1 << port) & dwInsert )
{
in_state->controllers[port].handle = XInputOpen( XDEVICE_TYPE_GAMEPAD, port, XDEVICE_NO_SLOT, NULL );
IN_PadPlugged(port);
}
}
return;
}
/*********
IN_CheckForNoControllers()
If there are no controllers plugged in, the UI
is notified so it can display an appropriate
message.
*********/
void IN_CheckForNoControllers()
{
extern bool noControllersConnected;
if(!noControllersConnected)
{
extern bool wasPlugged[4];
if( !wasPlugged[0] &&
!wasPlugged[1] &&
!wasPlugged[2] &&
!wasPlugged[3] )
{
// Tell the UI that there are no controllers connected
// VM_Call( uivm, UI_CONTROLLER_UNPLUGGED, true, -1);
noControllersConnected = true;
}
}
}
/*
=========================================================================
RUMBLE SUPPORT
=========================================================================
*/
bool IN_RumbleAdjust(int controller, int left, int right)
{
assert(controller >= 0 && controller < IN_MAX_CONTROLLERS);
// Get a device handle for the controller. This may fail.
HANDLE handle = in_state->controllers[controller].handle;
if (!handle) return false;
XINPUT_FEEDBACK* fb = &in_state->controllers[controller].feedback;
// If a prior rumble update is still pending, go away
if (fb->Header.dwStatus == ERROR_IO_PENDING) return false;
fb->Rumble.wLeftMotorSpeed = left;
fb->Rumble.wRightMotorSpeed = right;
return ERROR_IO_PENDING == XInputSetState(handle, fb);
}
/*
=========================================================================
=========================================================================
*/
/*
igBool IN_WindowClose(igWindow *window)
{
SV_Shutdown ("Server quit\n");
CL_Shutdown ();
Com_Shutdown ();
Sys_Quit ();
return true;
}
*/
/*
===========
IN_Shutdown
===========
*/
void IN_Shutdown( void ) {
IN_RumbleShutdown();
delete in_state;
in_state = NULL;
}
/*
===========
IN_Init
===========
*/
void IN_Init( void )
{
in_state = new inputstate_t;
// Initialize support for 4 gamepads
XDEVICE_PREALLOC_TYPE xdpt[] = {
{XDEVICE_TYPE_GAMEPAD, 4},
{XDEVICE_TYPE_MEMORY_UNIT, 1},
{XDEVICE_TYPE_VOICE_MICROPHONE, 1},
{XDEVICE_TYPE_VOICE_HEADPHONE, 1}
};
// Initialize the peripherals. We can only ever
// call XInitDevices once, no matter what.
static bool bInputInitialized = false;
if (!bInputInitialized)
XInitDevices( sizeof(xdpt) / sizeof(XDEVICE_PREALLOC_TYPE), xdpt );
bInputInitialized = true;
// Zero all of our data, including handles
memset(in_state->controllers, 0, sizeof(in_state->controllers));
// Find out the status of all gamepad ports, then open them
IN_ProcessChanges( XGetDevices( XDEVICE_TYPE_GAMEPAD ), 0 );
IN_RumbleInit();
}
static inline float _joyAxisConvert(SHORT x)
{
// Change scale
float y = x / 32767.0;
// Cheesy deadzone
if(fabs(y) < 0.25f)
{
y = 0.0f;
}
return y;
}
// How many controls on the xbox gamepad?
#define IN_NUM_DIGITAL_BUTTONS 8
#define IN_NUM_ANALOG_BUTTONS 8
// Cutoff where the analog buttons are considered to be "pressed"
// This should be smarter.
#define IN_ANALOG_BUTTON_THRESHOLD 64
void IN_UpdateGamepad(int port)
{
// Lookup table to convert the digital buttons to fakeAscii_t, in mask order
const fakeAscii_t digitalXlat[IN_NUM_DIGITAL_BUTTONS] = {
A_JOY5, // DPAD_UP
A_JOY7, // DPAD_DOWN
A_JOY8, // DPAD_LEFT
A_JOY6, // DPAD_LEFT
A_JOY4, // Start
A_JOY1, // Back
A_JOY2, // Left stick
A_JOY3 // Right stick
};
// Lookup table to convet the analog buttons to fakeAscii_t, in DX order
const fakeAscii_t analogXlat[IN_NUM_ANALOG_BUTTONS] = {
A_JOY15, // A
A_JOY14, // B
A_JOY16, // X
A_JOY13, // Y
A_JOY10, // Black
A_JOY9, // White
A_JOY11, // Left trigger
A_JOY12 // Right trigger
};
// Get new state
XINPUT_STATE newState;
XInputGetState( in_state->controllers[port].handle, &newState );
// Get old state
XINPUT_STATE &oldState(in_state->controllers[port].state);
int buttonIdx;
bool oldPressed, newPressed;
// Check all digital buttons first
for (buttonIdx = 0; buttonIdx < IN_NUM_DIGITAL_BUTTONS; ++buttonIdx)
{
oldPressed = oldState.Gamepad.wButtons & (1 << buttonIdx);
newPressed = newState.Gamepad.wButtons & (1 << buttonIdx);
if (oldPressed != newPressed)
IN_CommonJoyPress(port, digitalXlat[buttonIdx], newPressed);
}
// Now check all analog buttons
for (buttonIdx = 0; buttonIdx < IN_NUM_ANALOG_BUTTONS; ++buttonIdx)
{
oldPressed = oldState.Gamepad.bAnalogButtons[buttonIdx] > IN_ANALOG_BUTTON_THRESHOLD;
newPressed = newState.Gamepad.bAnalogButtons[buttonIdx] > IN_ANALOG_BUTTON_THRESHOLD;
if (oldPressed != newPressed)
IN_CommonJoyPress(port, analogXlat[buttonIdx], newPressed);
}
// Update joysticks
_padInfo.joyInfo[0].x = _joyAxisConvert(newState.Gamepad.sThumbLX);
_padInfo.joyInfo[0].y = _joyAxisConvert(newState.Gamepad.sThumbLY);
_padInfo.joyInfo[1].x = _joyAxisConvert(newState.Gamepad.sThumbRX);
_padInfo.joyInfo[1].y = _joyAxisConvert(newState.Gamepad.sThumbRY);
_padInfo.joyInfo[0].valid = _padInfo.joyInfo[1].valid = true;
_padInfo.padId = port;
// Copy state back
oldState = newState;
// Update game
IN_CommonUpdate();
}
/*
==================
IN_Frame
Called every frame, even if not generating commands
==================
*/
//extern int ignoreInputTime;
void IN_Frame (void)
{
if (in_state)
{
// First, check for changes in device status (removed/inserted pads)
DWORD dwInsert, dwRemove;
if( XGetDeviceChanges( XDEVICE_TYPE_GAMEPAD, &dwInsert, &dwRemove ) )
{
IN_ProcessChanges(dwInsert, dwRemove);
}
else
{
IN_CheckForNoControllers();
}
// Generate callbacks for each controller that's plugged in
for (int port = 0; port < IN_MAX_CONTROLLERS; ++port)
if (in_state->controllers[port].handle)
IN_UpdateGamepad(port);
IN_UIEmptyQueue();
IN_RumbleFrame();
}
}