Squashed 'external/gainput/' content from commit 2be0a50

git-subtree-dir: external/gainput
git-subtree-split: 2be0a50089eafcc6fccb66142180082e48f27f4c
This commit is contained in:
Simone
2024-01-22 08:51:55 +01:00
commit 4e42229bdd
170 changed files with 31921 additions and 0 deletions

View File

@@ -0,0 +1,274 @@
#include <gainput/gainput.h>
#include <gainput/GainputDebugRenderer.h>
#include "GainputInputDevicePadImpl.h"
#include <gainput/GainputInputDeltaState.h>
#include <gainput/GainputHelpers.h>
#include <gainput/GainputLog.h>
#if defined(GAINPUT_PLATFORM_LINUX)
#include "GainputInputDevicePadLinux.h"
#elif defined(GAINPUT_PLATFORM_WIN)
#include "GainputInputDevicePadWin.h"
#elif defined(GAINPUT_PLATFORM_IOS) || defined(GAINPUT_PLATFORM_TVOS)
#include "GainputInputDevicePadIos.h"
#elif defined(GAINPUT_PLATFORM_MAC)
#include "GainputInputDevicePadMac.h"
#elif defined(GAINPUT_PLATFORM_ANDROID)
#include "GainputInputDevicePadAndroid.h"
#endif
#include "GainputInputDevicePadNull.h"
namespace gainput
{
namespace
{
struct DeviceButtonInfo
{
ButtonType type;
const char* name;
};
DeviceButtonInfo deviceButtonInfos[] =
{
{ BT_FLOAT, "pad_left_stick_x" },
{ BT_FLOAT, "pad_left_stick_y" },
{ BT_FLOAT, "pad_right_stick_x" },
{ BT_FLOAT, "pad_right_stick_y" },
{ BT_FLOAT, "pad_axis_4" },
{ BT_FLOAT, "pad_axis_5" },
{ BT_FLOAT, "pad_axis_6" },
{ BT_FLOAT, "pad_axis_7" },
{ BT_FLOAT, "pad_axis_8" },
{ BT_FLOAT, "pad_axis_9" },
{ BT_FLOAT, "pad_axis_10" },
{ BT_FLOAT, "pad_axis_11" },
{ BT_FLOAT, "pad_axis_12" },
{ BT_FLOAT, "pad_axis_13" },
{ BT_FLOAT, "pad_axis_14" },
{ BT_FLOAT, "pad_axis_15" },
{ BT_FLOAT, "pad_axis_16" },
{ BT_FLOAT, "pad_axis_17" },
{ BT_FLOAT, "pad_axis_18" },
{ BT_FLOAT, "pad_axis_19" },
{ BT_FLOAT, "pad_axis_20" },
{ BT_FLOAT, "pad_axis_21" },
{ BT_FLOAT, "pad_axis_22" },
{ BT_FLOAT, "pad_axis_23" },
{ BT_FLOAT, "pad_axis_24" },
{ BT_FLOAT, "pad_axis_25" },
{ BT_FLOAT, "pad_axis_26" },
{ BT_FLOAT, "pad_axis_27" },
{ BT_FLOAT, "pad_axis_28" },
{ BT_FLOAT, "pad_axis_29" },
{ BT_FLOAT, "pad_axis_30" },
{ BT_FLOAT, "pad_axis_31" },
{ BT_FLOAT, "pad_acceleration_x" },
{ BT_FLOAT, "pad_acceleration_y" },
{ BT_FLOAT, "pad_acceleration_z" },
{ BT_FLOAT, "pad_gravity_x" },
{ BT_FLOAT, "pad_gravity_y" },
{ BT_FLOAT, "pad_gravity_z" },
{ BT_FLOAT, "pad_gyroscope_x" },
{ BT_FLOAT, "pad_gyroscope_y" },
{ BT_FLOAT, "pad_gyroscope_z" },
{ BT_FLOAT, "pad_magneticfield_x" },
{ BT_FLOAT, "pad_magneticfield_y" },
{ BT_FLOAT, "pad_magneticfield_z" },
{ BT_BOOL, "pad_button_start"},
{ BT_BOOL, "pad_button_select"},
{ BT_BOOL, "pad_button_left"},
{ BT_BOOL, "pad_button_right"},
{ BT_BOOL, "pad_button_up"},
{ BT_BOOL, "pad_button_down"},
{ BT_BOOL, "pad_button_a"},
{ BT_BOOL, "pad_button_b"},
{ BT_BOOL, "pad_button_x"},
{ BT_BOOL, "pad_button_y"},
{ BT_BOOL, "pad_button_l1"},
{ BT_BOOL, "pad_button_r1"},
{ BT_BOOL, "pad_button_l2"},
{ BT_BOOL, "pad_button_r2"},
{ BT_BOOL, "pad_button_l3"},
{ BT_BOOL, "pad_button_r3"},
{ BT_BOOL, "pad_button_home"},
{ BT_BOOL, "pad_button_17"},
{ BT_BOOL, "pad_button_18"},
{ BT_BOOL, "pad_button_19"},
{ BT_BOOL, "pad_button_20"},
{ BT_BOOL, "pad_button_21"},
{ BT_BOOL, "pad_button_22"},
{ BT_BOOL, "pad_button_23"},
{ BT_BOOL, "pad_button_24"},
{ BT_BOOL, "pad_button_25"},
{ BT_BOOL, "pad_button_26"},
{ BT_BOOL, "pad_button_27"},
{ BT_BOOL, "pad_button_28"},
{ BT_BOOL, "pad_button_29"},
{ BT_BOOL, "pad_button_30"},
{ BT_BOOL, "pad_button_31"}
};
const unsigned PadButtonCount = PadButtonCount_;
const unsigned PadAxisCount = PadButtonAxisCount_;
}
InputDevicePad::InputDevicePad(InputManager& manager, DeviceId device, unsigned index, DeviceVariant /*variant*/) :
InputDevice(manager, device, index == InputDevice::AutoIndex ? manager.GetDeviceCountByType(DT_PAD) : 0),
impl_(0)
{
state_ = manager.GetAllocator().New<InputState>(manager.GetAllocator(), PadButtonCount + PadAxisCount);
GAINPUT_ASSERT(state_);
previousState_ = manager.GetAllocator().New<InputState>(manager.GetAllocator(), PadButtonCount + PadAxisCount);
GAINPUT_ASSERT(previousState_);
#if defined(GAINPUT_PLATFORM_LINUX)
impl_ = manager.GetAllocator().New<InputDevicePadImplLinux>(manager, *this, index_, *state_, *previousState_);
#elif defined(GAINPUT_PLATFORM_WIN)
impl_ = manager.GetAllocator().New<InputDevicePadImplWin>(manager, *this, index_, *state_, *previousState_);
#elif defined(GAINPUT_PLATFORM_IOS) || defined(GAINPUT_PLATFORM_TVOS)
impl_ = manager.GetAllocator().New<InputDevicePadImplIos>(manager, *this, index_, *state_, *previousState_);
#elif defined(GAINPUT_PLATFORM_MAC)
impl_ = manager.GetAllocator().New<InputDevicePadImplMac>(manager, *this, index_, *state_, *previousState_);
#elif defined(GAINPUT_PLATFORM_ANDROID)
impl_ = manager.GetAllocator().New<InputDevicePadImplAndroid>(manager, *this, index_, *state_, *previousState_);
#endif
if (!impl_)
{
impl_ = manager.GetAllocator().New<InputDevicePadImplNull>(manager, *this, index_, *state_, *previousState_);
}
GAINPUT_ASSERT(impl_);
SetDeadZone(PadButtonLeftStickX, 0.15f);
SetDeadZone(PadButtonLeftStickY, 0.15f);
SetDeadZone(PadButtonRightStickX, 0.15f);
SetDeadZone(PadButtonRightStickY, 0.15f);
}
InputDevicePad::~InputDevicePad()
{
manager_.GetAllocator().Delete(state_);
manager_.GetAllocator().Delete(previousState_);
manager_.GetAllocator().Delete(impl_);
}
void
InputDevicePad::InternalUpdate(InputDeltaState* delta)
{
impl_->Update(delta);
if ((manager_.IsDebugRenderingEnabled() || IsDebugRenderingEnabled())
&& manager_.GetDebugRenderer())
{
DebugRenderer* debugRenderer = manager_.GetDebugRenderer();
InputState* state = GetInputState();
char buf[64];
float x = 0.4f;
float y = 0.2f;
for (int i = PadButtonStart; i < PadButtonMax_; ++i)
{
if (state->GetBool(i))
{
GetButtonName(i, buf, 64);
debugRenderer->DrawText(x, y, buf);
y += 0.025f;
}
}
x = 0.8f;
y = 0.2f;
const float circleRadius = 0.1f;
debugRenderer->DrawCircle(x, y, 0.01f);
debugRenderer->DrawCircle(x, y, circleRadius);
float dirX = state->GetFloat(PadButtonLeftStickX) * circleRadius;
float dirY = state->GetFloat(PadButtonLeftStickY) * circleRadius;
debugRenderer->DrawLine(x, y, x + dirX, y + dirY);
y = 0.6f;
debugRenderer->DrawCircle(x, y, 0.01f);
debugRenderer->DrawCircle(x, y, circleRadius);
dirX = state->GetFloat(PadButtonRightStickX) * circleRadius;
dirY = state->GetFloat(PadButtonRightStickY) * circleRadius;
debugRenderer->DrawLine(x, y, x + dirX, y + dirY);
}
}
InputDevice::DeviceState
InputDevicePad::InternalGetState() const
{
return impl_->GetState();
}
InputDevice::DeviceVariant
InputDevicePad::GetVariant() const
{
return impl_->GetVariant();
}
bool
InputDevicePad::IsValidButtonId(DeviceButtonId deviceButton) const
{
return impl_->IsValidButton(deviceButton);
}
size_t
InputDevicePad::GetAnyButtonDown(DeviceButtonSpec* outButtons, size_t maxButtonCount) const
{
GAINPUT_ASSERT(outButtons);
GAINPUT_ASSERT(maxButtonCount > 0);
return CheckAllButtonsDown(outButtons, maxButtonCount, PadButtonLeftStickX, PadButtonMax_);
}
size_t
InputDevicePad::GetButtonName(DeviceButtonId deviceButton, char* buffer, size_t bufferLength) const
{
GAINPUT_ASSERT(IsValidButtonId(deviceButton));
GAINPUT_ASSERT(buffer);
GAINPUT_ASSERT(bufferLength > 0);
strncpy(buffer, deviceButtonInfos[deviceButton].name, bufferLength);
buffer[bufferLength-1] = 0;
const size_t nameLen = strlen(deviceButtonInfos[deviceButton].name);
return nameLen >= bufferLength ? bufferLength : nameLen+1;
}
ButtonType
InputDevicePad::GetButtonType(DeviceButtonId deviceButton) const
{
return deviceButtonInfos[deviceButton].type;
}
DeviceButtonId
InputDevicePad::GetButtonByName(const char* name) const
{
GAINPUT_ASSERT(name);
for (unsigned i = 0; i < PadButtonCount + PadAxisCount; ++i)
{
if (strcmp(name, deviceButtonInfos[i].name) == 0)
{
return DeviceButtonId(i);
}
}
return InvalidDeviceButtonId;
}
InputState*
InputDevicePad::GetNextInputState()
{
return impl_->GetNextInputState();
}
bool
InputDevicePad::Vibrate(float leftMotor, float rightMotor)
{
return impl_->Vibrate(leftMotor, rightMotor);
}
}

