mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-27 14:32:26 +00:00
84173ee09b
The main bulk of this is the new start screen code. To make this work in Raze some more work on the startup procedure is needed. What this does provide is support for the DOS end-of-game text screens in Duke and SW on non-Windows systems.
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 "gameconfigfile.h"
|
|
#include "m_argv.h"
|
|
#include "cmdlib.h"
|
|
#include "keydef.h"
|
|
|
|
#include "i_mainwindow.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 ----------------------------------------------
|
|
|
|
// 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 = mainwindow.GetHandle();
|
|
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 = mainwindow.GetHandle();
|
|
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));
|
|
}
|