Merge pull request #1560 from magcius/pad-cleanup-1

Pad cleanup 1
This commit is contained in:
Ryan Houdek 2014-12-07 01:22:03 -06:00
commit d9d0e285c9
36 changed files with 275 additions and 1249 deletions

View File

@ -64,68 +64,18 @@ void GetStatus(u8 _numPAD, GCPadStatus* _pPADStatus)
return;
}
// if we are on the next input cycle, update output and input
// if we can get a lock
static int _last_numPAD = 4;
if (_numPAD <= _last_numPAD)
{
g_controller_interface.UpdateOutput();
g_controller_interface.UpdateInput();
}
_last_numPAD = _numPAD;
// get input
((GCPad*)s_config.controllers[_numPAD])->GetInput(_pPADStatus);
}
// __________________________________________________________________________________________________
// Function: Rumble
// Purpose: Pad rumble!
// input: _numPad - Which pad to rumble.
// _uType - Command type (Stop=0, Rumble=1, Stop Hard=2).
// _uStrength - Strength of the Rumble
// output: none
//
void Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength)
void Rumble(u8 _numPAD, const ControlState strength)
{
std::unique_lock<std::recursive_mutex> lk(s_config.controls_lock, std::try_to_lock);
if (lk.owns_lock())
{
// TODO: this has potential to not stop rumble if user is messing with GUI at the perfect time
// set rumble
if (1 == _uType && _uStrength > 2)
{
((GCPad*)s_config.controllers[ _numPAD ])->SetOutput(255);
}
else
{
((GCPad*)s_config.controllers[ _numPAD ])->SetOutput(0);
}
}
}
if (!lk.owns_lock())
return;
// __________________________________________________________________________________________________
// Function: Motor
// Purpose: For devices with constant Force feedback
// input: _numPAD - The pad to operate on
// _uType - 06 = Motor On, 04 = Motor Off
// _uStrength - 00 = Left Strong, 127 = Left Weak, 128 = Right Weak, 255 = Right Strong
// output: none
//
void Motor(u8 _numPAD, unsigned int _uType, unsigned int _uStrength)
{
std::unique_lock<std::recursive_mutex> lk(s_config.controls_lock, std::try_to_lock);
if (lk.owns_lock())
{
// TODO: this has potential to not stop rumble if user is messing with GUI at the perfect time
// set rumble
if (_uType == 6)
{
((GCPad*)s_config.controllers[ _numPAD ])->SetMotor(_uStrength);
}
}
((GCPad*)s_config.controllers[ _numPAD ])->SetOutput(strength);
}
bool GetMicButton(u8 pad)

View File

@ -17,8 +17,7 @@ void Initialize(void* const hwnd);
InputConfig* GetConfig();
void GetStatus(u8 _numPAD, GCPadStatus* _pPADStatus);
void Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength);
void Motor(u8 _numPAD, unsigned int _uType, unsigned int _uStrength);
void Rumble(u8 _numPAD, const ControlState strength);
bool GetMicButton(u8 pad);
}

View File

@ -120,16 +120,9 @@ void GCPad::GetInput(GCPadStatus* const pad)
pad->triggerRight = static_cast<u8>(triggers[1] * 0xFF);
}
void GCPad::SetMotor(const u8 on)
void GCPad::SetOutput(const ControlState strength)
{
// map 0..255 to -1.0..1.0
ControlState force = on / 127.5 - 1;
m_rumble->controls[0]->control_ref->State(force);
}
void GCPad::SetOutput(const u8 on)
{
m_rumble->controls[0]->control_ref->State(on);
m_rumble->controls[0]->control_ref->State(strength);
}
void GCPad::LoadDefaults(const ControllerInterface& ciface)

View File

@ -14,8 +14,7 @@ public:
GCPad(const unsigned int index);
void GetInput(GCPadStatus* const pad);
void SetOutput(const u8 on);
void SetMotor(const u8 on);
void SetOutput(const ControlState strength);
bool GetMicButton() const;

View File

@ -2,250 +2,40 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Movie.h"
#include "Core/HW/EXI_Device.h"
#include "Core/HW/EXI_DeviceMic.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI.h"
#include "Core/HW/SI_Device.h"
#include "Core/HW/SI_DeviceDanceMat.h"
#include "Core/HW/SystemTimers.h"
// --- Dance mat GameCube controller ---
CSIDevice_DanceMat::CSIDevice_DanceMat(SIDevices device, int _iDeviceNumber)
: ISIDevice(device, _iDeviceNumber)
, m_TButtonComboStart(0)
, m_TButtonCombo(0)
, m_LastButtonCombo(COMBO_NONE)
: CSIDevice_GCController(device, _iDeviceNumber) {}
u32 CSIDevice_DanceMat::MapPadStatus(const GCPadStatus& pad_status)
{
memset(&m_Origin, 0, sizeof(SOrigin));
m_Origin.uCommand = CMD_ORIGIN;
m_Origin.uOriginStickX = 0x80; // center
m_Origin.uOriginStickY = 0x80;
m_Origin.uSubStickStickX = 0x80;
m_Origin.uSubStickStickY = 0x80;
m_Origin.uTrigger_L = 0x00;
m_Origin.uTrigger_R = 0x00;
// Dunno if we need to do this, game/lib should set it?
m_Mode = 0x03;
}
int CSIDevice_DanceMat::RunBuffer(u8* _pBuffer, int _iLength)
{
// For debug logging only
ISIDevice::RunBuffer(_pBuffer, _iLength);
// Read the command
EBufferCommands command = static_cast<EBufferCommands>(_pBuffer[3]);
// Handle it
switch (command)
{
case CMD_RESET:
*(u32*)&_pBuffer[0] = SI_DANCEMAT;
break;
case CMD_DIRECT:
{
INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength);
u32 high, low;
GetData(high, low);
for (int i = 0; i < (_iLength - 1) / 2; i++)
{
_pBuffer[i + 0] = (high >> (i * 8)) & 0xff;
_pBuffer[i + 4] = (low >> (i * 8)) & 0xff;
}
}
break;
case CMD_ORIGIN:
{
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
for (int i = 0; i < (int)sizeof(SOrigin); i++)
{
_pBuffer[i ^ 3] = *pCalibration++;
}
}
break;
// Recalibrate (FiRES: i am not 100 percent sure about this)
case CMD_RECALIBRATE:
{
INFO_LOG(SERIALINTERFACE, "PAD - Recalibrate");
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
for (int i = 0; i < (int)sizeof(SOrigin); i++)
{
_pBuffer[i ^ 3] = *pCalibration++;
}
}
break;
// DEFAULT
default:
{
ERROR_LOG(SERIALINTERFACE, "Unknown SI command (0x%x)", command);
PanicAlert("SI: Unknown command (0x%x)", command);
}
break;
}
return _iLength;
}
// GetData
// Return true on new data (max 7 Bytes and 6 bits ;)
// [00?SYXBA] [1LRZUDRL] [x] [y] [cx] [cy] [l] [r]
// |\_ ERR_LATCH (error latched - check SISR)
// |_ ERR_STATUS (error on last GetData or SendCmd?)
bool CSIDevice_DanceMat::GetData(u32& _Hi, u32& _Low)
{
GCPadStatus PadStatus;
memset(&PadStatus, 0, sizeof(PadStatus));
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
Movie::CallGCInputManip(&PadStatus, ISIDevice::m_iDeviceNumber);
Movie::SetPolledDevice();
if (NetPlay_GetInput(ISIDevice::m_iDeviceNumber, &PadStatus))
{
}
else if (Movie::IsPlayingInput())
{
Movie::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber);
Movie::InputUpdate();
}
else if (Movie::IsRecordingInput())
{
Movie::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber);
Movie::InputUpdate();
}
else
{
Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber);
}
// Map the dpad to the blue arrows, the buttons to the orange arrows
// Z = + button, Start = - button
u16 map = 0;
if (PadStatus.button & PAD_BUTTON_UP)
if (pad_status.button & PAD_BUTTON_UP)
map |= 0x1000;
if (PadStatus.button & PAD_BUTTON_DOWN)
if (pad_status.button & PAD_BUTTON_DOWN)
map |= 0x2;
if (PadStatus.button & PAD_BUTTON_LEFT)
if (pad_status.button & PAD_BUTTON_LEFT)
map |= 0x8;
if (PadStatus.button & PAD_BUTTON_RIGHT)
if (pad_status.button & PAD_BUTTON_RIGHT)
map |= 0x4;
if (PadStatus.button & PAD_BUTTON_Y)
if (pad_status.button & PAD_BUTTON_Y)
map |= 0x200;
if (PadStatus.button & PAD_BUTTON_A)
if (pad_status.button & PAD_BUTTON_A)
map |= 0x10;
if (PadStatus.button & PAD_BUTTON_B)
if (pad_status.button & PAD_BUTTON_B)
map |= 0x100;
if (PadStatus.button & PAD_BUTTON_X)
if (pad_status.button & PAD_BUTTON_X)
map |= 0x800;
if (PadStatus.button & PAD_TRIGGER_Z)
if (pad_status.button & PAD_TRIGGER_Z)
map |= 0x400;
if (PadStatus.button & PAD_BUTTON_START)
if (pad_status.button & PAD_BUTTON_START)
map |= 0x1;
_Hi = (u32)(map << 16) | 0x8080;
// Low bits are packed differently per mode
if (m_Mode == 0 || m_Mode == 5 || m_Mode == 6 || m_Mode == 7)
{
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 8); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 12); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.substickY) << 16); // All 8 bits
_Low |= (u32)((u8)(PadStatus.substickX) << 24); // All 8 bits
}
else if (m_Mode == 1)
{
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits
_Low |= (u32)((u8)PadStatus.triggerRight << 8); // All 8 bits
_Low |= (u32)((u8)PadStatus.triggerLeft << 16); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickY << 24); // Top 4 bits
_Low |= (u32)((u8)PadStatus.substickX << 28); // Top 4 bits
}
else if (m_Mode == 2)
{
// Identifies the dance mat
_Low = 0x8080ffff;
}
else if (m_Mode == 3)
{
// Analog A/B are always 0
_Low = (u8)PadStatus.triggerRight; // All 8 bits
_Low |= (u32)((u8)PadStatus.triggerLeft << 8); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
}
else if (m_Mode == 4)
{
_Low = (u8)(PadStatus.analogB); // All 8 bits
_Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits
// triggerLeft/Right are always 0
_Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
}
return true;
return (u32)(map << 16) | 0x8080;
}
// SendCommand
void CSIDevice_DanceMat::SendCommand(u32 _Cmd, u8 _Poll)
void CSIDevice_DanceMat::HandleButtonCombos(const GCPadStatus& pad_status)
{
UCommand command(_Cmd);
switch (command.Command)
{
// Costis sent it in some demos :)
case 0x00:
break;
case CMD_WRITE:
{
unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard
unsigned int uStrength = command.Parameter2;
// get the correct pad number that should rumble locally when using netplay
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
if (numPAD < 4)
Pad::Rumble(numPAD, uType, uStrength);
if (!_Poll)
{
m_Mode = command.Parameter2;
INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode);
}
}
break;
default:
{
ERROR_LOG(SERIALINTERFACE, "Unknown direct command (0x%x)", _Cmd);
PanicAlert("SI: Unknown direct command");
}
break;
}
}
// Savestate support
void CSIDevice_DanceMat::DoState(PointerWrap& p)
{
p.Do(m_Origin);
p.Do(m_Mode);
p.Do(m_TButtonComboStart);
p.Do(m_TButtonCombo);
p.Do(m_LastButtonCombo);
}