View File

@@ -0,0 +1,79 @@
#ifndef GAINPUTINPUTDEVICEPADANDROID_H_
#define GAINPUTINPUTDEVICEPADANDROID_H_
#include "GainputInputDevicePadImpl.h"
#include <gainput/GainputHelpers.h>
namespace gainput
{
class InputDevicePadImplAndroid : public InputDevicePadImpl
{
public:
InputDevicePadImplAndroid(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState) :
manager_(manager),
device_(device),
state_(&state),
previousState_(&previousState),
nextState_(manager.GetAllocator(), PadButtonMax_),
delta_(0),
index_(index),
deviceState_(InputDevice::DS_UNAVAILABLE)
{
(void)previousState;
GAINPUT_ASSERT(index_ < MaxPadCount);
}
~InputDevicePadImplAndroid()
{
}
InputDevice::DeviceVariant GetVariant() const
{
return InputDevice::DV_STANDARD;
}
void Update(InputDeltaState* delta)
{
delta_ = delta;
*state_ = nextState_;
}
InputDevice::DeviceState GetState() const
{
return deviceState_;
}
void SetState(InputDevice::DeviceState state)
{
deviceState_ = state;
}
bool IsValidButton(DeviceButtonId deviceButton) const
{
return deviceButton < PadButtonMax_;
}
bool Vibrate(float /*leftMotor*/, float /*rightMotor*/)
{
return false; // TODO
}
InputState* GetNextInputState() { return &nextState_; }
private:
InputManager& manager_;
InputDevice& device_;
InputState* state_;
InputState* previousState_;
InputState nextState_;
InputDeltaState* delta_;
unsigned index_;
InputDevice::DeviceState deviceState_;
};
}
#endif

View File

@@ -0,0 +1,23 @@
#ifndef GAINPUTINPUTDEVICEPADIMPL_H_
#define GAINPUTINPUTDEVICEPADIMPL_H_
namespace gainput
{
class InputDevicePadImpl
{
public:
virtual ~InputDevicePadImpl() { }
virtual InputDevice::DeviceVariant GetVariant() const = 0;
virtual InputDevice::DeviceState GetState() const { return InputDevice::DS_OK; }
virtual void Update(InputDeltaState* delta) = 0;
virtual bool IsValidButton(DeviceButtonId deviceButton) const = 0;
virtual bool Vibrate(float leftMotor, float rightMotor) = 0;
virtual InputState* GetNextInputState() { return 0; }
};
}
#endif

View File

@@ -0,0 +1,60 @@
#ifndef GAINPUTINPUTDEVICEPADIOS_H_
#define GAINPUTINPUTDEVICEPADIOS_H_
#include <gainput/GainputContainers.h>
namespace gainput
{
class InputDevicePadImplIos : public InputDevicePadImpl
{
public:
InputDevicePadImplIos(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState);
~InputDevicePadImplIos();
InputDevice::DeviceVariant GetVariant() const
{
return InputDevice::DV_STANDARD;
}
void Update(InputDeltaState* delta);
InputDevice::DeviceState GetState() const
{
return deviceState_;
}
bool IsValidButton(DeviceButtonId deviceButton) const;
typedef gainput::Array<void *> GlobalControllerList;
static GlobalControllerList* mappedControllers_;
bool Vibrate(float leftMotor, float rightMotor)
{
return false;
}
private:
InputManager& manager_;
InputDevice& device_;
unsigned index_;
InputState& state_;
InputState& previousState_;
InputDevice::DeviceState deviceState_;
bool pausePressed_;
bool isMicro_;
bool isNormal_;
bool isExtended_;
bool supportsMotion_;
bool isRemote_;
void* gcController_;
void UpdateRemote_(InputDeltaState* delta);
void UpdateGamepad_(InputDeltaState* delta);
};
}
#endif

