mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-16 09:21:36 +00:00
1334 lines
34 KiB
C++
1334 lines
34 KiB
C++
/*
|
|
**
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2005-2016 Randy Heit
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
#include "i_input.h"
|
|
#include "d_eventbase.h"
|
|
#include "templates.h"
|
|
#include "gameconfigfile.h"
|
|
#include "m_argv.h"
|
|
#include "cmdlib.h"
|
|
#include "keydef.h"
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
#define DEFAULT_DEADZONE 0.25f
|
|
#define STATUS_SWITCH_TIME 3
|
|
|
|
#define VID_PLAY_COM 0x0b43
|
|
#define PID_EMS_USB2_PS2_CONTROLLER_ADAPTER 0x0003
|
|
|
|
#define VID_GREENASIA 0x0e8f
|
|
#define PID_DRAGON_PS2_CONTROLLER_ADAPTER 0x0003
|
|
#define PID_PANTHERLORD_PS2_CONTROLLER_ADAPTER 0x0029
|
|
|
|
#define VID_PERSONAL_COMMUNICATION_SYSTEMS 0x0810
|
|
#define PID_TWIN_USB_VIBRATION_GAMEPAD 0x0001
|
|
|
|
#define STATUS_DISCONNECTED 0xFF
|
|
#define STATUS_DIGITAL 0x41
|
|
#define STATUS_ANALOG 0x73
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
enum EAdapterType
|
|
{
|
|
ADAPTER_EMSUSB2,
|
|
ADAPTER_DragonPlus,
|
|
ADAPTER_PantherLord,
|
|
ADAPTER_TwinUSB,
|
|
ADAPTER_Unknown
|
|
};
|
|
|
|
struct FAdapterHandle
|
|
{
|
|
HANDLE Handle;
|
|
EAdapterType Type;
|
|
int ControllerNumber;
|
|
FString DeviceID;
|
|
};
|
|
|
|
class FRawPS2Controller : public IJoystickConfig
|
|
{
|
|
public:
|
|
FRawPS2Controller(HANDLE handle, EAdapterType type, int sequence, int controller, FString devid);
|
|
~FRawPS2Controller();
|
|
|
|
bool ProcessInput(RAWHID *raw, int code);
|
|
void AddAxes(float axes[NUM_JOYAXIS]);
|
|
bool IsConnected() { return Connected; }
|
|
|
|
// IJoystickConfig interface
|
|
FString GetName();
|
|
float GetSensitivity();
|
|
virtual void SetSensitivity(float scale);
|
|
|
|
int GetNumAxes();
|
|
float GetAxisDeadZone(int axis);
|
|
EJoyAxis GetAxisMap(int axis);
|
|
const char *GetAxisName(int axis);
|
|
float GetAxisScale(int axis);
|
|
|
|
void SetAxisDeadZone(int axis, float deadzone);
|
|
void SetAxisMap(int axis, EJoyAxis gameaxis);
|
|
void SetAxisScale(int axis, float scale);
|
|
|
|
bool IsSensitivityDefault();
|
|
bool IsAxisDeadZoneDefault(int axis);
|
|
bool IsAxisMapDefault(int axis);
|
|
bool IsAxisScaleDefault(int axis);
|
|
|
|
void SetDefaultConfig();
|
|
FString GetIdentifier();
|
|
|
|
protected:
|
|
struct AxisInfo
|
|
{
|
|
float Value;
|
|
float DeadZone;
|
|
float Multiplier;
|
|
EJoyAxis GameAxis;
|
|
uint8_t ButtonValue;
|
|
};
|
|
struct DefaultAxisConfig
|
|
{
|
|
EJoyAxis GameAxis;
|
|
float Multiplier;
|
|
};
|
|
enum
|
|
{
|
|
AXIS_ThumbLX,
|
|
AXIS_ThumbLY,
|
|
AXIS_ThumbRX,
|
|
AXIS_ThumbRY,
|
|
NUM_AXES
|
|
};
|
|
|
|
HANDLE Handle;
|
|
FString DeviceID;
|
|
int ControllerNumber;
|
|
int Sequence;
|
|
DWORD DisconnectCount;
|
|
EAdapterType Type;
|
|
float Multiplier;
|
|
AxisInfo Axes[NUM_AXES];
|
|
static DefaultAxisConfig DefaultAxes[NUM_AXES];
|
|
int LastButtons;
|
|
bool Connected;
|
|
bool Marked;
|
|
bool Active;
|
|
|
|
void Attached();
|
|
void Detached();
|
|
void NeutralInput();
|
|
|
|
static void ProcessThumbstick(int value1, AxisInfo *axis1, int value2, AxisInfo *axis2, int base);
|
|
|
|
friend class FRawPS2Manager;
|
|
};
|
|
|
|
class FRawPS2Manager : public FJoystickCollection
|
|
{
|
|
public:
|
|
FRawPS2Manager();
|
|
~FRawPS2Manager();
|
|
|
|
bool GetDevice();
|
|
bool ProcessRawInput(RAWINPUT *raw, int code);
|
|
void AddAxes(float axes[NUM_JOYAXIS]);
|
|
void GetDevices(TArray<IJoystickConfig *> &sticks);
|
|
IJoystickConfig *Rescan();
|
|
|
|
protected:
|
|
TArray<FRawPS2Controller *> Devices;
|
|
bool Registered;
|
|
|
|
void DoRegister();
|
|
FRawPS2Controller *EnumDevices();
|
|
static int DeviceSort(const void *a, const void *b);
|
|
};
|
|
|
|
// Each entry is an offset to the corresponding data field in the
|
|
// adapter's data packet. Some of these fields are optional and are
|
|
// assigned negative values if the adapter does not include them.
|
|
struct PS2Descriptor
|
|
{
|
|
const char *AdapterName;
|
|
uint8_t PacketSize;
|
|
int8_t ControllerNumber;
|
|
int8_t ControllerStatus;
|
|
uint8_t LeftX;
|
|
uint8_t LeftY;
|
|
uint8_t RightX;
|
|
uint8_t RightY;
|
|
int8_t DPadHat;
|
|
uint8_t DPadButtonsNibble:1;
|
|
int8_t DPadButtons:7; // up, right, down, left
|
|
uint8_t ButtonSet1:7; // triangle, circle, cross, square
|
|
uint8_t ButtonSet1Nibble:1;
|
|
uint8_t ButtonSet2:7; // L2, R2, L1, R1
|
|
uint8_t ButtonSet2Nibble:1;
|
|
uint8_t ButtonSet3:7; // select, start, lthumb, rthumb
|
|
uint8_t ButtonSet3Nibble:1;
|
|
};
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
extern HWND Window;
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
CUSTOM_CVAR(Bool, joy_ps2raw, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL)
|
|
{
|
|
I_StartupRawPS2();
|
|
event_t ev = { EV_DeviceChange };
|
|
D_PostEvent(&ev);
|
|
}
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
static const int ButtonKeys[16] =
|
|
{
|
|
KEY_PAD_DPAD_UP,
|
|
KEY_PAD_DPAD_RIGHT,
|
|
KEY_PAD_DPAD_DOWN,
|
|
KEY_PAD_DPAD_LEFT,
|
|
|
|
KEY_PAD_Y, // triangle
|
|
KEY_PAD_B, // circle
|
|
KEY_PAD_A, // cross
|
|
KEY_PAD_X, // square
|
|
|
|
KEY_PAD_LTRIGGER, // L2
|
|
KEY_PAD_RTRIGGER, // R2
|
|
KEY_PAD_LSHOULDER, // L1
|
|
KEY_PAD_RSHOULDER, // R1
|
|
|
|
KEY_PAD_BACK, // select
|
|
KEY_PAD_START,
|
|
KEY_PAD_LTHUMB,
|
|
KEY_PAD_RTHUMB
|
|
};
|
|
|
|
static const uint8_t HatButtons[16] =
|
|
{
|
|
1, 1+2, 2, 2+4, 4, 4+8, 8, 8+1,
|
|
0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
static const PS2Descriptor Descriptors[ADAPTER_Unknown] =
|
|
{
|
|
{ // ADAPTER_EMS_USB2
|
|
"EMS USB2 Adapter",
|
|
8, // packet size
|
|
0, // controller number
|
|
7, // controller status
|
|
3, // left x
|
|
4, // left y
|
|
5, // right x
|
|
6, // right y
|
|
-1, // hat
|
|
1, // d-pad buttons nibble
|
|
2, // d-pad buttons
|
|
1, // buttons 1
|
|
0, // buttons 1 nibble
|
|
1, // buttons 2
|
|
1, // buttons 2 nibble
|
|
2, // buttons 3
|
|
0, // buttons 3 nibble
|
|
},
|
|
{ // ADAPTER_DragonPlus
|
|
"Dragon+ Adapter",
|
|
9, // packet size
|
|
-1, // controller number
|
|
-1, // controller status
|
|
3, // left x
|
|
4, // left y
|
|
2, // right x
|
|
1, // right y
|
|
6, // hat
|
|
0, // d-pad buttons nibble
|
|
-1, // d-pad buttons
|
|
6, // buttons 1
|
|
1, // buttons 1 nibble
|
|
7, // buttons 2
|
|
0, // buttons 2 nibble
|
|
7, // buttons 3
|
|
1, // buttons 3 nibble
|
|
},
|
|
{ // ADAPTER_PantherLord
|
|
// Windows indentifies it as a ga451-USB device, but I call it a PantherLord
|
|
// because there's a box in the middle of the cable with a PantherLord sticker
|
|
// on it.
|
|
"PantherLord Adapter",
|
|
9, // packet size
|
|
-1, // controller number
|
|
-1, // controller status
|
|
4, // left x
|
|
5, // left y
|
|
3, // right x
|
|
2, // right y
|
|
-1, // hat ... This device has both a hat and d-pad buttons.
|
|
0, // d-pad buttons nibble Since buttons are better, we just read those.
|
|
8, // d-pad buttons
|
|
6, // buttons 1
|
|
1, // buttons 1 nibble
|
|
7, // buttons 2
|
|
0, // buttons 2 nibble
|
|
7, // buttons 3
|
|
1, // buttons 3 nibble
|
|
},
|
|
{ // ADAPTER_TwinUSB
|
|
"Twin USB Gamepad",
|
|
8, // packet size
|
|
0, // controller number
|
|
-1, // controller status
|
|
3, // left x
|
|
4, // left y
|
|
2, // right x
|
|
1, // right y
|
|
5, // hat
|
|
0, // d-pad buttons nibble
|
|
-1, // d-pad buttons
|
|
5, // buttons 1
|
|
1, // buttons 1 nibble
|
|
6, // buttons 2
|
|
0, // buttons 2 nibble
|
|
6, // buttons 3
|
|
1, // buttons 3 nibble
|
|
}
|
|
};
|
|
|
|
static const char *AxisNames[] =
|
|
{
|
|
"Left Thumb X Axis",
|
|
"Left Thumb Y Axis",
|
|
"Right Thumb X Axis",
|
|
"Right Thumb Y Axis",
|
|
};
|
|
|
|
FRawPS2Controller::DefaultAxisConfig FRawPS2Controller::DefaultAxes[NUM_AXES] =
|
|
{
|
|
// Game axis, multiplier
|
|
{ JOYAXIS_Side, 1 }, // ThumbLX
|
|
{ JOYAXIS_Forward, 1 }, // ThumbLY
|
|
{ JOYAXIS_Yaw, 1 }, // ThumbRX
|
|
{ JOYAXIS_Pitch, 0.75 }, // ThumbRY
|
|
};
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller - Constructor
|
|
//
|
|
// handle: The Raw Input handle for this device
|
|
// type: The adapter type
|
|
// sequence: The seqeunce number, for attaching numbers to names
|
|
// controller: The controller to check, for multi-controller adapters
|
|
//
|
|
//==========================================================================
|
|
|
|
FRawPS2Controller::FRawPS2Controller(HANDLE handle, EAdapterType type, int sequence, int controller, FString devid)
|
|
{
|
|
Handle = handle;
|
|
Type = type;
|
|
ControllerNumber = controller;
|
|
Sequence = sequence;
|
|
DeviceID = devid;
|
|
|
|
// The EMS USB2 controller provides attachment status. The others do not.
|
|
Connected = (Descriptors[type].ControllerStatus < 0);
|
|
if (Connected)
|
|
{
|
|
Attached();
|
|
}
|
|
|
|
M_LoadJoystickConfig(this);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller - Destructor
|
|
//
|
|
//==========================================================================
|
|
|
|
FRawPS2Controller::~FRawPS2Controller()
|
|
{
|
|
// Make sure to send any key ups before destroying this.
|
|
NeutralInput();
|
|
M_SaveJoystickConfig(this);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: ProcessInput
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FRawPS2Controller::ProcessInput(RAWHID *raw, int code)
|
|
{
|
|
// w32api has an incompatible definition of bRawData.
|
|
// (But the version that comes with MinGW64 is fine.)
|
|
#if defined(__GNUC__) && !defined(__MINGW64_VERSION_MAJOR)
|
|
uint8_t *rawdata = &raw->bRawData;
|
|
#else
|
|
uint8_t *rawdata = raw->bRawData;
|
|
#endif
|
|
const PS2Descriptor *desc = &Descriptors[Type];
|
|
bool digital;
|
|
|
|
// Ensure packet size is what we expect.
|
|
if (raw->dwSizeHid != desc->PacketSize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if 0
|
|
// If this is a multi-controller device, check that this packet
|
|
// is for the controller we were created for. We probably don't
|
|
// need to do this, because the multi-controller adapters
|
|
// send data for each controller to seperate device instances.
|
|
if (desc->ControllerNumber >= 0 &&
|
|
raw->bRawData[desc->ControllerNumber] != ControllerNumber)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
// Check for disconnected controller
|
|
if (desc->ControllerStatus >= 0)
|
|
{
|
|
if (rawdata[desc->ControllerStatus] == STATUS_DISCONNECTED)
|
|
{
|
|
// When you press the Analog button on a controller, the EMS
|
|
// USB2 will briefly report the controller as disconnected.
|
|
if (++DisconnectCount < STATUS_SWITCH_TIME)
|
|
{
|
|
NeutralInput();
|
|
return true;
|
|
}
|
|
if (Connected)
|
|
{
|
|
Detached();
|
|
}
|
|
return true;
|
|
}
|
|
if (!Connected)
|
|
{
|
|
Attached();
|
|
}
|
|
}
|
|
DisconnectCount = 0;
|
|
|
|
if (code == RIM_INPUTSINK)
|
|
{
|
|
NeutralInput();
|
|
return true;
|
|
}
|
|
|
|
// Check for digital controller
|
|
digital = false;
|
|
if (desc->ControllerStatus >= 0)
|
|
{
|
|
// The EMS USB2 is nice enough to actually tell us what type of
|
|
// controller is attached.
|
|
digital = (rawdata[desc->ControllerStatus] == STATUS_DIGITAL);
|
|
}
|
|
else
|
|
{
|
|
// The other adapters don't bother to tell us, but we can still
|
|
// make an educated guess. In analog mode, the axes center at 0x80.
|
|
// In digital mode, they center at 0x7F, and the right stick is
|
|
// fixed at center because it gets translated to presses of the
|
|
// four face buttons instead.
|
|
digital = (rawdata[desc->RightX] == 0x7F && rawdata[desc->RightY] == 0x7F);
|
|
}
|
|
|
|
// Convert axes to floating point and cancel out deadzones.
|
|
ProcessThumbstick(rawdata[desc->LeftX], &Axes[AXIS_ThumbLX],
|
|
rawdata[desc->LeftY], &Axes[AXIS_ThumbLY], KEY_PAD_LTHUMB_RIGHT);
|
|
|
|
// If we know we are digital, ignore the right stick.
|
|
if (digital)
|
|
{
|
|
ProcessThumbstick(0x80, &Axes[AXIS_ThumbRX],
|
|
0x80, &Axes[AXIS_ThumbRY], KEY_PAD_RTHUMB_RIGHT);
|
|
}
|
|
else
|
|
{
|
|
ProcessThumbstick(rawdata[desc->RightX], &Axes[AXIS_ThumbRX],
|
|
rawdata[desc->RightY], &Axes[AXIS_ThumbRY], KEY_PAD_RTHUMB_RIGHT);
|
|
}
|
|
|
|
// Generate events for buttons that have changed.
|
|
int buttons = 0;
|
|
|
|
// If we know we are digital, ignore the D-Pad.
|
|
if (!digital)
|
|
{
|
|
if (desc->DPadButtons >= 0)
|
|
{
|
|
buttons = rawdata[desc->DPadButtons] >> (4 * desc->DPadButtonsNibble);
|
|
}
|
|
else if (desc->DPadHat >= 0)
|
|
{
|
|
buttons = HatButtons[rawdata[desc->DPadHat] & 15];
|
|
}
|
|
}
|
|
buttons |= ((rawdata[desc->ButtonSet1] >> (4 * desc->ButtonSet1Nibble)) & 15) << 4;
|
|
buttons |= ((rawdata[desc->ButtonSet2] >> (4 * desc->ButtonSet2Nibble)) & 15) << 8;
|
|
buttons |= ((rawdata[desc->ButtonSet3] >> (4 * desc->ButtonSet3Nibble)) & 15) << 12;
|
|
Joy_GenerateButtonEvents(LastButtons, buttons, 16, ButtonKeys);
|
|
|
|
LastButtons = buttons;
|
|
return true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: ProcessThumbstick STATIC
|
|
//
|
|
// Converts both axie of a thumb stick to floating point, cancels out the
|
|
// deadzone, and generates button up/down events for them.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::ProcessThumbstick(int value1, AxisInfo *axis1, int value2, AxisInfo *axis2, int base)
|
|
{
|
|
uint8_t buttonstate;
|
|
double axisval1, axisval2;
|
|
|
|
axisval1 = value1 * (2.0 / 255) - 1.0;
|
|
axisval2 = value2 * (2.0 / 255) - 1.0;
|
|
axisval1 = Joy_RemoveDeadZone(axisval1, axis1->DeadZone, NULL);
|
|
axisval2 = Joy_RemoveDeadZone(axisval2, axis2->DeadZone, NULL);
|
|
axis1->Value = float(axisval1);
|
|
axis2->Value = float(axisval2);
|
|
|
|
// We store all four buttons in the first axis and ignore the second.
|
|
buttonstate = Joy_XYAxesToButtons(axisval1, axisval2);
|
|
Joy_GenerateButtonEvents(axis1->ButtonValue, buttonstate, 4, base);
|
|
axis1->ButtonValue = buttonstate;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: Attached
|
|
//
|
|
// This controller was just attached. Set all buttons and axes to 0.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::Attached()
|
|
{
|
|
int i;
|
|
|
|
Connected = true;
|
|
DisconnectCount = 0;
|
|
LastButtons = 0;
|
|
for (i = 0; i < NUM_AXES; ++i)
|
|
{
|
|
Axes[i].Value = 0;
|
|
Axes[i].ButtonValue = 0;
|
|
}
|
|
UpdateJoystickMenu(this);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: Detached
|
|
//
|
|
// This controller was just detached. Send button ups for buttons that
|
|
// were pressed the last time we got input from it.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::Detached()
|
|
{
|
|
Connected = false;
|
|
NeutralInput();
|
|
UpdateJoystickMenu(NULL);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: NeutralInput
|
|
//
|
|
// Sets the controller's state to a neutral one. Either because the
|
|
// controller is disconnected or because we are in the background.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::NeutralInput()
|
|
{
|
|
for (int i = 0; i < NUM_AXES; i += 2)
|
|
{
|
|
ProcessThumbstick(0x80, &Axes[i], 0x80, &Axes[i+1], KEY_PAD_LTHUMB_RIGHT + i*2);
|
|
}
|
|
Joy_GenerateButtonEvents(LastButtons, 0, 16, ButtonKeys);
|
|
LastButtons = 0;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: AddAxes
|
|
//
|
|
// Add the values of each axis to the game axes.
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::AddAxes(float axes[NUM_JOYAXIS])
|
|
{
|
|
// Add to game axes.
|
|
for (int i = 0; i < NUM_AXES; ++i)
|
|
{
|
|
axes[Axes[i].GameAxis] -= float(Axes[i].Value * Multiplier * Axes[i].Multiplier);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: SetDefaultConfig
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::SetDefaultConfig()
|
|
{
|
|
Multiplier = 1;
|
|
for (int i = 0; i < NUM_AXES; ++i)
|
|
{
|
|
Axes[i].DeadZone = DEFAULT_DEADZONE;
|
|
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
|
|
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetIdentifier
|
|
//
|
|
//==========================================================================
|
|
|
|
FString FRawPS2Controller::GetIdentifier()
|
|
{
|
|
FString id = "PS2:";
|
|
id += DeviceID;
|
|
return id;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetName
|
|
//
|
|
//==========================================================================
|
|
|
|
FString FRawPS2Controller::GetName()
|
|
{
|
|
FString res = Descriptors[Type].AdapterName;
|
|
if (Sequence != 0)
|
|
{
|
|
res.AppendFormat(" #%d", Sequence);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetSensitivity
|
|
//
|
|
//==========================================================================
|
|
|
|
float FRawPS2Controller::GetSensitivity()
|
|
{
|
|
return Multiplier;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: SetSensitivity
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::SetSensitivity(float scale)
|
|
{
|
|
Multiplier = scale;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: IsSensitivityDefault
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FRawPS2Controller::IsSensitivityDefault()
|
|
{
|
|
return Multiplier == 1;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetNumAxes
|
|
//
|
|
//==========================================================================
|
|
|
|
int FRawPS2Controller::GetNumAxes()
|
|
{
|
|
return NUM_AXES;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetAxisDeadZone
|
|
//
|
|
//==========================================================================
|
|
|
|
float FRawPS2Controller::GetAxisDeadZone(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return Axes[axis].DeadZone;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetAxisMap
|
|
//
|
|
//==========================================================================
|
|
|
|
EJoyAxis FRawPS2Controller::GetAxisMap(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return Axes[axis].GameAxis;
|
|
}
|
|
return JOYAXIS_None;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetAxisName
|
|
//
|
|
//==========================================================================
|
|
|
|
const char *FRawPS2Controller::GetAxisName(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return AxisNames[axis];
|
|
}
|
|
return "Invalid";
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: GetAxisScale
|
|
//
|
|
//==========================================================================
|
|
|
|
float FRawPS2Controller::GetAxisScale(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return Axes[axis].Multiplier;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: SetAxisDeadZone
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::SetAxisDeadZone(int axis, float deadzone)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
Axes[axis].DeadZone = clamp(deadzone, 0.f, 1.f);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: SetAxisMap
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::SetAxisMap(int axis, EJoyAxis gameaxis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
Axes[axis].GameAxis = (unsigned(gameaxis) < NUM_JOYAXIS) ? gameaxis : JOYAXIS_None;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Controller :: SetAxisScale
|
|
//
|
|
//==========================================================================
|
|
|
|
void FRawPS2Controller::SetAxisScale(int axis, float scale)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
Axes[axis].Multiplier = scale;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Controller :: IsAxisDeadZoneDefault
|
|
//
|
|
//===========================================================================
|
|
|
|
bool FRawPS2Controller::IsAxisDeadZoneDefault(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return Axes[axis].DeadZone == DEFAULT_DEADZONE;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Controller :: IsAxisScaleDefault
|
|
//
|
|
//===========================================================================
|
|
|
|
bool FRawPS2Controller::IsAxisScaleDefault(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return Axes[axis].Multiplier == DefaultAxes[axis].Multiplier;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Controller :: IsAxisMapDefault
|
|
//
|
|
//===========================================================================
|
|
|
|
bool FRawPS2Controller::IsAxisMapDefault(int axis)
|
|
{
|
|
if (unsigned(axis) < NUM_AXES)
|
|
{
|
|
return Axes[axis].GameAxis == DefaultAxes[axis].GameAxis;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Manager - Constructor
|
|
//
|
|
//==========================================================================
|
|
|
|
FRawPS2Manager::FRawPS2Manager()
|
|
{
|
|
Registered = false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Manager - Destructor
|
|
//
|
|
//==========================================================================
|
|
|
|
FRawPS2Manager::~FRawPS2Manager()
|
|
{
|
|
for (unsigned i = 0; i < Devices.Size(); ++i)
|
|
{
|
|
if (Devices[i] != NULL)
|
|
{
|
|
delete Devices[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// FRawPS2Manager :: GetDevice
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FRawPS2Manager::GetDevice()
|
|
{
|
|
RAWINPUTDEVICE rid;
|
|
|
|
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
|
|
rid.usUsage = HID_GDP_JOYSTICK;
|
|
rid.dwFlags = RIDEV_INPUTSINK;
|
|
rid.hwndTarget = Window;
|
|
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
|
{
|
|
return false;
|
|
}
|
|
rid.dwFlags = RIDEV_REMOVE;
|
|
rid.hwndTarget = NULL; // Must be NULL for RIDEV_REMOVE.
|
|
RegisterRawInputDevices(&rid, 1, sizeof(rid));
|
|
EnumDevices();
|
|
return true;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: AddAxes
|
|
//
|
|
// Adds the state of all attached device axes to the passed array.
|
|
//
|
|
//===========================================================================
|
|
|
|
void FRawPS2Manager::AddAxes(float axes[NUM_JOYAXIS])
|
|
{
|
|
for (unsigned i = 0; i < Devices.Size(); ++i)
|
|
{
|
|
if (Devices[i]->IsConnected())
|
|
{
|
|
Devices[i]->AddAxes(axes);
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: GetJoysticks
|
|
//
|
|
// Adds the IJoystick interfaces for each device we created to the sticks
|
|
// array, if they are detected as connected.
|
|
//
|
|
//===========================================================================
|
|
|
|
void FRawPS2Manager::GetDevices(TArray<IJoystickConfig *> &sticks)
|
|
{
|
|
for (unsigned i = 0; i < Devices.Size(); ++i)
|
|
{
|
|
if (Devices[i]->IsConnected())
|
|
{
|
|
sticks.Push(Devices[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: ProcessRawInput
|
|
//
|
|
//===========================================================================
|
|
|
|
bool FRawPS2Manager::ProcessRawInput(RAWINPUT *raw, int code)
|
|
{
|
|
if (raw->header.dwType != RIM_TYPEHID)
|
|
{
|
|
return false;
|
|
}
|
|
for (unsigned i = 0; i < Devices.Size(); ++i)
|
|
{
|
|
if (Devices[i]->Handle == raw->header.hDevice)
|
|
{
|
|
if (Devices[i]->ProcessInput(&raw->data.hid, code))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: Rescan
|
|
//
|
|
//===========================================================================
|
|
|
|
IJoystickConfig *FRawPS2Manager::Rescan()
|
|
{
|
|
return EnumDevices();
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: EnumDevices
|
|
//
|
|
// Find out what PS2 adaptors we understand are on the system and create
|
|
// FRawPS2Controller objects for them. May return a pointer to the first new
|
|
// device found.
|
|
//
|
|
//===========================================================================
|
|
|
|
FRawPS2Controller *FRawPS2Manager::EnumDevices()
|
|
{
|
|
UINT nDevices, numDevices;
|
|
RAWINPUTDEVICELIST *devices;
|
|
UINT i, j;
|
|
|
|
if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
if ((devices = (RAWINPUTDEVICELIST *)malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if ((numDevices = GetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1)
|
|
{
|
|
free(devices);
|
|
return NULL;
|
|
}
|
|
|
|
TArray<FAdapterHandle> adapters;
|
|
|
|
for (i = 0; i < numDevices; ++i)
|
|
{
|
|
if (devices[i].dwType == RIM_TYPEHID)
|
|
{
|
|
RID_DEVICE_INFO rdi;
|
|
UINT cbSize;
|
|
|
|
cbSize = rdi.cbSize = sizeof(rdi);
|
|
if ((INT)GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0)
|
|
{
|
|
// All the PS2 adapters report themselves as joysticks.
|
|
// (By comparison, the 360 controller reports itself as a gamepad.)
|
|
if (rdi.hid.usUsagePage == HID_GENERIC_DESKTOP_PAGE &&
|
|
rdi.hid.usUsage == HID_GDP_JOYSTICK)
|
|
{
|
|
EAdapterType type = ADAPTER_Unknown;
|
|
|
|
// Check vendor and product IDs to see if we know what this is.
|
|
if (rdi.hid.dwVendorId == VID_PLAY_COM)
|
|
{
|
|
if (rdi.hid.dwProductId == PID_EMS_USB2_PS2_CONTROLLER_ADAPTER)
|
|
{
|
|
type = ADAPTER_EMSUSB2;
|
|
}
|
|
}
|
|
else if (rdi.hid.dwVendorId == VID_GREENASIA)
|
|
{
|
|
if (rdi.hid.dwProductId == PID_DRAGON_PS2_CONTROLLER_ADAPTER)
|
|
{
|
|
type = ADAPTER_DragonPlus;
|
|
}
|
|
else if (rdi.hid.dwProductId == PID_PANTHERLORD_PS2_CONTROLLER_ADAPTER)
|
|
{
|
|
type = ADAPTER_PantherLord;
|
|
}
|
|
}
|
|
else if (rdi.hid.dwVendorId == VID_PERSONAL_COMMUNICATION_SYSTEMS)
|
|
{
|
|
if (rdi.hid.dwProductId == PID_TWIN_USB_VIBRATION_GAMEPAD)
|
|
{
|
|
type = ADAPTER_TwinUSB;
|
|
}
|
|
}
|
|
if (type != ADAPTER_Unknown)
|
|
{
|
|
// Get the device name. Part of this is a path under HKLM\CurrentControlSet\Enum
|
|
// with \ characters replaced by # characters. It is not a human-friendly name.
|
|
// The layout for the name string is:
|
|
// <Enumerator>#<Device ID>#<Device Interface Class GUID>
|
|
// The Device ID has multiple #-seperated parts and uniquely identifies
|
|
// this device and which USB port it is connected to.
|
|
wchar_t name[256];
|
|
UINT namelen = countof(name);
|
|
wchar_t *devid, *devidend;
|
|
|
|
if (GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen) == (UINT)-1)
|
|
{ // Can't get name. Skip it, since there's stuff in there we need for config.
|
|
continue;
|
|
}
|
|
|
|
devid = wcschr(name, '#');
|
|
if (devid == NULL)
|
|
{ // Should not happen
|
|
continue;
|
|
}
|
|
devidend = wcsrchr(++devid, '#');
|
|
if (devidend != NULL)
|
|
{
|
|
*devidend = '\0';
|
|
}
|
|
|
|
FAdapterHandle handle = { devices[i].hDevice, type, 0, FString(devid) };
|
|
|
|
// Adapters that support more than one controller have a seperate device
|
|
// entry for each controller. We can examine the name to determine which
|
|
// controller this device is for.
|
|
if (Descriptors[type].ControllerNumber >= 0)
|
|
{
|
|
wchar_t *col = wcsstr(devid, L"&Col");
|
|
if (col != NULL)
|
|
{
|
|
// I have no idea if this number is base 16 or base 10. Every
|
|
// other number in the name is base 16, so I assume this one is
|
|
// too, but since I don't have anything that goes higher than 02,
|
|
// I can't be sure.
|
|
handle.ControllerNumber = wcstoul(col + 4, NULL, 16);
|
|
}
|
|
}
|
|
adapters.Push(handle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(devices);
|
|
|
|
// Sort the found devices so that we have a consistant ordering.
|
|
qsort(&adapters[0], adapters.Size(), sizeof(FAdapterHandle), DeviceSort);
|
|
|
|
// Compare the new list of devices with the one we previously instantiated.
|
|
// Every device we currently hold is marked 0. Then scan through the new
|
|
// list and try to find it there, if it's found, it is marked 1. At the end
|
|
// of this, devices marked 1 existed before and are left alone. Devices
|
|
// marked 0 are no longer present and should be destroyed. If a device is
|
|
// present in the new list that we have not yet instantiated, we
|
|
// instantiate it now.
|
|
FRawPS2Controller *newone = NULL;
|
|
EAdapterType lasttype = ADAPTER_Unknown;
|
|
int sequence = 0; // Resets to 0 or 1 each time the adapter type changes
|
|
|
|
for (j = 0; j < Devices.Size(); ++j)
|
|
{
|
|
Devices[j]->Marked = false;
|
|
}
|
|
for (i = 0; i < adapters.Size(); ++i)
|
|
{
|
|
FAdapterHandle *adapter = &adapters[i];
|
|
|
|
if (adapter->Type != lasttype)
|
|
{
|
|
lasttype = adapter->Type;
|
|
// Peak ahead. If the next adapter has the same type, use 1.
|
|
// Otherwise, use 0. (0 means to not append a number to
|
|
// the device name.)
|
|
if (i == adapters.Size() - 1 || adapter->Type != adapters[i+1].Type)
|
|
{
|
|
sequence = 0;
|
|
}
|
|
else
|
|
{
|
|
sequence = 1;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < Devices.Size(); ++j)
|
|
{
|
|
if (Devices[j]->Handle == adapter->Handle)
|
|
{
|
|
Devices[j]->Marked = true;
|
|
break;
|
|
}
|
|
}
|
|
if (j == Devices.Size())
|
|
{ // Not found. Add it.
|
|
FRawPS2Controller *device = new FRawPS2Controller(adapter->Handle, adapter->Type, sequence++,
|
|
adapter->ControllerNumber, adapter->DeviceID);
|
|
device->Marked = true;
|
|
Devices.Push(device);
|
|
if (newone == NULL)
|
|
{
|
|
newone = device;
|
|
}
|
|
}
|
|
}
|
|
// Remove detached devices and avoid holes in the list.
|
|
for (i = j = 0; j < Devices.Size(); ++j)
|
|
{
|
|
if (!Devices[j]->Marked)
|
|
{
|
|
delete Devices[j];
|
|
}
|
|
else
|
|
{
|
|
if (i != j)
|
|
{
|
|
Devices[i] = Devices[j];
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
Devices.Resize(i);
|
|
DoRegister();
|
|
return newone;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: DeviceSort STATIC
|
|
//
|
|
// Sorts first by device type, then by ID, then by controller number.
|
|
//
|
|
//===========================================================================
|
|
|
|
int FRawPS2Manager::DeviceSort(const void *a, const void *b)
|
|
{
|
|
const FAdapterHandle *ha = (const FAdapterHandle *)a;
|
|
const FAdapterHandle *hb = (const FAdapterHandle *)b;
|
|
int lex = ha->Type - hb->Type;
|
|
if (lex == 0)
|
|
{
|
|
// Skip device part of the ID and sort the connection part
|
|
const char *ca = strchr(ha->DeviceID, '#');
|
|
const char *cb = strchr(hb->DeviceID, '#');
|
|
const char *ea, *eb;
|
|
// The last bit looks like a controller number. Strip it out to be safe
|
|
// if this is a multi-controller adapter.
|
|
if (ha->ControllerNumber != 0)
|
|
{
|
|
ea = strrchr(ca, '&');
|
|
eb = strrchr(cb, '&');
|
|
}
|
|
else
|
|
{
|
|
ea = strlen(ca) + ca;
|
|
eb = strlen(cb) + cb;
|
|
}
|
|
for (; ca < ea && cb < eb && lex == 0; ++ca, ++cb)
|
|
{
|
|
lex = *ca - *cb;
|
|
}
|
|
if (lex == 0)
|
|
{
|
|
lex = ha->ControllerNumber - hb->ControllerNumber;
|
|
}
|
|
}
|
|
return lex;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// FRawPS2Manager :: DoRegister
|
|
//
|
|
// Ensure that we are only listening for input if devices we care about
|
|
// are attached to the system.
|
|
//
|
|
//===========================================================================
|
|
|
|
void FRawPS2Manager::DoRegister()
|
|
{
|
|
RAWINPUTDEVICE rid;
|
|
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
|
|
rid.usUsage = HID_GDP_JOYSTICK;
|
|
if (Devices.Size() == 0)
|
|
{
|
|
if (Registered)
|
|
{
|
|
rid.dwFlags = RIDEV_REMOVE;
|
|
rid.hwndTarget = NULL;
|
|
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
|
{
|
|
Registered = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Registered)
|
|
{
|
|
rid.dwFlags = RIDEV_INPUTSINK;
|
|
rid.hwndTarget = Window;
|
|
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
|
|
{
|
|
Registered = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// I_StartupRawPS2
|
|
//
|
|
//===========================================================================
|
|
|
|
void I_StartupRawPS2()
|
|
{
|
|
if (!joy_ps2raw || !use_joystick || Args->CheckParm("-nojoy"))
|
|
{
|
|
if (JoyDevices[INPUT_RawPS2] != NULL)
|
|
{
|
|
delete JoyDevices[INPUT_RawPS2];
|
|
JoyDevices[INPUT_RawPS2] = NULL;
|
|
UpdateJoystickMenu(NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (JoyDevices[INPUT_RawPS2] == NULL)
|
|
{
|
|
FJoystickCollection *joys = new FRawPS2Manager;
|
|
if (joys->GetDevice())
|
|
{
|
|
JoyDevices[INPUT_RawPS2] = joys;
|
|
}
|
|
else
|
|
{
|
|
delete joys;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// I_IsPS2Adapter
|
|
//
|
|
// The Data1 part of a DirectInput product GUID contains the device's vendor
|
|
// and product IDs. Returns true if we know what this device is.
|
|
//
|
|
//===========================================================================
|
|
|
|
bool I_IsPS2Adapter(DWORD vidpid)
|
|
{
|
|
return (vidpid == MAKELONG(VID_PLAY_COM, PID_EMS_USB2_PS2_CONTROLLER_ADAPTER) ||
|
|
vidpid == MAKELONG(VID_GREENASIA, PID_DRAGON_PS2_CONTROLLER_ADAPTER) ||
|
|
vidpid == MAKELONG(VID_GREENASIA, PID_PANTHERLORD_PS2_CONTROLLER_ADAPTER) ||
|
|
vidpid == MAKELONG(VID_PERSONAL_COMMUNICATION_SYSTEMS, PID_TWIN_USB_VIBRATION_GAMEPAD));
|
|
}
|