View File

@ -4,98 +4,12 @@
#pragma once
#include "Core/HW/SI_Device.h"
#include "InputCommon/GCPadStatus.h"
#include "Core/HW/SI_DeviceGCController.h"
// standard GameCube controller
class CSIDevice_DanceMat : public ISIDevice
class CSIDevice_DanceMat : public CSIDevice_GCController
{
private:
// Commands
enum EBufferCommands
{
CMD_RESET = 0x00,
CMD_DIRECT = 0x40,
CMD_ORIGIN = 0x41,
CMD_RECALIBRATE = 0x42,
};
struct SOrigin
{
u8 uCommand;// Maybe should be button bits?
u8 unk_1; // ..and this would be the other half
u8 uOriginStickX;
u8 uOriginStickY;
u8 uSubStickStickX;
u8 uSubStickStickY;
u8 uTrigger_L;
u8 uTrigger_R;
u8 unk_4;
u8 unk_5;
u8 unk_6;
u8 unk_7;
};
enum EDirectCommands
{
CMD_WRITE = 0x40
};
union UCommand
{
u32 Hex;
struct
{
u32 Parameter1 : 8;
u32 Parameter2 : 8;
u32 Command : 8;
u32 : 8;
};
UCommand() {Hex = 0;}
UCommand(u32 _iValue) {Hex = _iValue;}
};
enum EButtonCombo
{
COMBO_NONE = 0,
COMBO_ORIGIN,
COMBO_RESET
};
// struct to compare input against
// Set on connection and (standard pad only) on button combo
SOrigin m_Origin;
// PADAnalogMode
u8 m_Mode;
// Timer to track special button combos:
// y, X, start for 3 seconds updates origin with current status
// Technically, the above is only on standard pad, wavebird does not support it for example
// b, x, start for 3 seconds triggers reset (PI reset button interrupt)
u64 m_TButtonComboStart, m_TButtonCombo;
// Type of button combo from the last/current poll
EButtonCombo m_LastButtonCombo;
public:
// Constructor
CSIDevice_DanceMat(SIDevices device, int _iDeviceNumber);
// Run the SI Buffer
virtual int RunBuffer(u8* _pBuffer, int _iLength) override;
// Send and Receive pad input from network
static bool NetPlay_GetInput(u8 numPAD, GCPadStatus* status);
static u8 NetPlay_InGamePadToLocalPad(u8 numPAD);
// Return true on new data
virtual bool GetData(u32& _Hi, u32& _Low) override;
// Send a command directly
virtual void SendCommand(u32 _Cmd, u8 _Poll) override;
// Savestate support
virtual void DoState(PointerWrap& p) override;
virtual u32 MapPadStatus(const GCPadStatus& pad_status) override;
virtual void HandleButtonCombos(const GCPadStatus& pad_status) override;
};

View File