View File

@@ -0,0 +1,364 @@
#include <gainput/gainput.h>
#if defined(GAINPUT_PLATFORM_IOS) || defined(GAINPUT_PLATFORM_TVOS)
#include "GainputInputDevicePadImpl.h"
#include <gainput/GainputInputDeltaState.h>
#include <gainput/GainputHelpers.h>
#include <gainput/GainputLog.h>
#include "GainputInputDevicePadIos.h"
#import <GameController/GameController.h>
namespace gainput
{
InputDevicePadImplIos::GlobalControllerList* InputDevicePadImplIos::mappedControllers_;
namespace
{
static bool isAppleTvRemote(GCController* controller)
{
#if defined(GAINPUT_PLATFORM_TVOS)
if (!controller)
{
return false;
}
GCGamepad* gamepad = [controller gamepad];
GCMicroGamepad* microGamepad = [controller microGamepad];
GCExtendedGamepad* extgamepad = [controller extendedGamepad];
GCMotion* motion = [controller motion];
return
microGamepad != NULL &&
motion != NULL &&
gamepad == NULL &&
extgamepad == NULL;
#else
return false;
#endif
}
static GCController* getGcController(void* c)
{
return static_cast<GCController*>(c);
}
static bool IsValidHardwareController(GCController const * controller)
{
if (controller == NULL) return false;
NSArray* controllers = [GCController controllers];
const std::size_t controllerCount = [controllers count];
for (std::size_t i = 0; i < controllerCount; ++i)
{
GCController * currentController = controllers[i];
if (currentController == controller)
{
return true;
}
}
return false;
}
// Removes all invalid controller mappings (controllers that are not available anymore)
static void CleanDisconnectedControllersFromMapping()
{
if (!InputDevicePadImplIos::mappedControllers_)
{
return;
}
InputDevicePadImplIos::GlobalControllerList& mapping = *InputDevicePadImplIos::mappedControllers_;
for (InputDevicePadImplIos::GlobalControllerList::iterator it = mapping.begin(); it != mapping.end(); )
{
GCController const* controller = static_cast<GCController*>(*it);
if (!IsValidHardwareController(controller))
{
it = mapping.erase(it);
}
else
{
++it;
}
}
}
// Returns true if this controller is already mapped to a gainput pad, otherwise false
static bool IsMapped(GCController* controller)
{
if (0 == controller
|| !InputDevicePadImplIos::mappedControllers_)
{
return false;
}
InputDevicePadImplIos::GlobalControllerList& mapping = *InputDevicePadImplIos::mappedControllers_;
for(std::size_t index = 0; index < mapping.size(); ++index)
{
GCController const* mapped_controller = static_cast<GCController*>(mapping[index]);
if (controller == mapped_controller)
{
return true;
}
}
return false;
}
/// Returns the first hardware controller that is not yet mapped
static GCController* GetFirstNonMappedController()
{
NSArray* controllers = [GCController controllers];
const std::size_t controllerCount = [controllers count];
for (std::size_t i = 0; i < controllerCount; ++i)
{
GCController* currentController = controllers[i];
if (!IsMapped(currentController))
{
return currentController;
}
}
return 0;
}
}
InputDevicePadImplIos::InputDevicePadImplIos(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState) :
manager_(manager),
device_(device),
index_(index),
state_(state),
previousState_(previousState),
deviceState_(InputDevice::DS_UNAVAILABLE),
pausePressed_(false),
isMicro_(false),
isNormal_(false),
isExtended_(false),
supportsMotion_(false),
isRemote_(false),
gcController_(0)
{
(void)&this->manager_;
(void)&this->previousState_;
device_.SetDeadZone(PadButtonGyroscopeX, 0.1);
device_.SetDeadZone(PadButtonGyroscopeY, 0.1);
device_.SetDeadZone(PadButtonGyroscopeZ, 0.1);
if (!mappedControllers_)
{
mappedControllers_ = manager.GetAllocator().New<GlobalControllerList>(manager.GetAllocator());
}
}
InputDevicePadImplIos::~InputDevicePadImplIos()
{
}
void InputDevicePadImplIos::Update(InputDeltaState* delta)
{
bool validHardwareController = IsValidHardwareController(getGcController(gcController_));
if (!validHardwareController)
{
CleanDisconnectedControllersFromMapping();
GCController * firstNonMappedController = GetFirstNonMappedController();
if (firstNonMappedController)
{
GCControllerPlayerIndex newIndex = static_cast<GCControllerPlayerIndex>(index_);
if (firstNonMappedController.playerIndex != newIndex)
{
firstNonMappedController.playerIndex = newIndex;
}
// register pause menu button handler
__block InputDevicePadImplIos* block_deviceImpl = this;
firstNonMappedController.controllerPausedHandler = ^(GCController* controller)
{
block_deviceImpl->pausePressed_ = true;
};
}
else
{
deviceState_ = InputDevice::DS_UNAVAILABLE;
return;
}
gcController_ = firstNonMappedController;
}
GAINPUT_ASSERT(gcController_);
if (!gcController_)
{
deviceState_ = InputDevice::DS_UNAVAILABLE;
isExtended_ = false;
supportsMotion_ = false;
return;
}
deviceState_ = InputDevice::DS_OK;
GCController* controller = getGcController(gcController_);
isRemote_ = isAppleTvRemote(controller);
isExtended_ = [controller extendedGamepad] != 0;
#if defined(GAINPUT_PLATFORM_TVOS)
isMicro_ = [controller microGamepad] != 0;
#else
isMicro_ = false;
#endif
isNormal_ = [controller gamepad] != 0;
supportsMotion_ = [controller motion] != 0;
if (isRemote_)
{
UpdateRemote_(delta);
}
else
{
UpdateGamepad_(delta);
}
HandleButton(device_, state_, delta, PadButtonHome, pausePressed_);
pausePressed_ = false;
}
void InputDevicePadImplIos::UpdateRemote_(InputDeltaState* delta)
{
#if defined(GAINPUT_PLATFORM_TVOS)
GCController* controller = getGcController(gcController_);
if (isMicro_)
{
GCMicroGamepad* gamepad = [controller microGamepad];
gamepad.reportsAbsoluteDpadValues = YES;
HandleButton(device_, state_, delta, PadButtonA, gamepad.buttonA.pressed); // Force push on touch area
HandleButton(device_, state_, delta, PadButtonX, gamepad.buttonX.pressed); // Play/Pause Button
HandleAxis(device_, state_, delta, PadButtonAxis30, gamepad.dpad.xAxis.value); // Touch area X
HandleAxis(device_, state_, delta, PadButtonAxis31, gamepad.dpad.yAxis.value); // Touch area Y
}
if (supportsMotion_)
{
GCMotion* motion = [controller motion];
HandleAxis(device_, state_, delta, PadButtonAccelerationX, motion.userAcceleration.x);
HandleAxis(device_, state_, delta, PadButtonAccelerationY, motion.userAcceleration.y);
HandleAxis(device_, state_, delta, PadButtonAccelerationZ, motion.userAcceleration.z);
HandleAxis(device_, state_, delta, PadButtonGravityX, motion.gravity.x);
HandleAxis(device_, state_, delta, PadButtonGravityY, motion.gravity.y);
HandleAxis(device_, state_, delta, PadButtonGravityZ, motion.gravity.z);
// The Siri Remote does not have a gyro.
HandleAxis(device_, state_, delta, PadButtonGyroscopeX, 0.0f);
HandleAxis(device_, state_, delta, PadButtonGyroscopeY, 0.0f);
HandleAxis(device_, state_, delta, PadButtonGyroscopeZ, 0.0f);
}
#endif
}
void InputDevicePadImplIos::UpdateGamepad_(InputDeltaState* delta)
{
GCController* controller = getGcController(gcController_);
if (isExtended_)
{
GCExtendedGamepad* gamepad = [controller extendedGamepad];
HandleButton(device_, state_, delta, PadButtonL1, gamepad.leftShoulder.pressed);
HandleButton(device_, state_, delta, PadButtonR1, gamepad.rightShoulder.pressed);
HandleButton(device_, state_, delta, PadButtonLeft, gamepad.dpad.left.pressed);
HandleButton(device_, state_, delta, PadButtonRight, gamepad.dpad.right.pressed);
HandleButton(device_, state_, delta, PadButtonUp, gamepad.dpad.up.pressed);
HandleButton(device_, state_, delta, PadButtonDown, gamepad.dpad.down.pressed);
HandleButton(device_, state_, delta, PadButtonA, gamepad.buttonA.pressed);
HandleButton(device_, state_, delta, PadButtonB, gamepad.buttonB.pressed);
HandleButton(device_, state_, delta, PadButtonX, gamepad.buttonX.pressed);
HandleButton(device_, state_, delta, PadButtonY, gamepad.buttonY.pressed);
HandleAxis(device_, state_, delta, PadButtonLeftStickX, gamepad.leftThumbstick.xAxis.value);
HandleAxis(device_, state_, delta, PadButtonLeftStickY, gamepad.leftThumbstick.yAxis.value);
HandleAxis(device_, state_, delta, PadButtonRightStickX, gamepad.rightThumbstick.xAxis.value);
HandleAxis(device_, state_, delta, PadButtonRightStickY, gamepad.rightThumbstick.yAxis.value);
HandleButton(device_, state_, delta, PadButtonL2, gamepad.leftTrigger.pressed);
HandleAxis(device_, state_, delta, PadButtonAxis4, gamepad.leftTrigger.value);
HandleButton(device_, state_, delta, PadButtonR2, gamepad.rightTrigger.pressed);
HandleAxis(device_, state_, delta, PadButtonAxis5, gamepad.rightTrigger.value);
}
else if (isNormal_)
{
GCGamepad* gamepad = [controller gamepad];
HandleButton(device_, state_, delta, PadButtonL1, gamepad.leftShoulder.pressed);
HandleButton(device_, state_, delta, PadButtonR1, gamepad.rightShoulder.pressed);
HandleButton(device_, state_, delta, PadButtonLeft, gamepad.dpad.left.pressed);
HandleButton(device_, state_, delta, PadButtonRight, gamepad.dpad.right.pressed);
HandleButton(device_, state_, delta, PadButtonUp, gamepad.dpad.up.pressed);
HandleButton(device_, state_, delta, PadButtonDown, gamepad.dpad.down.pressed);
HandleButton(device_, state_, delta, PadButtonA, gamepad.buttonA.pressed);
HandleButton(device_, state_, delta, PadButtonB, gamepad.buttonB.pressed);
HandleButton(device_, state_, delta, PadButtonX, gamepad.buttonX.pressed);
HandleButton(device_, state_, delta, PadButtonY, gamepad.buttonY.pressed);
}
#if defined(GAINPUT_PLATFORM_TVOS)
else if (isMicro_)
{
GCMicroGamepad * gamepad = [controller microGamepad];
gamepad.reportsAbsoluteDpadValues = YES;
HandleButton(device_, state_, delta, PadButtonA, gamepad.buttonA.pressed); // Force push on touch area
HandleButton(device_, state_, delta, PadButtonX, gamepad.buttonX.pressed); // Play/Pause Button
HandleAxis(device_, state_, delta, PadButtonAxis30, gamepad.dpad.xAxis.value); // Touch area X
HandleAxis(device_, state_, delta, PadButtonAxis31, gamepad.dpad.yAxis.value); // Touch area Y
}
#endif
if (GCMotion* motion = [controller motion])
{
HandleAxis(device_, state_, delta, PadButtonAccelerationX, motion.userAcceleration.x);
HandleAxis(device_, state_, delta, PadButtonAccelerationY, motion.userAcceleration.y);
HandleAxis(device_, state_, delta, PadButtonAccelerationZ, motion.userAcceleration.z);
HandleAxis(device_, state_, delta, PadButtonGravityX, motion.gravity.x);
HandleAxis(device_, state_, delta, PadButtonGravityY, motion.gravity.y);
HandleAxis(device_, state_, delta, PadButtonGravityZ, motion.gravity.z);
const float gyroX = 2.0f * (motion.attitude.x * motion.attitude.z + motion.attitude.w * motion.attitude.y);
const float gyroY = 2.0f * (motion.attitude.y * motion.attitude.z - motion.attitude.w * motion.attitude.x);
const float gyroZ = 1.0f - 2.0f * (motion.attitude.x * motion.attitude.x + motion.attitude.y * motion.attitude.y);
HandleAxis(device_, state_, delta, PadButtonGyroscopeX, gyroX);
HandleAxis(device_, state_, delta, PadButtonGyroscopeY, gyroY);
HandleAxis(device_, state_, delta, PadButtonGyroscopeZ, gyroZ);
}
}
bool InputDevicePadImplIos::IsValidButton(DeviceButtonId deviceButton) const
{
if (supportsMotion_ && deviceButton >= PadButtonAccelerationX && deviceButton <= PadButtonMagneticFieldZ)
{
return true;
}
if (isExtended_)
{
return (deviceButton >= PadButtonLeftStickX && deviceButton <= PadButtonAxis5)
|| (deviceButton >= PadButtonLeft && deviceButton <= PadButtonR2)
|| deviceButton == PadButtonHome;
}
return (deviceButton >= PadButtonLeft && deviceButton <= PadButtonR1)
|| deviceButton == PadButtonHome;
}
}
#endif

View File

@@ -0,0 +1,285 @@
#ifndef GAINPUTINPUTDEVICEPADLINUX_H_
#define GAINPUTINPUTDEVICEPADLINUX_H_
#include <fcntl.h>
#include <unistd.h>
#include <linux/joystick.h>
#include <errno.h>
// Cf. http://www.kernel.org/doc/Documentation/input/joystick-api.txt
// Cf. http://ps3.jim.sh/sixaxis/usb/
namespace gainput
{
/// Maximum negative and positive value for an axis.
const float MaxAxisValue = 32767.0f;
static const char* PadDeviceIds[MaxPadCount] =
{
"/dev/input/js0",
"/dev/input/js1",
"/dev/input/js2",
"/dev/input/js3",
"/dev/input/js4",
"/dev/input/js5",
"/dev/input/js6",
"/dev/input/js7",
"/dev/input/js8",
"/dev/input/js9"
};
class InputDevicePadImplLinux : public InputDevicePadImpl
{
public:
InputDevicePadImplLinux(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState) :
manager_(manager),
device_(device),
state_(state),
index_(index),
deviceState_(InputDevice::DS_UNAVAILABLE),
fd_(-1),
buttonDialect_(manager_.GetAllocator())
{
GAINPUT_ASSERT(index_ < MaxPadCount);
CheckForDevice();
}
~InputDevicePadImplLinux()
{
if (fd_ != -1)
{
close(fd_);
}
}
InputDevice::DeviceVariant GetVariant() const
{
return InputDevice::DV_STANDARD;
}
void Update(InputDeltaState* delta)
{
CheckForDevice();
if (fd_ < 0)
{
return;
}
js_event event;
int c;
while ( (c = read(fd_, &event, sizeof(js_event))) == sizeof(js_event) )
{
event.type &= ~JS_EVENT_INIT;
if (event.type == JS_EVENT_AXIS)
{
GAINPUT_ASSERT(event.number < PadButtonAxisCount_);
DeviceButtonId buttonId = event.number;
const float value = float(event.value)/MaxAxisValue;
if (axisDialect_.count(buttonId))
{
buttonId = axisDialect_[buttonId];
}
if (buttonId == PadButtonUp)
{
HandleButton(device_, state_, delta, PadButtonUp, value < 0.0f);
HandleButton(device_, state_, delta, PadButtonDown, value > 0.0f);
}
else if (buttonId == PadButtonLeft)
{
HandleButton(device_, state_, delta, PadButtonLeft, value < 0.0f);
HandleButton(device_, state_, delta, PadButtonRight, value > 0.0f);
}
else
{
HandleAxis(device_, state_, delta, buttonId, value);
}
}
else if (event.type == JS_EVENT_BUTTON)
{
GAINPUT_ASSERT(event.number < PadButtonCount_);
if (buttonDialect_.count(event.number))
{
DeviceButtonId buttonId = buttonDialect_[event.number];
const bool value(event.value);
HandleButton(device_, state_, delta, buttonId, value);
}
#ifdef GAINPUT_DEBUG
else
{
GAINPUT_LOG("Unknown pad button #%d: %d\n", int(event.number), event.value);
}
#endif
}
}
GAINPUT_ASSERT(c == -1);
if (c == -1
&& (errno == EBADF || errno == ECONNRESET || errno == ENOTCONN || errno == EIO || errno == ENXIO || errno == ENODEV))
{
#ifdef GAINPUT_DEBUG
GAINPUT_LOG("Pad lost.\n");
#endif
deviceState_ = InputDevice::DS_UNAVAILABLE;
fd_ = -1;
}
}
InputDevice::DeviceState GetState() const
{
return deviceState_;
}
bool IsValidButton(DeviceButtonId deviceButton) const
{
if (buttonDialect_.empty())
{
return deviceButton < PadButtonMax_;
}
for (HashMap<unsigned, DeviceButtonId>::const_iterator it = buttonDialect_.begin();
it != buttonDialect_.end();
++it)
{
if (it->second == deviceButton)
{
return true;
}
}
for (HashMap<unsigned, DeviceButtonId>::const_iterator it = axisDialect_.begin();
it != axisDialect_.end();
++it)
{
if (it->second == deviceButton)
{
return true;
}
}
return false;
}
bool Vibrate(float /*leftMotor*/, float /*rightMotor*/)
{
return false; // TODO
}
private:
InputManager& manager_;
InputDevice& device_;
InputState& state_;
unsigned index_;
InputDevice::DeviceState deviceState_;
int fd_;
HashMap<unsigned, DeviceButtonId> buttonDialect_;
HashMap<unsigned, DeviceButtonId> axisDialect_;
void CheckForDevice()
{
if (fd_ != -1)
{
return;
}
deviceState_ = InputDevice::DS_UNAVAILABLE;
fd_ = open(PadDeviceIds[index_], O_RDONLY | O_NONBLOCK);
if (fd_ < 0)
{
return;
}
GAINPUT_ASSERT(fd_ >= 0);
#ifdef GAINPUT_DEBUG
char axesCount;
ioctl(fd_, JSIOCGAXES, &axesCount);
GAINPUT_LOG("Axes count: %d\n", int(axesCount));
int driverVersion;
ioctl(fd_, JSIOCGVERSION, &driverVersion);
GAINPUT_LOG("Driver version: %d\n", driverVersion);
#endif
char name[128] = "";
if (ioctl(fd_, JSIOCGNAME(sizeof(name)), name) < 0)
{
strncpy(name, "Unknown", sizeof(name));
}
#ifdef GAINPUT_DEBUG
GAINPUT_LOG("Name: %s\n", name);
#endif
for (unsigned i = PadButtonLeftStickX; i < PadButtonAxisCount_; ++i)
{
axisDialect_[i] = i;
}
if (strcmp(name, "Sony PLAYSTATION(R)3 Controller") == 0)
{
#ifdef GAINPUT_DEBUG
GAINPUT_LOG(" --> known controller\n");
#endif
buttonDialect_[0] = PadButtonSelect;
buttonDialect_[1] = PadButtonL3;
buttonDialect_[2] = PadButtonR3;
buttonDialect_[3] = PadButtonStart;
buttonDialect_[4] = PadButtonUp;
buttonDialect_[5] = PadButtonRight;
buttonDialect_[6] = PadButtonDown;
buttonDialect_[7] = PadButtonLeft;
buttonDialect_[8] = PadButtonL2;
buttonDialect_[9] = PadButtonR2;
buttonDialect_[10] = PadButtonL1;
buttonDialect_[11] = PadButtonR1;
buttonDialect_[12] = PadButtonY;
buttonDialect_[13] = PadButtonB;
buttonDialect_[14] = PadButtonA;
buttonDialect_[15] = PadButtonX;
buttonDialect_[16] = PadButtonHome;
}
else if (strcmp(name, "Microsoft X-Box 360 pad") == 0)
{
#ifdef GAINPUT_DEBUG
GAINPUT_LOG(" --> known controller\n");
#endif
buttonDialect_[6] = PadButtonSelect;
buttonDialect_[9] = PadButtonL3;
buttonDialect_[10] = PadButtonR3;
buttonDialect_[7] = PadButtonStart;
buttonDialect_[4] = PadButtonL1;
buttonDialect_[5] = PadButtonR1;
buttonDialect_[3] = PadButtonY;
buttonDialect_[1] = PadButtonB;
buttonDialect_[0] = PadButtonA;
buttonDialect_[2] = PadButtonX;
buttonDialect_[8] = PadButtonHome;
axisDialect_[3] = PadButtonRightStickX;
axisDialect_[4] = PadButtonRightStickY;
axisDialect_[2] = PadButtonAxis4;
axisDialect_[5] = PadButtonAxis5;
axisDialect_[7] = PadButtonUp;
axisDialect_[6] = PadButtonLeft;
// Dummy entries for IsValidButton
axisDialect_[-1] = PadButtonDown;
axisDialect_[-2] = PadButtonRight;
}
deviceState_ = InputDevice::DS_OK;
}
};
}
#endif