@ -131,10 +131,7 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber);
}
// Thankfully changing mode does not change the high bits ;)
_Hi = (u32)((u8)PadStatus.stickY);
_Hi |= (u32)((u8)PadStatus.stickX << 8);
_Hi |= (u32)((u16)(PadStatus.button | PAD_USE_ORIGIN) << 16);
_Hi = MapPadStatus(PadStatus);
// Low bits are packed differently per mode
if (m_Mode == 0 || m_Mode == 5 || m_Mode == 6 || m_Mode == 7)
@ -181,11 +178,27 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
}
HandleButtonCombos(PadStatus);
return true;
}
u32 CSIDevice_GCController::MapPadStatus(const GCPadStatus& pad_status)
{
// Thankfully changing mode does not change the high bits ;)
u32 _Hi = 0;
_Hi = (u32)((u8)pad_status.stickY);
_Hi |= (u32)((u8)pad_status.stickX << 8);
_Hi |= (u32)((u16)(pad_status.button | PAD_USE_ORIGIN) << 16);
return _Hi;
}
void CSIDevice_GCController::HandleButtonCombos(const GCPadStatus& pad_status)
{
// Keep track of the special button combos (embedded in controller hardware... :( )
EButtonCombo tempCombo;
if ((PadStatus.button & 0xff00) == (PAD_BUTTON_Y|PAD_BUTTON_X|PAD_BUTTON_START))
if ((pad_status.button & 0xff00) == (PAD_BUTTON_Y|PAD_BUTTON_X|PAD_BUTTON_START))
tempCombo = COMBO_ORIGIN;
else if ((PadStatus.button & 0xff00) == (PAD_BUTTON_B|PAD_BUTTON_X|PAD_BUTTON_START))
else if ((pad_status.button & 0xff00) == (PAD_BUTTON_B|PAD_BUTTON_X|PAD_BUTTON_START))
tempCombo = COMBO_RESET;
else
tempCombo = COMBO_NONE;
@ -204,21 +217,18 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
ProcessorInterface::ResetButton_Tap();
else if (m_LastButtonCombo == COMBO_ORIGIN)
{
m_Origin.uOriginStickX = PadStatus.stickX;
m_Origin.uOriginStickY = PadStatus.stickY;
m_Origin.uSubStickStickX = PadStatus.substickX;
m_Origin.uSubStickStickY = PadStatus.substickY;
m_Origin.uTrigger_L = PadStatus.triggerLeft;
m_Origin.uTrigger_R = PadStatus.triggerRight;
m_Origin.uOriginStickX = pad_status.stickX;
m_Origin.uOriginStickY = pad_status.stickY;
m_Origin.uSubStickStickX = pad_status.substickX;
m_Origin.uSubStickStickY = pad_status.substickY;
m_Origin.uTrigger_L = pad_status.triggerLeft;
m_Origin.uTrigger_R = pad_status.triggerRight;
}
m_LastButtonCombo = COMBO_NONE;
}
}
return true;
}
// SendCommand
void CSIDevice_GCController::SendCommand(u32 _Cmd, u8 _Poll)
{
@ -239,7 +249,12 @@ void CSIDevice_GCController::SendCommand(u32 _Cmd, u8 _Poll)
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
if (numPAD < 4)
Pad::Rumble(numPAD, uType, uStrength);
{
if (uType == 1 && uStrength > 2)
Pad::Rumble(numPAD, 1.0);
else
Pad::Rumble(numPAD, 0.0);
}
if (!_Poll)
{

View File

@ -7,11 +7,9 @@
#include "Core/HW/SI_Device.h"
#include "InputCommon/GCPadStatus.h"
// standard GameCube controller
class CSIDevice_GCController : public ISIDevice
{
private:
protected:
// Commands
enum EBufferCommands
@ -24,7 +22,7 @@ private:
struct SOrigin
{
u8 uCommand;// Maybe should be button bits?
u8 uCommand; // Maybe should be button bits?
u8 unk_1; // ..and this would be the other half
u8 uOriginStickX;
u8 uOriginStickY;
@ -94,6 +92,9 @@ public:
// Return true on new data
virtual bool GetData(u32& _Hi, u32& _Low) override;
virtual u32 MapPadStatus(const GCPadStatus& pad_status);
virtual void HandleButtonCombos(const GCPadStatus& pad_status);
// Send a command directly
virtual void SendCommand(u32 _Cmd, u8 _Poll) override;

View File

@ -2,241 +2,18 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Movie.h"
#include "Core/HW/EXI_Device.h"
#include "Core/HW/EXI_DeviceMic.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI.h"
#include "Core/HW/SI_Device.h"
#include "Core/HW/SI_DeviceGCSteeringWheel.h"
#include "Core/HW/SystemTimers.h"
// --- standard GameCube controller ---
CSIDevice_GCSteeringWheel::CSIDevice_GCSteeringWheel(SIDevices device, int _iDeviceNumber)
: ISIDevice(device, _iDeviceNumber)
, m_TButtonComboStart(0)
, m_TButtonCombo(0)
, m_LastButtonCombo(COMBO_NONE)
{
memset(&m_Origin, 0, sizeof(SOrigin));
m_Origin.uCommand = CMD_ORIGIN;
m_Origin.uOriginStickX = 0x80; // center
m_Origin.uOriginStickY = 0x80;
m_Origin.uSubStickStickX = 0x80;
m_Origin.uSubStickStickY = 0x80;
m_Origin.uTrigger_L = 0x1F; // 0-30 is the lower deadzone
m_Origin.uTrigger_R = 0x1F;
: CSIDevice_GCController(device, _iDeviceNumber)
{}
// Dunno if we need to do this, game/lib should set it?
m_Mode = 0x03;
}
int CSIDevice_GCSteeringWheel::RunBuffer(u8* _pBuffer, int _iLength)
{
// For debug logging only
ISIDevice::RunBuffer(_pBuffer, _iLength);
// Read the command
EBufferCommands command = static_cast<EBufferCommands>(_pBuffer[3]);
// Handle it
switch (command)
{
case CMD_RESET:
*(u32*)&_pBuffer[0] = SI_GC_STEERING;
break;
case CMD_ORIGIN:
{
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
for (int i = 0; i < (int)sizeof(SOrigin); i++)
{
_pBuffer[i ^ 3] = *pCalibration++;
}
}
break;
// Recalibrate (FiRES: i am not 100 percent sure about this)
case CMD_RECALIBRATE:
{
INFO_LOG(SERIALINTERFACE, "PAD - Recalibrate");
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
for (int i = 0; i < (int)sizeof(SOrigin); i++)
{
_pBuffer[i ^ 3] = *pCalibration++;
}
}
break;
// Seen in F-Zero GX
case CMD_MOTOR_OFF:
break;
// DEFAULT
default:
{
ERROR_LOG(SERIALINTERFACE, "Unknown SI command (0x%x)", command);
}
break;
}
return _iLength;
}
// GetData
// Return true on new data (max 7 Bytes and 6 bits ;)
// [00?SYXBA] [1LRZUDRL] [x] [y] [cx] [cy] [l] [r]
// |\_ ERR_LATCH (error latched - check SISR)
// |_ ERR_STATUS (error on last GetData or SendCmd?)
bool CSIDevice_GCSteeringWheel::GetData(u32& _Hi, u32& _Low)
{
GCPadStatus PadStatus;
memset(&PadStatus, 0, sizeof(PadStatus));
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
Movie::CallGCInputManip(&PadStatus, ISIDevice::m_iDeviceNumber);
Movie::SetPolledDevice();
if (NetPlay_GetInput(ISIDevice::m_iDeviceNumber, &PadStatus))
{
}
else if (Movie::IsPlayingInput())
{
Movie::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber);
Movie::InputUpdate();
}
else if (Movie::IsRecordingInput())
{
Movie::RecordInput(&PadStatus, ISIDevice::m_iDeviceNumber);
Movie::InputUpdate();
}
else
{
Movie::CheckPadStatus(&PadStatus, ISIDevice::m_iDeviceNumber);
}
// Thankfully changing mode does not change the high bits ;)
_Hi = (u32)((u8)PadStatus.stickX); // Steering
_Hi |= 0x800; // Pedal connected flag
_Hi |= (u32)((u16)(PadStatus.button | PAD_USE_ORIGIN) << 16);
// Low bits are packed differently per mode
if (m_Mode == 0 || m_Mode == 5 || m_Mode == 7)
{
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 8); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 12); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.substickY) << 16); // All 8 bits
_Low |= (u32)((u8)(PadStatus.substickX) << 24); // All 8 bits
}
else if (m_Mode == 1)
{
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits
_Low |= (u32)((u8)PadStatus.triggerRight << 8); // All 8 bits
_Low |= (u32)((u8)PadStatus.triggerLeft << 16); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickY << 24); // Top 4 bits
_Low |= (u32)((u8)PadStatus.substickX << 28); // Top 4 bits
}
else if (m_Mode == 2)
{
_Low = (u8)(PadStatus.analogB); // All 8 bits
_Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits
_Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 16); // Top 4 bits
_Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 20); // Top 4 bits
_Low |= (u32)((u8)PadStatus.substickY << 24); // Top 4 bits
_Low |= (u32)((u8)PadStatus.substickX << 28); // Top 4 bits
}
else if (m_Mode == 3)
{
// Analog A/B are always 0
_Low = (u8)PadStatus.triggerRight; // All 8 bits
_Low |= (u32)((u8)PadStatus.triggerLeft << 8); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
}
else if (m_Mode == 4)
{
_Low = (u8)(PadStatus.analogB); // All 8 bits
_Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits
// triggerLeft/Right are always 0
_Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
}
else if (m_Mode == 6)
{
_Low = (u8)PadStatus.triggerRight; // All 8 bits
_Low |= (u32)((u8)PadStatus.triggerLeft << 8); // All 8 bits
// The GC Steering Wheel appears to have combined pedals
// (both the Accelerate and Brake pedals are mapped to a single axis)
// We use the stickY axis for the pedals.
if (PadStatus.stickY < 128)
_Low |= (u32)((u8)(255 - ((PadStatus.stickY & 0x7f) * 2)) << 16); // All 8 bits (Brake)
if (PadStatus.stickY >= 128)
_Low |= (u32)((u8)((PadStatus.stickY & 0x7f) * 2) << 24); // All 8 bits (Accelerate)
}
// Keep track of the special button combos (embedded in controller hardware... :( )
EButtonCombo tempCombo;
if ((PadStatus.button & 0xff00) == (PAD_BUTTON_Y|PAD_BUTTON_X|PAD_BUTTON_START))
tempCombo = COMBO_ORIGIN;
else if ((PadStatus.button & 0xff00) == (PAD_BUTTON_B|PAD_BUTTON_X|PAD_BUTTON_START))
tempCombo = COMBO_RESET;
else
tempCombo = COMBO_NONE;
if (tempCombo != m_LastButtonCombo)
{
m_LastButtonCombo = tempCombo;
if (m_LastButtonCombo != COMBO_NONE)
m_TButtonComboStart = CoreTiming::GetTicks();
}
if (m_LastButtonCombo != COMBO_NONE)
{
m_TButtonCombo = CoreTiming::GetTicks();
if ((m_TButtonCombo - m_TButtonComboStart) > SystemTimers::GetTicksPerSecond() * 3)
{
if (m_LastButtonCombo == COMBO_RESET)
{
ProcessorInterface::ResetButton_Tap();
}
else if (m_LastButtonCombo == COMBO_ORIGIN)
{
m_Origin.uOriginStickX = PadStatus.stickX;
m_Origin.uOriginStickY = PadStatus.stickY;
m_Origin.uSubStickStickX = PadStatus.substickX;
m_Origin.uSubStickStickY = PadStatus.substickY;
m_Origin.uTrigger_L = PadStatus.triggerLeft;
m_Origin.uTrigger_R = PadStatus.triggerRight;
}
m_LastButtonCombo = COMBO_NONE;
}
}
return true;
}
// SendCommand
void CSIDevice_GCSteeringWheel::SendCommand(u32 _Cmd, u8 _Poll)
{
UCommand command(_Cmd);
switch (command.Command)
{
// Costis sent it in some demos :)
case 0x00:
break;
case CMD_FORCE:
if (command.Command == CMD_FORCE)
{
unsigned int uStrength = command.Parameter1; // 0 = left strong, 127 = left weak, 128 = right weak, 255 = right strong
unsigned int uType = command.Parameter2; // 06 = motor on, 04 = motor off
@ -245,7 +22,18 @@ void CSIDevice_GCSteeringWheel::SendCommand(u32 _Cmd, u8 _Poll)
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
if (numPAD < 4)
Pad::Motor(numPAD, uType, uStrength);
{
if (uType == 0x06)
{
// map 0..255 to -1.0..1.0
ControlState strength = uStrength / 127.5 - 1;
Pad::Rumble(numPAD, strength);
}
else
{
Pad::Rumble(numPAD, 0);
}
}
if (!_Poll)
{
@ -253,41 +41,8 @@ void CSIDevice_GCSteeringWheel::SendCommand(u32 _Cmd, u8 _Poll)
INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode);
}
}
break;
case CMD_WRITE:
else
{
unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard
unsigned int uStrength = command.Parameter2;
// get the correct pad number that should rumble locally when using netplay
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
if (numPAD < 4)
Pad::Rumble(numPAD, uType, uStrength);
if (!_Poll)
{
m_Mode = command.Parameter2;
INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode);
}
}
break;
default:
{
ERROR_LOG(SERIALINTERFACE, "Unknown direct command (0x%x)", _Cmd);
}
break;
return CSIDevice_GCController::SendCommand(_Cmd, _Poll);
}
}
// Savestate support
void CSIDevice_GCSteeringWheel::DoState(PointerWrap& p)
{
p.Do(m_Origin);
p.Do(m_Mode);
p.Do(m_TButtonComboStart);
p.Do(m_TButtonCombo);
p.Do(m_LastButtonCombo);
}