View File

@@ -0,0 +1,413 @@
#include <gainput/gainput.h>
#ifdef GAINPUT_PLATFORM_MAC
#include "GainputInputDevicePadImpl.h"
#include <gainput/GainputInputDeltaState.h>
#include <gainput/GainputHelpers.h>
#include <gainput/GainputLog.h>
#include "GainputInputDevicePadMac.h"
#import <CoreFoundation/CoreFoundation.h>
#import <IOKit/hid/IOHIDManager.h>
#import <IOKit/hid/IOHIDUsageTables.h>
namespace gainput
{
extern bool MacIsApplicationKey();
namespace {
static const unsigned kMaxPads = 16;
static bool usedPadIndices_[kMaxPads] = { false };
static inline float FixUpAnalog(float analog, const float minAxis, const float maxAxis, bool symmetric)
{
analog = analog < minAxis ? minAxis : analog > maxAxis ? maxAxis : analog; // clamp
analog -= minAxis;
analog /= (Abs(minAxis) + Abs(maxAxis))*(symmetric ? 0.5f : 1.0f);
if (symmetric)
{
analog -= 1.0f;
}
return analog;
}
static void OnDeviceInput(void* inContext, IOReturn inResult, void* inSender, IOHIDValueRef value)
{
if (!MacIsApplicationKey())
{
return;
}
IOHIDElementRef elem = IOHIDValueGetElement(value);
InputDevicePadImplMac* device = reinterpret_cast<InputDevicePadImplMac*>(inContext);
GAINPUT_ASSERT(device);
uint32_t usagePage = IOHIDElementGetUsagePage(elem);
uint32_t usage = IOHIDElementGetUsage(elem);
if (IOHIDElementGetReportCount(elem) > 1
|| (usagePage == kHIDPage_GenericDesktop && usage == kHIDUsage_GD_Pointer) )
{
return;
}
if (usagePage >= kHIDPage_VendorDefinedStart)
{
return;
}
InputManager& manager = device->manager_;
CFIndex state = (int)IOHIDValueGetIntegerValue(value);
float analog = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical);
if (usagePage == kHIDPage_Button && device->buttonDialect_.count(usage))
{
const DeviceButtonId buttonId = device->buttonDialect_[usage];
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, buttonId, state != 0);
}
else if (usagePage == kHIDPage_GenericDesktop)
{
if (usage == kHIDUsage_GD_Hatswitch)
{
int dpadX = 0;
int dpadY = 0;
switch(state)
{
case 0: dpadX = 0; dpadY = 1; break;
case 1: dpadX = 1; dpadY = 1; break;
case 2: dpadX = 1; dpadY = 0; break;
case 3: dpadX = 1; dpadY = -1; break;
case 4: dpadX = 0; dpadY = -1; break;
case 5: dpadX = -1; dpadY = -1; break;
case 6: dpadX = -1; dpadY = 0; break;
case 7: dpadX = -1; dpadY = 1; break;
default: dpadX = 0; dpadY = 0; break;
}
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, PadButtonLeft, dpadX < 0);
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, PadButtonRight, dpadX > 0);
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, PadButtonUp, dpadY > 0);
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, PadButtonDown, dpadY < 0);
}
else if (device->axisDialect_.count(usage))
{
const DeviceButtonId buttonId = device->axisDialect_[usage];
if (buttonId == PadButtonAxis4 || buttonId == PadButtonAxis5)
{
analog = FixUpAnalog(analog, device->minTriggerAxis_, device->maxTriggerAxis_, false);
}
else if (buttonId == PadButtonLeftStickY || buttonId == PadButtonRightStickY)
{
analog = -FixUpAnalog(analog, device->minAxis_, device->maxAxis_, true);
}
else
{
analog = FixUpAnalog(analog, device->minAxis_, device->maxAxis_, true);
}
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, buttonId, analog);
}
else if (device->buttonDialect_.count(usage))
{
const DeviceButtonId buttonId = device->buttonDialect_[usage];
manager.EnqueueConcurrentChange(device->device_, device->nextState_, device->delta_, buttonId, state != 0);
}
#ifdef GAINPUT_DEBUG
else
{
GAINPUT_LOG("Unmapped button (generic): %d\n", usage);
}
#endif
}
#ifdef GAINPUT_DEBUG
else
{
GAINPUT_LOG("Unmapped button: %d\n", usage);
}
#endif
}
static void OnDeviceConnected(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
InputDevicePadImplMac* device = reinterpret_cast<InputDevicePadImplMac*>(inContext);
GAINPUT_ASSERT(device);
if (device->deviceState_ != InputDevice::DS_UNAVAILABLE)
{
return;
}
for (unsigned i = 0; i < device->index_ && i < kMaxPads; ++i)
{
if (!usedPadIndices_[i])
{
return;
}
}
if (device->index_ < kMaxPads)
{
usedPadIndices_[device->index_] = true;
}
device->deviceState_ = InputDevice::DS_OK;
long vendorId = 0;
long productId = 0;
if (CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey) ))
{
if (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef))
{
CFNumberGetValue((CFNumberRef)tCFTypeRef, kCFNumberSInt32Type, &vendorId);
}
}
if (CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey) ))
{
if (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef))
{
CFNumberGetValue((CFNumberRef)tCFTypeRef, kCFNumberSInt32Type, &productId);
}
}
if (vendorId == 0x054c && (productId == 0x5c4 || productId == 0x9cc)) // Sony DualShock 4
{
device->minAxis_ = 0;
device->maxAxis_ = 256;
device->minTriggerAxis_ = device->minAxis_;
device->maxTriggerAxis_ = device->maxAxis_;
device->axisDialect_[kHIDUsage_GD_X] = PadButtonLeftStickX;
device->axisDialect_[kHIDUsage_GD_Y] = PadButtonLeftStickY;
device->axisDialect_[kHIDUsage_GD_Z] = PadButtonRightStickX;
device->axisDialect_[kHIDUsage_GD_Rz] = PadButtonRightStickY;
device->axisDialect_[kHIDUsage_GD_Rx] = PadButtonAxis4;
device->axisDialect_[kHIDUsage_GD_Ry] = PadButtonAxis5;
device->buttonDialect_[0x09] = PadButtonSelect;
device->buttonDialect_[0x0b] = PadButtonL3;
device->buttonDialect_[0x0c] = PadButtonR3;
device->buttonDialect_[0x0A] = PadButtonStart;
device->buttonDialect_[0xfffffff0] = PadButtonUp;
device->buttonDialect_[0xfffffff1] = PadButtonRight;
device->buttonDialect_[0xfffffff2] = PadButtonDown;
device->buttonDialect_[0xfffffff3] = PadButtonLeft;
device->buttonDialect_[0x05] = PadButtonL1;
device->buttonDialect_[0x07] = PadButtonL2;
device->buttonDialect_[0x06] = PadButtonR1;
device->buttonDialect_[0x08] = PadButtonR2;
device->buttonDialect_[0x04] = PadButtonY;
device->buttonDialect_[0x03] = PadButtonB;
device->buttonDialect_[0x02] = PadButtonA;
device->buttonDialect_[0x01] = PadButtonX;
device->buttonDialect_[0x0d] = PadButtonHome;
device->buttonDialect_[0x0e] = PadButton17; // Touch pad
}
else if (vendorId == 0x054c && productId == 0x268) // Sony DualShock 3
{
device->minAxis_ = 0;
device->maxAxis_ = 256;
device->minTriggerAxis_ = device->minAxis_;
device->maxTriggerAxis_ = device->maxAxis_;
device->axisDialect_[kHIDUsage_GD_X] = PadButtonLeftStickX;
device->axisDialect_[kHIDUsage_GD_Y] = PadButtonLeftStickY;
device->axisDialect_[kHIDUsage_GD_Z] = PadButtonRightStickX;
device->axisDialect_[kHIDUsage_GD_Rz] = PadButtonRightStickY;
device->axisDialect_[kHIDUsage_GD_Rx] = PadButtonAxis4;
device->axisDialect_[kHIDUsage_GD_Ry] = PadButtonAxis5;
//device->buttonDialect_[0] = PadButtonSelect;
device->buttonDialect_[2] = PadButtonL3;
device->buttonDialect_[3] = PadButtonR3;
device->buttonDialect_[4] = PadButtonStart;
device->buttonDialect_[5] = PadButtonUp;
device->buttonDialect_[6] = PadButtonRight;
device->buttonDialect_[7] = PadButtonDown;
device->buttonDialect_[8] = PadButtonLeft;
device->buttonDialect_[11] = PadButtonL1;
device->buttonDialect_[9] = PadButtonL2;
device->buttonDialect_[12] = PadButtonR1;
device->buttonDialect_[10] = PadButtonR2;
device->buttonDialect_[13] = PadButtonY;
device->buttonDialect_[14] = PadButtonB;
device->buttonDialect_[15] = PadButtonA;
device->buttonDialect_[16] = PadButtonX;
device->buttonDialect_[17] = PadButtonHome;
}
else if (vendorId == 0x045e && (productId == 0x028E || productId == 0x028F || productId == 0x02D1)) // Microsoft 360 Controller wired/wireless, Xbox One Controller
{
device->minAxis_ = -(1<<15);
device->maxAxis_ = 1<<15;
device->minTriggerAxis_ = 0;
device->maxTriggerAxis_ = 255;
device->axisDialect_[kHIDUsage_GD_X] = PadButtonLeftStickX;
device->axisDialect_[kHIDUsage_GD_Y] = PadButtonLeftStickY;
device->axisDialect_[kHIDUsage_GD_Rx] = PadButtonRightStickX;
device->axisDialect_[kHIDUsage_GD_Ry] = PadButtonRightStickY;
device->axisDialect_[kHIDUsage_GD_Z] = PadButtonAxis4;
device->axisDialect_[kHIDUsage_GD_Rz] = PadButtonAxis5;
device->buttonDialect_[0x0a] = PadButtonSelect;
device->buttonDialect_[0x07] = PadButtonL3;
device->buttonDialect_[0x08] = PadButtonR3;
device->buttonDialect_[0x09] = PadButtonStart;
device->buttonDialect_[0x0c] = PadButtonUp;
device->buttonDialect_[0x0f] = PadButtonRight;
device->buttonDialect_[0x0d] = PadButtonDown;
device->buttonDialect_[0x0e] = PadButtonLeft;
device->buttonDialect_[0x05] = PadButtonL1;
device->buttonDialect_[0x06] = PadButtonR1;
device->buttonDialect_[0x04] = PadButtonY;
device->buttonDialect_[0x02] = PadButtonB;
device->buttonDialect_[0x01] = PadButtonA;
device->buttonDialect_[0x03] = PadButtonX;
device->buttonDialect_[0x0b] = PadButtonHome;
}
}
static void OnDeviceRemoved(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
InputDevicePadImplMac* device = reinterpret_cast<InputDevicePadImplMac*>(inContext);
GAINPUT_ASSERT(device);
device->deviceState_ = InputDevice::DS_UNAVAILABLE;
if (device->index_ < kMaxPads)
{
usedPadIndices_[device->index_] = true;
}
}
}
InputDevicePadImplMac::InputDevicePadImplMac(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState) :
buttonDialect_(manager.GetAllocator()),
axisDialect_(manager.GetAllocator()),
minAxis_(-FLT_MAX),
maxAxis_(FLT_MAX),
minTriggerAxis_(-FLT_MAX),
maxTriggerAxis_(FLT_MAX),
manager_(manager),
device_(device),
index_(index),
state_(state),
previousState_(previousState),
nextState_(manager.GetAllocator(), PadButtonCount_ + PadButtonAxisCount_),
deviceState_(InputDevice::DS_UNAVAILABLE),
ioManager_(0)
{
IOHIDManagerRef ioManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone);
if (CFGetTypeID(ioManager) != IOHIDManagerGetTypeID())
{
return;
}
ioManager_ = ioManager;
static const unsigned kKeyCount = 2;
CFStringRef keys[kKeyCount] = {
CFSTR(kIOHIDDeviceUsagePageKey),
CFSTR(kIOHIDDeviceUsageKey),
};
int usagePage = kHIDPage_GenericDesktop;
int usage = kHIDUsage_GD_GamePad;
CFNumberRef values[kKeyCount] = {
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usagePage),
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usage),
};
CFMutableArrayRef matchingArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault,
(const void **) keys, (const void **) values, kKeyCount,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(matchingArray, matchingDict);
CFRelease(matchingDict);
CFRelease(values[1]);
usage = kHIDUsage_GD_MultiAxisController;
values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usage);
matchingDict = CFDictionaryCreate(kCFAllocatorDefault,
(const void **) keys, (const void **) values, kKeyCount,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(matchingArray, matchingDict);
CFRelease(matchingDict);
CFRelease(values[1]);
usage = kHIDUsage_GD_Joystick;
values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usage);
matchingDict = CFDictionaryCreate(kCFAllocatorDefault,
(const void **) keys, (const void **) values, kKeyCount,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(matchingArray, matchingDict);
CFRelease(matchingDict);
for (unsigned i = 0; i < kKeyCount; ++i)
{
CFRelease(keys[i]);
CFRelease(values[i]);
}
IOHIDManagerSetDeviceMatchingMultiple(ioManager, matchingArray);
CFRelease(matchingArray);
IOHIDManagerRegisterDeviceMatchingCallback(ioManager, OnDeviceConnected, this);
IOHIDManagerRegisterDeviceRemovalCallback(ioManager, OnDeviceRemoved, this);
IOHIDManagerRegisterInputValueCallback(ioManager, OnDeviceInput, this);
IOHIDManagerOpen(ioManager, kIOHIDOptionsTypeNone);
IOHIDManagerScheduleWithRunLoop(ioManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
InputDevicePadImplMac::~InputDevicePadImplMac()
{
IOHIDManagerRef ioManager = reinterpret_cast<IOHIDManagerRef>(ioManager_);
IOHIDManagerUnscheduleFromRunLoop(ioManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDManagerClose(ioManager, 0);
CFRelease(ioManager);
}
void InputDevicePadImplMac::Update(InputDeltaState* delta)
{
delta_ = delta;
state_ = nextState_;
}
bool InputDevicePadImplMac::IsValidButton(DeviceButtonId deviceButton) const
{
if (buttonDialect_.empty())
{
return deviceButton < PadButtonMax_;
}
for (HashMap<unsigned, DeviceButtonId>::const_iterator it = buttonDialect_.begin();
it != buttonDialect_.end();
++it)
{
if (it->second == deviceButton)
{
return true;
}
}
for (HashMap<unsigned, DeviceButtonId>::const_iterator it = axisDialect_.begin();
it != axisDialect_.end();
++it)
{
if (it->second == deviceButton)
{
return true;
}
}
return false;
}
}
#endif

View File

@@ -0,0 +1,57 @@
#ifndef GAINPUTINPUTDEVICEPADMAC_H_
#define GAINPUTINPUTDEVICEPADMAC_H_
namespace gainput
{
class InputDevicePadImplMac : public InputDevicePadImpl
{
public:
InputDevicePadImplMac(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState);
~InputDevicePadImplMac();
InputDevice::DeviceVariant GetVariant() const
{
return InputDevice::DV_STANDARD;
}
void Update(InputDeltaState* delta);
InputDevice::DeviceState GetState() const
{
return deviceState_;
}
bool IsValidButton(DeviceButtonId deviceButton) const;
bool Vibrate(float leftMotor, float rightMotor)
{
return false;
}
HashMap<unsigned, DeviceButtonId> buttonDialect_;
HashMap<unsigned, DeviceButtonId> axisDialect_;
float minAxis_;
float maxAxis_;
float minTriggerAxis_;
float maxTriggerAxis_;
InputManager& manager_;
InputDevice& device_;
unsigned index_;
InputState& state_;
InputState& previousState_;
InputState nextState_;
InputDeltaState* delta_;
InputDevice::DeviceState deviceState_;
void* ioManager_;
private:
};
}
#endif

View File

@@ -0,0 +1,43 @@
#ifndef GAINPUTINPUTDEVICEPADNULL_H_
#define GAINPUTINPUTDEVICEPADNULL_H_
namespace gainput
{
class InputDevicePadImplNull : public InputDevicePadImpl
{
public:
InputDevicePadImplNull(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState)
{
}
InputDevice::DeviceVariant GetVariant() const
{
return InputDevice::DV_NULL;
}
void Update(InputDeltaState* /*delta*/)
{
}
InputDevice::DeviceState GetState() const
{
return InputDevice::DS_OK;
}
bool IsValidButton(DeviceButtonId /*deviceButton*/) const
{
return false;
}
bool Vibrate(float /*leftMotor*/, float /*rightMotor*/)
{
return false;
}
};
}
#endif

View File

@@ -0,0 +1,165 @@
#ifndef GAINPUTINPUTDEVICEPADWIN_H_
#define GAINPUTINPUTDEVICEPADWIN_H_
// Cf. http://msdn.microsoft.com/en-us/library/windows/desktop/ee417005%28v=vs.85%29.aspx
#include "../GainputWindows.h"
#include <XInput.h>
namespace gainput
{
const float MaxTriggerValue = 255.0f;
const float MaxAxisValue = 32767.0f;
const float MaxNegativeAxisValue = 32768.0f;
const float MaxMotorSpeed = 65535.0f;
class InputDevicePadImplWin : public InputDevicePadImpl
{
public:
InputDevicePadImplWin(InputManager& manager, InputDevice& device, unsigned index, InputState& state, InputState& previousState) :
manager_(manager),
device_(device),
state_(state),
previousState_(previousState),
deviceState_(InputDevice::DS_UNAVAILABLE),
lastPacketNumber_(-1),
hasBattery_(false)
{
padIndex_ = index;
GAINPUT_ASSERT(padIndex_ < MaxPadCount);
#if 0
XINPUT_BATTERY_INFORMATION xbattery;
DWORD result = XInputGetBatteryInformation(padIndex, BATTERY_DEVTYPE_GAMEPAD, &xbattery);
if (result == ERROR_SUCCESS)
{
hasBattery = (xbattery.BatteryType == BATTERY_TYPE_ALKALINE
|| xbattery.BatteryType == BATTERY_TYPE_NIMH);
}
#endif
}
InputDevice::DeviceVariant GetVariant() const
{
return InputDevice::DV_STANDARD;
}
void Update(InputDeltaState* delta)
{
XINPUT_STATE xstate;
DWORD result = XInputGetState(padIndex_, &xstate);
if (result != ERROR_SUCCESS)
{
deviceState_ = InputDevice::DS_UNAVAILABLE;
return;
}
deviceState_ = InputDevice::DS_OK;
#if 0
if (hasBattery)
{
XINPUT_BATTERY_INFORMATION xbattery;
result = XInputGetBatteryInformation(padIndex, BATTERY_DEVTYPE_GAMEPAD, &xbattery);
if (result == ERROR_SUCCESS)
{
if (xbattery.BatteryType == BATTERY_TYPE_ALKALINE
|| xbattery.BatteryType == BATTERY_TYPE_NIMH)
{
if (xbattery.BatteryLevel == BATTERY_LEVEL_EMPTY
|| xbattery.BatteryLevel == BATTERY_LEVEL_LOW)
{
deviceState = InputDevice::DS_LOW_BATTERY;
}
}
}
}
#endif
if (xstate.dwPacketNumber == lastPacketNumber_)
{
// Not changed
return;
}
lastPacketNumber_ = xstate.dwPacketNumber;
HandleButton(device_, state_, delta, PadButtonUp, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0);
HandleButton(device_, state_, delta, PadButtonDown, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0);
HandleButton(device_, state_, delta, PadButtonLeft, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0);
HandleButton(device_, state_, delta, PadButtonRight, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0);
HandleButton(device_, state_, delta, PadButtonA, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0);
HandleButton(device_, state_, delta, PadButtonB, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0);
HandleButton(device_, state_, delta, PadButtonX, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0);
HandleButton(device_, state_, delta, PadButtonY, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0);
HandleButton(device_, state_, delta, PadButtonStart, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_START) != 0);
HandleButton(device_, state_, delta, PadButtonSelect, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) != 0);
HandleButton(device_, state_, delta, PadButtonL3, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0);
HandleButton(device_, state_, delta, PadButtonR3, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0);
HandleButton(device_, state_, delta, PadButtonL1, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0);
HandleButton(device_, state_, delta, PadButtonR1, (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0);
HandleButton(device_, state_, delta, PadButtonL2, xstate.Gamepad.bLeftTrigger != 0);
HandleButton(device_, state_, delta, PadButtonR2, xstate.Gamepad.bRightTrigger != 0);
HandleAxis(device_, state_, delta, PadButtonAxis4, float(xstate.Gamepad.bLeftTrigger)/MaxTriggerValue);
HandleAxis(device_, state_, delta, PadButtonAxis5, float(xstate.Gamepad.bRightTrigger)/MaxTriggerValue);
HandleAxis(device_, state_, delta, PadButtonLeftStickX, GetAxisValue(xstate.Gamepad.sThumbLX));
HandleAxis(device_, state_, delta, PadButtonLeftStickY, GetAxisValue(xstate.Gamepad.sThumbLY));
HandleAxis(device_, state_, delta, PadButtonRightStickX, GetAxisValue(xstate.Gamepad.sThumbRX));
HandleAxis(device_, state_, delta, PadButtonRightStickY, GetAxisValue(xstate.Gamepad.sThumbRY));
}
InputDevice::DeviceState GetState() const
{
return deviceState_;
}
bool IsValidButton(DeviceButtonId deviceButton) const
{
return deviceButton < PadButtonAxis4 || (deviceButton >= PadButtonStart && deviceButton <= PadButtonR3);
}
bool Vibrate(float leftMotor, float rightMotor)
{
GAINPUT_ASSERT(leftMotor >= 0.0f && leftMotor <= 1.0f);
GAINPUT_ASSERT(rightMotor >= 0.0f && rightMotor <= 1.0f);
XINPUT_VIBRATION xvibration;
xvibration.wLeftMotorSpeed = static_cast<WORD>(leftMotor*MaxMotorSpeed);
xvibration.wRightMotorSpeed = static_cast<WORD>(rightMotor*MaxMotorSpeed);
DWORD result = XInputSetState(padIndex_, &xvibration);
return result == ERROR_SUCCESS;
}
private:
InputManager& manager_;
InputDevice& device_;
InputState& state_;
InputState& previousState_;
InputDevice::DeviceState deviceState_;
unsigned padIndex_;
DWORD lastPacketNumber_;
bool hasBattery_;
static float GetAxisValue(SHORT value)
{
if (value < 0)
{
return float(value) / MaxNegativeAxisValue;
}
else
{
return float(value) / MaxAxisValue;
}
}
};
}
#endif