View File

@ -4,14 +4,11 @@
#pragma once
#include "Core/HW/SI_Device.h"
#include "InputCommon/GCPadStatus.h"
#include "Core/HW/SI_DeviceGCController.h"
// standard GameCube controller
class CSIDevice_GCSteeringWheel : public ISIDevice
class CSIDevice_GCSteeringWheel : public CSIDevice_GCController
{
private:
// Commands
enum EBufferCommands
{
@ -21,82 +18,13 @@ private:
CMD_MOTOR_OFF = 0xff,
};
struct SOrigin
{
u8 uCommand;// Maybe should be button bits?
u8 unk_1; // ..and this would be the other half
u8 uOriginStickX;
u8 uOriginStickY;
u8 uSubStickStickX;
u8 uSubStickStickY;
u8 uTrigger_L;
u8 uTrigger_R;
u8 unk_4;
u8 unk_5;
u8 unk_6;
u8 unk_7;
};
enum EDirectCommands
{
CMD_FORCE = 0x30,
CMD_WRITE = 0x40
};
union UCommand
{
u32 Hex;
struct
{
u32 Parameter1 : 8;
u32 Parameter2 : 8;
u32 Command : 8;
u32 : 8;
};
UCommand() {Hex = 0;}
UCommand(u32 _iValue) {Hex = _iValue;}
};
enum EButtonCombo
{
COMBO_NONE = 0,
COMBO_ORIGIN,
COMBO_RESET
};
// struct to compare input against
// Set on connection and (standard pad only) on button combo
SOrigin m_Origin;
// PADAnalogMode
u8 m_Mode;
// Timer to track special button combos:
// y, X, start for 3 seconds updates origin with current status
// Technically, the above is only on standard pad, wavebird does not support it for example
// b, x, start for 3 seconds triggers reset (PI reset button interrupt)
u64 m_TButtonComboStart, m_TButtonCombo;
// Type of button combo from the last/current poll
EButtonCombo m_LastButtonCombo;
public:
// Constructor
CSIDevice_GCSteeringWheel(SIDevices device, int _iDeviceNumber);
// Run the SI Buffer
virtual int RunBuffer(u8* _pBuffer, int _iLength) override;
// Send and Receive pad input from network
static bool NetPlay_GetInput(u8 numPAD, GCPadStatus* status);
static u8 NetPlay_InGamePadToLocalPad(u8 numPAD);
// Return true on new data
virtual bool GetData(u32& _Hi, u32& _Low) override;
// Send a command directly
virtual void SendCommand(u32 _Cmd, u8 _Poll) override;
// Savestate support
virtual void DoState(PointerWrap& p) override;
virtual void SendCommand(u32 _Cmd, u8 _Poll);
};

View File

@ -61,6 +61,8 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule:
#include "Core/IPC_HLE/WII_IPC_HLE.h"
#include "Core/PowerPC/PowerPC.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/VideoBackendBase.h"
@ -79,6 +81,7 @@ static int et_DSP;
static int et_IPC_HLE;
static int et_PatchEngine; // PatchEngine updates every 1/60th of a second by default
static int et_Throttle;
static int et_UpdateInput;
// These are badly educated guesses
// Feel free to experiment. Set these in Init below.
@ -132,6 +135,14 @@ static void VICallback(u64 userdata, int cyclesLate)
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine() - cyclesLate, et_VI);
}
static void UpdateInputCallback(u64 userdata, int cyclesLate)
{
g_controller_interface.UpdateInput();
// Poll system input every 1/60th of a second.
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond() / 60 - cyclesLate, et_UpdateInput);
}
static void SICallback(u64 userdata, int cyclesLate)
{
SerialInterface::UpdateDevices();
@ -258,6 +269,7 @@ void Init()
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);
et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback);
et_UpdateInput = CoreTiming::RegisterEvent("UpdateInput", UpdateInputCallback);
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI);
CoreTiming::ScheduleEvent(0, et_DSP);
@ -271,6 +283,8 @@ void Init()
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
CoreTiming::ScheduleEvent(IPC_HLE_PERIOD, et_IPC_HLE);
CoreTiming::ScheduleEvent(0, et_UpdateInput);
}
void Shutdown()

View File

@ -17,7 +17,6 @@ namespace Wiimote
{
static InputConfig s_config(WIIMOTE_INI_NAME, _trans("Wiimote"), "Wiimote");
static int s_last_number = 4;
InputConfig* GetConfig()
{
@ -115,13 +114,6 @@ void Update(int _number)
// TODO: change this to a try_to_lock, and make it give empty input on failure
std::lock_guard<std::recursive_mutex> lk(s_config.controls_lock);
if (_number <= s_last_number)
{
g_controller_interface.UpdateOutput();
g_controller_interface.UpdateInput();
}
s_last_number = _number;
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
((WiimoteEmu::Wiimote*)s_config.controllers[_number])->Update();
else
@ -154,7 +146,6 @@ void DoState(u8 **ptr, PointerWrap::Mode mode)
// TODO:
PointerWrap p(ptr, mode);
p.Do(s_last_number);
for (unsigned int i=0; i<MAX_BBMOTES; ++i)
((WiimoteEmu::Wiimote*)s_config.controllers[i])->DoState(p);
}

View File

@ -835,16 +835,6 @@ bool WiimoteEmu::Wiimote::NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size)
return false;
}
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, GCPadStatus* PadStatus)
{
return false;
}
bool CSIDevice_DanceMat::NetPlay_GetInput(u8 numPAD, GCPadStatus* PadStatus)
{
return false;
}
// called from ---CPU--- thread
// so all players' games get the same time
u32 CEXIIPL::NetPlay_GetGCTime()
@ -869,16 +859,6 @@ u8 CSIDevice_GCController::NetPlay_InGamePadToLocalPad(u8 numPAD)
return numPAD;
}
u8 CSIDevice_GCSteeringWheel::NetPlay_InGamePadToLocalPad(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_InGamePadToLocalPad(numPAD);
}
u8 CSIDevice_DanceMat::NetPlay_InGamePadToLocalPad(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_InGamePadToLocalPad(numPAD);
}
bool NetPlay::IsNetPlayRunning()
{
return netplay_client != nullptr;

View File

@ -39,9 +39,6 @@ private:
};
public:
bool UpdateInput() { return true; }
bool UpdateOutput() { return true; }
Touchscreen(int padID);
~Touchscreen() {}

View File

@ -98,9 +98,6 @@ void ControllerInterface::Shutdown()
for (ciface::Core::Device::Output* o : d->Outputs())
o->SetState(0);
// Update output
d->UpdateOutput();
// Delete device
delete d;
}
@ -135,52 +132,15 @@ void ControllerInterface::Shutdown()
//
// Update input for all devices, return true if all devices returned successful
//
bool ControllerInterface::UpdateInput(const bool force)
void ControllerInterface::UpdateInput()
{
std::unique_lock<std::recursive_mutex> lk(update_lock, std::defer_lock);
if (force)
lk.lock();
else if (!lk.try_lock())
return false;
size_t ok_count = 0;
if (!lk.try_lock())
return;
for (ciface::Core::Device* d : m_devices)
{
if (d->UpdateInput())
++ok_count;
//else
// disabled. it might be causing problems
//(*d)->ClearInputState();
}
return (m_devices.size() == ok_count);
}
//
// UpdateOutput
//
// Update output for all devices, return true if all devices returned successful
//
bool ControllerInterface::UpdateOutput(const bool force)
{
std::unique_lock<std::recursive_mutex> lk(update_lock, std::defer_lock);
if (force)
lk.lock();
else if (!lk.try_lock())
return false;
size_t ok_count = 0;
for (ciface::Core::Device* d : m_devices)
{
if (d->UpdateOutput())
++ok_count;
}
return (m_devices.size() == ok_count);
d->UpdateInput();
}
//
@ -299,14 +259,9 @@ ciface::Core::Device::Control* ControllerInterface::OutputReference::Detect(cons
// this loop is to make stuff like flashing keyboard LEDs work
while (ms > (slept += 10))
{
// TODO: improve this to update more than just the default device's output
device->UpdateOutput();
Common::SleepCurrentThread(10);
}
State(0);
device->UpdateOutput();
}
return nullptr;
}

View File

@ -120,8 +120,7 @@ public:
bool IsInit() const { return m_is_init; }
void UpdateReference(ControlReference* control, const ciface::Core::DeviceQualifier& default_device) const;
bool UpdateInput(const bool force = false);
bool UpdateOutput(const bool force = false);
void UpdateInput();
std::recursive_mutex update_lock;

View File

@ -149,7 +149,9 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI
InitForceFeedback(m_device, (int)objects.size());
}
ClearInputState();
ZeroMemory(&m_state_in, sizeof(m_state_in));
// set hats to center
memset(m_state_in.rgdwPOV, 0xFF, sizeof(m_state_in.rgdwPOV));
}
Joystick::~Joystick()
@ -158,13 +160,6 @@ Joystick::~Joystick()
m_device->Release();
}
void Joystick::ClearInputState()
{
ZeroMemory(&m_state_in, sizeof(m_state_in));
// set hats to center
memset(m_state_in.rgdwPOV, 0xFF, sizeof(m_state_in.rgdwPOV));
}
std::string Joystick::GetName() const
{
return GetDeviceName(m_device);
@ -182,7 +177,7 @@ std::string Joystick::GetSource() const
// update IO
bool Joystick::UpdateInput()
void Joystick::UpdateInput()
{
HRESULT hr = 0;
@ -220,9 +215,7 @@ bool Joystick::UpdateInput()
// try reacquire if input lost
if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr)
hr = m_device->Acquire();
return SUCCEEDED(hr);
m_device->Acquire();
}
// get name

View File

@ -52,9 +52,7 @@ private:
};
public:
bool UpdateInput();
void ClearInputState();
void UpdateInput() override;
Joystick(const LPDIRECTINPUTDEVICE8 device, const unsigned int index);
~Joystick();

View File

@ -30,17 +30,6 @@ static const struct
#include "InputCommon/ControllerInterface/DInput/NamedKeys.h" // NOLINT
};
static const struct
{
const BYTE code;
const char* const name;
} named_lights[] =
{
{ VK_NUMLOCK, "NUM LOCK" },
{ VK_CAPITAL, "CAPS LOCK" },
{ VK_SCROLL, "SCROLL LOCK" }
};
// lil silly
static HWND hwnd;
@ -102,16 +91,11 @@ KeyboardMouse::KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIREC
m_last_update = GetTickCount();
ZeroMemory(&m_state_in, sizeof(m_state_in));
ZeroMemory(m_state_out, sizeof(m_state_out));
ZeroMemory(&m_current_state_out, sizeof(m_current_state_out));
// KEYBOARD
// add keys
for (u8 i = 0; i < sizeof(named_keys)/sizeof(*named_keys); ++i)
AddInput(new Key(i, m_state_in.keyboard[named_keys[i].code]));
// add lights
for (u8 i = 0; i < sizeof(named_lights)/sizeof(*named_lights); ++i)
AddOutput(new Light(i));
// MOUSE
// get caps
@ -155,7 +139,7 @@ void GetMousePos(ControlState* const x, ControlState* const y)
*y = (ControlState)point.y / (ControlState)win_height * 2 - 1;
}
bool KeyboardMouse::UpdateInput()
void KeyboardMouse::UpdateInput()
{
DIMOUSESTATE2 tmp_mouse;
@ -191,50 +175,7 @@ bool KeyboardMouse::UpdateInput()
// update mouse cursor
GetMousePos(&m_state_in.cursor.x, &m_state_in.cursor.y);
return true;
}
return false;
}
bool KeyboardMouse::UpdateOutput()
{
class KInput : public INPUT
{
public:
KInput( const unsigned char key, const bool up = false )
{
memset( this, 0, sizeof(*this) );
type = INPUT_KEYBOARD;
ki.wVk = key;
if (up)
ki.dwFlags = KEYEVENTF_KEYUP;
}
};
std::vector< KInput > kbinputs;
for (unsigned int i = 0; i < sizeof(m_state_out)/sizeof(*m_state_out); ++i)
{
bool want_on = false;
if (m_state_out[i])
want_on = m_state_out[i] > GetTickCount() % 255 ; // light should flash when output is 0.5
// lights are set to their original state when output is zero
if (want_on ^ m_current_state_out[i])
{
kbinputs.push_back(KInput(named_lights[i].code)); // press
kbinputs.push_back(KInput(named_lights[i].code, true)); // release
m_current_state_out[i] ^= 1;
}
}
if (kbinputs.size())
return ( kbinputs.size() == SendInput( (UINT)kbinputs.size(), &kbinputs[0], sizeof( kbinputs[0] ) ) );
else
return true;
}
std::string KeyboardMouse::GetName() const
@ -280,11 +221,6 @@ std::string KeyboardMouse::Cursor::GetName() const
return tmpstr;
}
std::string KeyboardMouse::Light::GetName() const
{
return named_lights[m_index].name;
}
// get/set state
ControlState KeyboardMouse::Key::GetState() const
{
@ -306,10 +242,5 @@ ControlState KeyboardMouse::Cursor::GetState() const
return std::max(0.0, ControlState(m_axis) / (m_positive ? 1.0 : -1.0));
}
void KeyboardMouse::Light::SetState(const ControlState state)
{
//state_out[m_index] = (unsigned char)(state * 255);
}
}
}

View File

@ -76,19 +76,8 @@ private:
const bool m_positive;
};
class Light : public Output
{
public:
std::string GetName() const;
Light(u8 index) : m_index(index) {}
void SetState(ControlState state);
private:
const u8 m_index;
};
public:
bool UpdateInput();
bool UpdateOutput();
void UpdateInput() override;
KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device);
~KeyboardMouse();
@ -103,8 +92,6 @@ private:
DWORD m_last_update;
State m_state_in;
unsigned char m_state_out[3]; // NUM CAPS SCROLL
bool m_current_state_out[3]; // NUM CAPS SCROLL
};
}

View File

@ -66,20 +66,6 @@ Device::Output* Device::FindOutput(const std::string &name) const
return nullptr;
}
//
// Device :: ClearInputState
//
// Device classes should override this function
// ControllerInterface will call this when the device returns failure during UpdateInput
// used to try to set all buttons and axes to their default state when user unplugs a gamepad during play
// buttons/axes that were held down at the time of unplugging should be seen as not pressed after unplugging
//
void Device::ClearInputState()
{
// this is going to be called for every UpdateInput call that fails
// kinda slow but, w/e, should only happen when user unplugs a device while playing
}
bool Device::Control::InputGateOn()
{
if (SConfig::GetInstance().m_BackgroundInput)

View File

@ -100,10 +100,7 @@ public:
virtual std::string GetName() const = 0;
virtual int GetId() const = 0;
virtual std::string GetSource() const = 0;
virtual bool UpdateInput() = 0;
virtual bool UpdateOutput() = 0;
virtual void ClearInputState();
virtual void UpdateInput() {}
const std::vector<Input*>& Inputs() const { return m_inputs; }
const std::vector<Output*>& Outputs() const { return m_outputs; }

View File

@ -38,17 +38,6 @@ static const ForceType force_type_names[] =
//{GUID_Friction, "Friction"},
};
ForceFeedbackDevice::~ForceFeedbackDevice()
{
// release the ff effect iface's
for (EffectState& state : m_state_out)
{
state.iface->Stop();
state.iface->Unload();
state.iface->Release();
}
}
bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, int cAxes)
{
if (cAxes == 0)
@ -107,14 +96,12 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i
LPDIRECTINPUTEFFECT pEffect;
if (SUCCEEDED(device->CreateEffect(f.guid, &eff, &pEffect, nullptr)))
{
m_state_out.push_back(EffectState(pEffect));
if (f.guid == GUID_ConstantForce)
AddOutput(new ForceConstant(f.name, m_state_out.back()));
AddOutput(new ForceConstant(f.name, pEffect));
else if (f.guid == GUID_RampForce)
AddOutput(new ForceRamp(f.name, m_state_out.back()));
AddOutput(new ForceRamp(f.name, pEffect));
else
AddOutput(new ForcePeriodic(f.name, m_state_out.back()));
AddOutput(new ForcePeriodic(f.name, pEffect));
}
}
@ -133,40 +120,32 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i
return true;
}
bool ForceFeedbackDevice::UpdateOutput()
template<typename P>
ForceFeedbackDevice::Force<P>::~Force()
{
size_t ok_count = 0;
m_iface->Stop();
m_iface->Unload();
m_iface->Release();
}
DIEFFECT eff;
memset(&eff, 0, sizeof(eff));
template<typename P>
void ForceFeedbackDevice::Force<P>::Update()
{
DIEFFECT eff = {};
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
for (EffectState& state : m_state_out)
{
if (state.params)
{
if (state.size)
{
eff.cbTypeSpecificParams = state.size;
eff.lpvTypeSpecificParams = state.params;
eff.cbTypeSpecificParams = sizeof(P);
eff.lpvTypeSpecificParams = &params;
// set params and start effect
ok_count += SUCCEEDED(state.iface->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START));
}
else
{
ok_count += SUCCEEDED(state.iface->Stop());
}
m_iface->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START);
}
state.params = nullptr;
}
else
{
++ok_count;
}
}
return (m_state_out.size() == ok_count);
template<typename P>
void ForceFeedbackDevice::Force<P>::Stop()
{
m_iface->Stop();
}
template<>
@ -174,15 +153,14 @@ void ForceFeedbackDevice::ForceConstant::SetState(const ControlState state)
{
const LONG new_val = LONG(10000 * state);
LONG &val = params.lMagnitude;
if (val != new_val)
{
val = new_val;
m_state.params = &params; // tells UpdateOutput the state has changed
if (params.lMagnitude == new_val)
return;
// tells UpdateOutput to either start or stop the force
m_state.size = new_val ? sizeof(params) : 0;
}
params.lMagnitude = new_val;
if (new_val)
Update();
else
Stop();
}
template<>
@ -190,14 +168,14 @@ void ForceFeedbackDevice::ForceRamp::SetState(const ControlState state)
{
const LONG new_val = LONG(10000 * state);
if (params.lStart != new_val)
{
params.lStart = params.lEnd = new_val;
m_state.params = &params; // tells UpdateOutput the state has changed
if (params.lStart == new_val)
return;
// tells UpdateOutput to either start or stop the force
m_state.size = new_val ? sizeof(params) : 0;
}
params.lStart = params.lEnd = new_val;
if (new_val)
Update();
else
Stop();
}
template<>
@ -205,22 +183,19 @@ void ForceFeedbackDevice::ForcePeriodic::SetState(const ControlState state)
{
const DWORD new_val = DWORD(10000 * state);
DWORD &val = params.dwMagnitude;
if (val != new_val)
{
val = new_val;
//params.dwPeriod = 0;//DWORD(0.05 * DI_SECONDS); // zero is working fine for me
if (params.dwMagnitude == new_val)
return;
m_state.params = &params; // tells UpdateOutput the state has changed
// tells UpdateOutput to either start or stop the force
m_state.size = new_val ? sizeof(params) : 0;
}
params.dwMagnitude = new_val;
if (new_val)
Update();
else
Stop();
}
template <typename P>
ForceFeedbackDevice::Force<P>::Force(const std::string& name, EffectState& state)
: m_name(name), m_state(state)
ForceFeedbackDevice::Force<P>::Force(const std::string& name, LPDIRECTINPUTEFFECT iface)
: m_name(name), m_iface(iface)
{
memset(&params, 0, sizeof(params));
}

View File

@ -25,25 +25,19 @@ namespace ForceFeedback
class ForceFeedbackDevice : public Core::Device
{
private:
struct EffectState
{
EffectState(LPDIRECTINPUTEFFECT eff) : iface(eff), params(nullptr), size(0) {}
LPDIRECTINPUTEFFECT iface;
void* params; // null when force hasn't changed
u8 size; // zero when force should stop
};
template <typename P>
class Force : public Output
{
public:
std::string GetName() const;
Force(const std::string& name, EffectState& state);
Force(const std::string& name, LPDIRECTINPUTEFFECT iface);
~Force();
void SetState(ControlState state);
void Update();
void Stop();
private:
const std::string m_name;
EffectState& m_state;
LPDIRECTINPUTEFFECT m_iface;
P params;
};
typedef Force<DICONSTANTFORCE> ForceConstant;
@ -52,11 +46,6 @@ private:
public:
bool InitForceFeedback(const LPDIRECTINPUTDEVICE8, int cAxes);
bool UpdateOutput();
virtual ~ForceFeedbackDevice();
private:
std::list<EffectState> m_state_out;
};

View File

@ -72,8 +72,6 @@ private:
};
public:
bool UpdateInput();
Joystick(IOHIDDeviceRef device, std::string name, int index);
~Joystick();

View File

@ -90,11 +90,6 @@ Joystick::~Joystick()
m_ff_device->Release();
}
bool Joystick::UpdateInput()
{
return true;
}
std::string Joystick::GetName() const
{
return m_device_name;

View File

@ -53,8 +53,7 @@ private:
};
public:
bool UpdateInput();
bool UpdateOutput();
void UpdateInput() override;
Keyboard(IOHIDDeviceRef device, std::string name, int index, void *window);

View File

@ -54,7 +54,7 @@ Keyboard::Keyboard(IOHIDDeviceRef device, std::string name, int index, void *win
AddInput(new Button(i, m_mousebuttons[i]));
}
bool Keyboard::UpdateInput()
void Keyboard::UpdateInput()
{
CGRect bounds = CGRectZero;
uint32_t windowid[1] = { m_windowid };
@ -85,13 +85,6 @@ bool Keyboard::UpdateInput()
m_mousebuttons[0] = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonLeft);
m_mousebuttons[1] = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight);
m_mousebuttons[2] = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonCenter);
return true;
}
bool Keyboard::UpdateOutput()
{
return true;
}
std::string Keyboard::GetName() const

View File

@ -121,40 +121,26 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsi
// constant effect
if (supported_effects & SDL_HAPTIC_CONSTANT)
{
m_state_out.push_back(EffectIDState());
AddOutput(new ConstantEffect(m_state_out.back()));
}
AddOutput(new ConstantEffect(m_haptic));
// ramp effect
if (supported_effects & SDL_HAPTIC_RAMP)
{
m_state_out.push_back(EffectIDState());
AddOutput(new RampEffect(m_state_out.back()));
}
AddOutput(new RampEffect(m_haptic));
// sine effect
if (supported_effects & SDL_HAPTIC_SINE)
{
m_state_out.push_back(EffectIDState());
AddOutput(new SineEffect(m_state_out.back()));
}
AddOutput(new SineEffect(m_haptic));
#ifdef SDL_HAPTIC_SQUARE
// square effect
if (supported_effects & SDL_HAPTIC_SQUARE)
{
m_state_out.push_back(EffectIDState());
AddOutput(new SquareEffect(m_state_out.back()));
}
AddOutput(new SquareEffect(m_haptic));
#endif // defined(SDL_HAPTIC_SQUARE)
// triangle effect
if (supported_effects & SDL_HAPTIC_TRIANGLE)
{
m_state_out.push_back(EffectIDState());
AddOutput(new TriangleEffect(m_state_out.back()));
}
AddOutput(new TriangleEffect(m_haptic));
}
#endif
@ -167,13 +153,6 @@ Joystick::~Joystick()
{
// stop/destroy all effects
SDL_HapticStopAll(m_haptic);
for (auto &i : m_state_out)
{
if (i.id != -1)
{
SDL_HapticDestroyEffect(m_haptic, i.id);
}
}
// close haptic first
SDL_HapticClose(m_haptic);
}
@ -184,6 +163,26 @@ Joystick::~Joystick()
}
#ifdef USE_SDL_HAPTIC
void Joystick::HapticEffect::Update()
{
if (m_id == -1 && m_effect.type > 0)
{
m_id = SDL_HapticNewEffect(m_haptic, &m_effect);
if (m_id > -1)
SDL_HapticRunEffect(m_haptic, m_id, 1);
}
else if (m_id > -1 && m_effect.type == 0)
{
SDL_HapticStopEffect(m_haptic, m_id);
SDL_HapticDestroyEffect(m_haptic, m_id);
m_id = -1;
}
else if (m_id > -1)
{
SDL_HapticUpdateEffect(m_haptic, m_id, &m_effect);
}
}
std::string Joystick::ConstantEffect::GetName() const
{
return "Constant";
@ -215,58 +214,51 @@ void Joystick::ConstantEffect::SetState(ControlState state)
{
if (state)
{
m_effect.effect.type = SDL_HAPTIC_CONSTANT;
m_effect.effect.constant.length = SDL_HAPTIC_INFINITY;
m_effect.type = SDL_HAPTIC_CONSTANT;
m_effect.constant.length = SDL_HAPTIC_INFINITY;
}
else
{
m_effect.effect.type = 0;
m_effect.type = 0;
}
const Sint16 old = m_effect.effect.constant.level;
m_effect.effect.constant.level = (Sint16)(state * 0x7FFF);
if (old != m_effect.effect.constant.level)
m_effect.changed = true;
m_effect.constant.level = (Sint16)(state * 0x7FFF);
Update();
}
void Joystick::RampEffect::SetState(ControlState state)
{
if (state)
{
m_effect.effect.type = SDL_HAPTIC_RAMP;
m_effect.effect.ramp.length = SDL_HAPTIC_INFINITY;
m_effect.type = SDL_HAPTIC_RAMP;
m_effect.ramp.length = SDL_HAPTIC_INFINITY;
}
else
{
m_effect.effect.type = 0;
m_effect.type = 0;
}
const Sint16 old = m_effect.effect.ramp.start;
m_effect.effect.ramp.start = (Sint16)(state * 0x7FFF);
if (old != m_effect.effect.ramp.start)
m_effect.changed = true;
m_effect.ramp.start = (Sint16)(state * 0x7FFF);
Update();
}
void Joystick::SineEffect::SetState(ControlState state)
{
if (state)
{
m_effect.effect.type = SDL_HAPTIC_SINE;
m_effect.effect.periodic.length = 250;
m_effect.type = SDL_HAPTIC_SINE;
m_effect.periodic.length = 250;
}
else
{
m_effect.effect.type = 0;
m_effect.type = 0;
}
const Sint16 old = m_effect.effect.periodic.magnitude;
m_effect.effect.periodic.period = 5;
m_effect.effect.periodic.magnitude = (Sint16)(state * 0x5000);
m_effect.effect.periodic.attack_length = 0;
m_effect.effect.periodic.fade_length = 500;
if (old != m_effect.effect.periodic.magnitude)
m_effect.changed = true;
m_effect.periodic.period = 5;
m_effect.periodic.magnitude = (Sint16)(state * 0x5000);
m_effect.periodic.attack_length = 0;
m_effect.periodic.fade_length = 500;
Update();
}
#ifdef SDL_HAPTIC_SQUARE
@ -274,22 +266,19 @@ void Joystick::SquareEffect::SetState(ControlState state)
{
if (state)
{
m_effect.effect.type = SDL_HAPTIC_SQUARE;
m_effect.effect.periodic.length = 250;
m_effect.type = SDL_HAPTIC_SQUARE;
m_effect.periodic.length = 250;
}
else
{
m_effect.effect.type = 0;
m_effect.type = 0;
}
const Sint16 old = m_effect.effect.periodic.magnitude;
m_effect.effect.periodic.period = 5;
m_effect.effect.periodic.magnitude = state * 0x5000;
m_effect.effect.periodic.attack_length = 0;
m_effect.effect.periodic.fade_length = 100;
if (old != m_effect.effect.periodic.magnitude)
m_effect.changed = true;
m_effect.periodic.period = 5;
m_effect.periodic.magnitude = state * 0x5000;
m_effect.periodic.attack_length = 0;
m_effect.periodic.fade_length = 100;
Update();
}
#endif // defined(SDL_HAPTIC_SQUARE)
@ -297,69 +286,26 @@ void Joystick::TriangleEffect::SetState(ControlState state)
{
if (state)
{
m_effect.effect.type = SDL_HAPTIC_TRIANGLE;
m_effect.effect.periodic.length = 250;
m_effect.type = SDL_HAPTIC_TRIANGLE;
m_effect.periodic.length = 250;
}
else
{
m_effect.effect.type = 0;
m_effect.type = 0;
}
const Sint16 old = m_effect.effect.periodic.magnitude;
m_effect.effect.periodic.period = 5;
m_effect.effect.periodic.magnitude = (Sint16)(state * 0x5000);
m_effect.effect.periodic.attack_length = 0;
m_effect.effect.periodic.fade_length = 100;
if (old != m_effect.effect.periodic.magnitude)
m_effect.changed = true;
m_effect.periodic.period = 5;
m_effect.periodic.magnitude = (Sint16)(state * 0x5000);
m_effect.periodic.attack_length = 0;
m_effect.periodic.fade_length = 100;
Update();
}
#endif
bool Joystick::UpdateInput()
void Joystick::UpdateInput()
{
// each joystick is doin this, o well
SDL_JoystickUpdate();
return true;
}
bool Joystick::UpdateOutput()
{
#ifdef USE_SDL_HAPTIC
for (auto &i : m_state_out)
{
if (i.changed) // if SetState was called on this output
{
if (-1 == i.id) // effect isn't currently uploaded
{
if (i.effect.type) // if outputstate is >0 this would be true
{
if ((i.id = SDL_HapticNewEffect(m_haptic, &i.effect)) > -1) // upload the effect
{
SDL_HapticRunEffect(m_haptic, i.id, 1); // run the effect
}
}
}
else // effect is already uploaded
{
if (i.effect.type) // if ouputstate >0
{
SDL_HapticUpdateEffect(m_haptic, i.id, &i.effect); // update the effect
}
else
{
SDL_HapticStopEffect(m_haptic, i.id); // else, stop and remove the effect
SDL_HapticDestroyEffect(m_haptic, i.id);
i.id = -1; // mark it as not uploaded
}
}
i.changed = false;
}
}
#endif
return true;
}
std::string Joystick::GetName() const

View File

@ -33,17 +33,6 @@ class Joystick : public Core::Device
{
private:
#ifdef USE_SDL_HAPTIC
struct EffectIDState
{
EffectIDState() : effect(SDL_HapticEffect()), id(-1), changed(false) {}
SDL_HapticEffect effect;
int id;
bool changed;
};
#endif
class Button : public Core::Device::Input
{
public:
@ -80,62 +69,65 @@ private:
};
#ifdef USE_SDL_HAPTIC
class ConstantEffect : public Output
class HapticEffect : public Output
{
public:
std::string GetName() const override;
ConstantEffect(EffectIDState& effect) : m_effect(effect) {}
void SetState(ControlState state) override;
private:
EffectIDState& m_effect;
HapticEffect(SDL_Haptic* haptic) : m_haptic(haptic), m_id(-1) {}
~HapticEffect() { m_effect.type = 0; Update(); }
protected:
void Update();
SDL_HapticEffect m_effect;
SDL_Haptic* m_haptic;
int m_id;
};
class RampEffect : public Output
class ConstantEffect : public HapticEffect
{
public:
ConstantEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
std::string GetName() const override;
RampEffect(EffectIDState& effect) : m_effect(effect) {}
void SetState(ControlState state) override;
private:
EffectIDState& m_effect;
};
class SineEffect : public Output
class RampEffect : public HapticEffect
{
public:
RampEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
std::string GetName() const override;
void SetState(ControlState state) override;
};
class SineEffect : public HapticEffect
{
public:
SineEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
std::string GetName() const override;
SineEffect(EffectIDState& effect) : m_effect(effect) {}
void SetState(ControlState state) override;
private:
EffectIDState& m_effect;
};
#ifdef SDL_HAPTIC_SQUARE
class SquareEffect : public Output
class SquareEffect : public HapticEffect
{
public:
SquareEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
std::string GetName() const;
SquareEffect(EffectIDState& effect) : m_effect(effect) {}
void SetState(ControlState state);
private:
EffectIDState& m_effect;
};
#endif // defined(SDL_HAPTIC_SQUARE)
class TriangleEffect : public Output
class TriangleEffect : public HapticEffect
{
public:
TriangleEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
std::string GetName() const override;
TriangleEffect(EffectIDState& effect) : m_effect(effect) {}
void SetState(ControlState state) override;
private:
EffectIDState& m_effect;
};
#endif
public:
bool UpdateInput() override;
bool UpdateOutput() override;
void UpdateInput() override;
Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsigned int index);
~Joystick();
@ -150,7 +142,6 @@ private:
const unsigned int m_index;
#ifdef USE_SDL_HAPTIC
std::list<EffectIDState> m_state_out;
SDL_Haptic* m_haptic;
#endif
};

View File

@ -121,7 +121,6 @@ Device::Device(const XINPUT_CAPABILITIES& caps, u8 index)
: m_index(index), m_subtype(caps.SubType)
{
ZeroMemory(&m_state_out, sizeof(m_state_out));
ZeroMemory(&m_current_state_out, sizeof(m_current_state_out));
// XInputGetCaps seems to always claim all capabilities are supported
// but I will leave all this stuff in, incase m$ fixes xinput up a bit
@ -162,14 +161,9 @@ Device::Device(const XINPUT_CAPABILITIES& caps, u8 index)
{
//WORD val = (&caps.Vibration.wLeftMotorSpeed)[i]; // should be max value / nope, more lies
if ((&caps.Vibration.wLeftMotorSpeed)[i])
AddOutput(new Motor(i, (&m_state_out.wLeftMotorSpeed)[i], 65535));
AddOutput(new Motor(i, this, (&m_state_out.wLeftMotorSpeed)[i], 65535));
}
ClearInputState();
}
void Device::ClearInputState()
{
ZeroMemory(&m_state_in, sizeof(m_state_in));
}
@ -208,24 +202,14 @@ std::string Device::GetSource() const
// Update I/O
bool Device::UpdateInput()
void Device::UpdateInput()
{
return (ERROR_SUCCESS == PXInputGetState(m_index, &m_state_in));
PXInputGetState(m_index, &m_state_in);
}
bool Device::UpdateOutput()
void Device::UpdateMotors()
{
// this if statement is to make rumble work better when multiple ControllerInterfaces are using the device
// only calls XInputSetState if the state changed
if (memcmp(&m_state_out, &m_current_state_out, sizeof(m_state_out)))
{
m_current_state_out = m_state_out;
return (ERROR_SUCCESS == PXInputSetState(m_index, &m_state_out));
}
else
{
return true;
}
PXInputSetState(m_index, &m_state_out);
}
// GET name/source/id
@ -270,6 +254,7 @@ ControlState Device::Axis::GetState() const
void Device::Motor::SetState(ControlState state)
{
m_motor = (WORD)(state * m_range);
m_parent->UpdateMotors();
}
}

View File

@ -64,19 +64,17 @@ private:
{
public:
std::string GetName() const;
Motor(u8 index, WORD& motor, WORD range) : m_index(index), m_motor(motor), m_range(range) {}
Motor(u8 index, Device* parent, WORD &motor, WORD range) : m_index(index), m_parent(parent), m_motor(motor), m_range(range) {}
void SetState(ControlState state);
private:
WORD& m_motor;
const WORD m_range;
const u8 m_index;
Device* m_parent;
};
public:
bool UpdateInput();
bool UpdateOutput();
void ClearInputState();
void UpdateInput() override;
Device(const XINPUT_CAPABILITIES& capabilities, u8 index);
@ -84,9 +82,11 @@ public:
int GetId() const;
std::string GetSource() const;
void UpdateMotors();
private:
XINPUT_STATE m_state_in;
XINPUT_VIBRATION m_state_out, m_current_state_out;
XINPUT_VIBRATION m_state_out;
const BYTE m_subtype;
const u8 m_index;
};

View File

@ -208,7 +208,7 @@ void KeyboardMouse::UpdateCursor()
m_state.cursor.y = win_y / (float)win_attribs.height * 2 - 1;
}
bool KeyboardMouse::UpdateInput()
void KeyboardMouse::UpdateInput()
{
XFlush(m_display);
@ -282,13 +282,6 @@ bool KeyboardMouse::UpdateInput()
m_state.axis.y *= MOUSE_AXIS_SMOOTHING;
m_state.axis.y += delta_y;
m_state.axis.y /= MOUSE_AXIS_SMOOTHING+1.0f;
return true;
}
bool KeyboardMouse::UpdateOutput()
{
return true;
}
std::string KeyboardMouse::GetName() const

View File

@ -98,8 +98,7 @@ private:
void UpdateCursor();
public:
bool UpdateInput() override;
bool UpdateOutput() override;
void UpdateInput() override;
KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid);
~KeyboardMouse();

View File

@ -49,7 +49,7 @@ KeyboardMouse::~KeyboardMouse()
XCloseDisplay(m_display);
}
bool KeyboardMouse::UpdateInput()
void KeyboardMouse::UpdateInput()
{
XQueryKeymap(m_display, m_state.keyboard);
@ -64,16 +64,8 @@ bool KeyboardMouse::UpdateInput()
// the mouse position as a range from -1 to 1
m_state.cursor.x = (float)win_x / (float)win_attribs.width * 2 - 1;
m_state.cursor.y = (float)win_y / (float)win_attribs.height * 2 - 1;
return true;
}
bool KeyboardMouse::UpdateOutput()
{
return true;
}
std::string KeyboardMouse::GetName() const
{
return "Keyboard Mouse";

View File

@ -70,8 +70,7 @@ private:
};
public:
bool UpdateInput() override;
bool UpdateOutput() override;
void UpdateInput() override;
KeyboardMouse(Window window);
~KeyboardMouse();