mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-27 21:35:19 +00:00
[Qt/Input] Improve pad_settings_dialog a bit (#3611)
* Input: further work on remapping Xinput and begin work on remapping DS4 * Input: Improve pad_settings_dialog a bit and begin Remapping for XInput * Input: begin evdev remapping and change all handlers to use cfg::string * Input: finish work on remapping evdev and some more crap * Input: finish work on remapping Xinput and DS4 * Input: add DS4 Colors to DS4 config * Input: Improve DS4 deadzone scaling Jarves made some mistakes, so I'll fix them in the follow up commit * Input: fix Jarves fixes on DS4 deadzone and remove unnecessary usage of toUtf8 * Input: add primitive batterychecks to XInput and DS4 * Input: add mmjoystick remapping * Input: Fix evdev and some Vibration issues * Input: adjust capabilities to fix stick input for games like LoS 2 also fix threshold slider minimum also add ps button to all the handlers * Input: Further evdev work based on danilaml code review and own debugging: Fixed path issue, <= 0 issue, some captures, const, axis with same codes. Adds a map to each device that differentiates negative and positive axis mappings. adjusted rest of the file to tabs (ListDevices and beginning of threadProc) * Input: use 20ms vibration update time for xbox one elite controllers. * Input: Fix return type of Clamp() * Input: Evdev Fix * Input: Evdev Optional GetNextButtonPress presumably better than the other * Input: review changes * Input: evdev: fix wrong index in axis handling move bindpadtodevice down to keep consistency between handlers and not get crazy * Input: evdev: fix expensive add_device in GetNextButtonPress * cleanup * Input: mmjoy: fix type * Input: evdev: final fixes * Input: evdev: exclude unnecessary buttons while mapping Xbox 360 or DS4 * Input: add deadzone preview by passing necessary values in callback use 0.5 of max value for threshold in pad dialog * Input: get rid of all-uppercase variables
This commit is contained in:
parent
695b4c1f06
commit
662fe8cc95
@ -118,7 +118,7 @@ namespace cfg
|
||||
bool m_value;
|
||||
|
||||
public:
|
||||
const bool def;
|
||||
bool def;
|
||||
|
||||
_bool(node* owner, const std::string& name, bool def = false)
|
||||
: _base(type::_bool, owner, name)
|
||||
@ -150,6 +150,11 @@ namespace cfg
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set(const bool& value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
@ -217,7 +222,7 @@ namespace cfg
|
||||
int_type m_value;
|
||||
|
||||
public:
|
||||
const int_type def;
|
||||
int_type def;
|
||||
|
||||
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: _base(type::_int, owner, name)
|
||||
@ -253,6 +258,11 @@ namespace cfg
|
||||
return false;
|
||||
}
|
||||
|
||||
void set(const s64& value)
|
||||
{
|
||||
m_value = static_cast<int_type>(value);
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return make_int_range(Min, Max);
|
||||
@ -271,7 +281,7 @@ namespace cfg
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
const std::string def;
|
||||
std::string def;
|
||||
|
||||
string(node* owner, const std::string& name, const std::string& def = {})
|
||||
: _base(type::string, owner, name)
|
||||
|
@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "stdafx.h"
|
||||
#include "../../Utilities/Config.h"
|
||||
#include "../../Utilities/types.h"
|
||||
|
||||
// TODO: HLE info (constants, structs, etc.) should not be available here
|
||||
@ -159,6 +162,13 @@ struct Pad
|
||||
u32 m_device_capability;
|
||||
u32 m_device_type;
|
||||
|
||||
// Cable State: 0 - 1 plugged in ?
|
||||
u8 m_cable_state;
|
||||
|
||||
// DS4: 0 - 9 while unplugged, 0 - 10 while plugged in, 11 charge complete
|
||||
// XInput: 0 = Empty, 1 = Low, 2 = Medium, 3 = Full
|
||||
u8 m_battery_level;
|
||||
|
||||
std::vector<Button> m_buttons;
|
||||
std::vector<AnalogStick> m_sticks;
|
||||
std::vector<AnalogSensor> m_sensors;
|
||||
@ -238,23 +248,280 @@ struct Pad
|
||||
}
|
||||
};
|
||||
|
||||
struct pad_config : cfg::node
|
||||
{
|
||||
std::string cfg_type = "";
|
||||
std::string cfg_name = "";
|
||||
|
||||
cfg::string ls_left { this, "Left Stick Left", "" };
|
||||
cfg::string ls_down { this, "Left Stick Down", "" };
|
||||
cfg::string ls_right{ this, "Left Stick Right", "" };
|
||||
cfg::string ls_up { this, "Left Stick Up", "" };
|
||||
cfg::string rs_left { this, "Right Stick Left", "" };
|
||||
cfg::string rs_down { this, "Right Stick Down", "" };
|
||||
cfg::string rs_right{ this, "Right Stick Right", "" };
|
||||
cfg::string rs_up { this, "Right Stick Up", "" };
|
||||
cfg::string start { this, "Start", "" };
|
||||
cfg::string select { this, "Select", "" };
|
||||
cfg::string ps { this, "PS Button", "" };
|
||||
cfg::string square { this, "Square", "" };
|
||||
cfg::string cross { this, "Cross", "" };
|
||||
cfg::string circle { this, "Circle", "" };
|
||||
cfg::string triangle{ this, "Triangle", "" };
|
||||
cfg::string left { this, "Left", "" };
|
||||
cfg::string down { this, "Down", "" };
|
||||
cfg::string right { this, "Right", "" };
|
||||
cfg::string up { this, "Up", "" };
|
||||
cfg::string r1 { this, "R1", "" };
|
||||
cfg::string r2 { this, "R2", "" };
|
||||
cfg::string r3 { this, "R3", "" };
|
||||
cfg::string l1 { this, "L1", "" };
|
||||
cfg::string l2 { this, "L2", "" };
|
||||
cfg::string l3 { this, "L3", "" };
|
||||
|
||||
cfg::_int<0, 1000000> lstickdeadzone{ this, "Left Stick Deadzone", 0 };
|
||||
cfg::_int<0, 1000000> rstickdeadzone{ this, "Right Stick Deadzone", 0 };
|
||||
cfg::_int<0, 1000000> ltriggerthreshold{ this, "Left Trigger Threshold", 0 };
|
||||
cfg::_int<0, 1000000> rtriggerthreshold{ this, "Right Trigger Threshold", 0 };
|
||||
cfg::_int<0, 1000000> padsquircling{ this, "Pad Squircling Factor", 0 };
|
||||
|
||||
cfg::_int<0, 255> colorR{ this, "Color Value R", 0 };
|
||||
cfg::_int<0, 255> colorG{ this, "Color Value G", 0 };
|
||||
cfg::_int<0, 255> colorB{ this, "Color Value B", 0 };
|
||||
|
||||
cfg::_bool enable_vibration_motor_large{ this, "Enable Large Vibration Motor", true };
|
||||
cfg::_bool enable_vibration_motor_small{ this, "Enable Small Vibration Motor", true };
|
||||
cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false };
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
|
||||
bool exist()
|
||||
{
|
||||
return fs::is_file(cfg_name);
|
||||
}
|
||||
};
|
||||
|
||||
class PadHandlerBase
|
||||
{
|
||||
protected:
|
||||
static const u32 MAX_GAMEPADS = 7;
|
||||
|
||||
int m_trigger_threshold = 0;
|
||||
int m_thumb_threshold = 0;
|
||||
|
||||
bool b_has_deadzones = false;
|
||||
bool b_has_rumble = false;
|
||||
bool b_has_config = false;
|
||||
pad_config m_pad_config;
|
||||
|
||||
template <typename T>
|
||||
T lerp(T v0, T v1, T t) {
|
||||
return std::fma(t, v1, std::fma(-t, v0, v0));
|
||||
}
|
||||
|
||||
// Search an unordered map for a string value and return found keycode
|
||||
int FindKeyCode(std::unordered_map<u32, std::string> map, const std::string& name)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
if (it->second == name) return it->first;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
long FindKeyCode(std::unordered_map<u64, std::string> map, const std::string& name)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
if (it->second == name) return it->first;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Get normalized trigger value based on the range defined by a threshold
|
||||
u16 NormalizeTriggerInput(u16 value, int threshold)
|
||||
{
|
||||
if (value <= threshold || threshold >= trigger_max)
|
||||
{
|
||||
return static_cast<u16>(0);
|
||||
}
|
||||
else if (threshold <= trigger_min)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (u16)(float(trigger_max) * float(value - threshold) / float(trigger_max - threshold));
|
||||
}
|
||||
};
|
||||
|
||||
// Get new scaled value between 0 and 255 based on its minimum and maximum
|
||||
float ScaleStickInput(s32 raw_value, int minimum, int maximum)
|
||||
{
|
||||
// value based on max range converted to [0, 1]
|
||||
float val = float(Clamp(raw_value, minimum, maximum) - minimum) / float(abs(maximum) + abs(minimum));
|
||||
return 255.0f * val;
|
||||
};
|
||||
|
||||
// normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions
|
||||
// the input values must lie in 0+
|
||||
u16 NormalizeDirectedInput(u16 raw_value, float threshold, float maximum)
|
||||
{
|
||||
if (threshold >= maximum || maximum <= 0)
|
||||
{
|
||||
return static_cast<u16>(0);
|
||||
}
|
||||
|
||||
float val = float(Clamp(raw_value, 0, maximum)) / maximum; // value based on max range converted to [0, 1]
|
||||
|
||||
if (threshold <= 0)
|
||||
{
|
||||
return static_cast<u16>(255.0f * val);
|
||||
}
|
||||
else
|
||||
{
|
||||
float thresh = threshold / maximum; // threshold converted to [0, 1]
|
||||
return static_cast<u16>(255.0f * std::min(1.0f, (val - thresh) / (1.0f - thresh)));
|
||||
}
|
||||
};
|
||||
|
||||
u16 NormalizeStickInput(s32 raw_value, int threshold, bool ignore_threshold = false)
|
||||
{
|
||||
if (ignore_threshold)
|
||||
{
|
||||
return static_cast<u16>(ScaleStickInput(raw_value, 0, thumb_max));
|
||||
}
|
||||
else
|
||||
{
|
||||
return NormalizeDirectedInput(raw_value, threshold, thumb_max);
|
||||
}
|
||||
}
|
||||
|
||||
// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13%
|
||||
// X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range
|
||||
// return is new x and y values in 0-255 range
|
||||
std::tuple<u16, u16> NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone)
|
||||
{
|
||||
const float dzRange = deadzone / float((std::abs(thumb_max) + std::abs(thumb_min)));
|
||||
|
||||
float X = inX / 255.0f;
|
||||
float Y = inY / 255.0f;
|
||||
|
||||
if (dzRange > 0.f)
|
||||
{
|
||||
const float mag = std::min(sqrtf(X*X + Y*Y), 1.f);
|
||||
|
||||
if (mag <= 0)
|
||||
{
|
||||
return std::tuple<u16, u16>(ConvertAxis(X), ConvertAxis(Y));
|
||||
}
|
||||
|
||||
if (mag > dzRange) {
|
||||
float pos = lerp(0.13f, 1.f, (mag - dzRange) / (1 - dzRange));
|
||||
float scale = pos / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
else {
|
||||
float pos = lerp(0.f, 0.13f, mag / dzRange);
|
||||
float scale = pos / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
}
|
||||
return std::tuple<u16, u16>( ConvertAxis(X), ConvertAxis(Y) );
|
||||
};
|
||||
|
||||
// get clamped value between min and max
|
||||
s32 Clamp(f32 input, s32 min, s32 max)
|
||||
{
|
||||
if (input > max)
|
||||
return max;
|
||||
else if (input < min)
|
||||
return min;
|
||||
else return static_cast<s32>(input);
|
||||
};
|
||||
|
||||
// get clamped value between 0 and 255
|
||||
u16 Clamp0To255(f32 input)
|
||||
{
|
||||
return static_cast<u16>(Clamp(input, 0, 255));
|
||||
};
|
||||
|
||||
// get clamped value between 0 and 1023
|
||||
u16 Clamp0To1023(f32 input)
|
||||
{
|
||||
return static_cast<u16>(Clamp(input, 0, 1023));
|
||||
}
|
||||
|
||||
// input has to be [-1,1]. result will be [0,255]
|
||||
u16 ConvertAxis(float value)
|
||||
{
|
||||
return static_cast<u16>((value + 1.0)*(255.0 / 2.0));
|
||||
};
|
||||
|
||||
// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle
|
||||
// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange
|
||||
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000
|
||||
// This function assumes inX and inY is already in 0-255
|
||||
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY, float squircle_factor)
|
||||
{
|
||||
// convert inX and Y to a (-1, 1) vector;
|
||||
const f32 x = ((f32)inX - 127.5f) / 127.5f;
|
||||
const f32 y = ((f32)inY - 127.5f) / 127.5f;
|
||||
|
||||
// compute angle and len of given point to be used for squircle radius
|
||||
const f32 angle = std::atan2(y, x);
|
||||
const f32 r = std::sqrt(std::pow(x, 2.f) + std::pow(y, 2.f));
|
||||
|
||||
// now find len/point on the given squircle from our current angle and radius in polar coords
|
||||
// https://thatsmaths.com/2016/07/14/squircles/
|
||||
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / (squircle_factor / 1000.f)) * r;
|
||||
|
||||
// we now have len and angle, convert to cartisian
|
||||
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127.5f);
|
||||
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127.5f);
|
||||
return std::tuple<u16, u16>(newX, newY);
|
||||
}
|
||||
|
||||
public:
|
||||
s32 thumb_min = 0;
|
||||
s32 thumb_max = 255;
|
||||
s32 trigger_min = 0;
|
||||
s32 trigger_max = 255;
|
||||
s32 vibration_min = 0;
|
||||
s32 vibration_max = 255;
|
||||
|
||||
virtual bool Init() { return true; };
|
||||
virtual ~PadHandlerBase() = default;
|
||||
|
||||
//Does it have GUI Config?
|
||||
bool has_config() { return b_has_config; };
|
||||
bool has_rumble() { return b_has_rumble; };
|
||||
bool has_deadzones() { return b_has_deadzones; };
|
||||
pad_config* GetConfig() { return &m_pad_config; };
|
||||
//Sets window to config the controller(optional)
|
||||
virtual void ConfigController(std::string device) {};
|
||||
virtual void GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback) {};
|
||||
virtual void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) {};
|
||||
//Return list of devices for that handler
|
||||
virtual std::vector<std::string> ListDevices() = 0;
|
||||
//Callback called during pad_thread::ThreadFunc
|
||||
virtual void ThreadProc() = 0;
|
||||
//Binds a Pad to a device
|
||||
virtual bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) = 0;
|
||||
|
||||
private:
|
||||
virtual void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) {};
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,10 +7,77 @@
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
const u32 MAX_GAMEPADS = 7;
|
||||
|
||||
class ds4_pad_handler final : public PadHandlerBase
|
||||
{
|
||||
// These are all the possible buttons on a standard DS4 controller
|
||||
// The touchpad is restricted to its button for now (or forever?)
|
||||
enum DS4KeyCodes
|
||||
{
|
||||
Triangle = 0,
|
||||
Circle,
|
||||
Cross,
|
||||
Square,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
R1,
|
||||
//R2But,
|
||||
R3,
|
||||
L1,
|
||||
//L2But,
|
||||
L3,
|
||||
Share,
|
||||
Options,
|
||||
PSButton,
|
||||
TouchPad,
|
||||
|
||||
L2,
|
||||
R2,
|
||||
|
||||
LSXNeg,
|
||||
LSXPos,
|
||||
LSYNeg,
|
||||
LSYPos,
|
||||
RSXNeg,
|
||||
RSXPos,
|
||||
RSYNeg,
|
||||
RSYPos,
|
||||
|
||||
KeyCodeCount
|
||||
};
|
||||
|
||||
// Unique names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u32, std::string> button_list =
|
||||
{
|
||||
{ DS4KeyCodes::Triangle, "Triangle" },
|
||||
{ DS4KeyCodes::Circle, "Circle" },
|
||||
{ DS4KeyCodes::Cross, "Cross" },
|
||||
{ DS4KeyCodes::Square, "Square" },
|
||||
{ DS4KeyCodes::Left, "Left" },
|
||||
{ DS4KeyCodes::Right, "Right" },
|
||||
{ DS4KeyCodes::Up, "Up" },
|
||||
{ DS4KeyCodes::Down, "Down" },
|
||||
{ DS4KeyCodes::R1, "R1" },
|
||||
{ DS4KeyCodes::R2, "R2" },
|
||||
{ DS4KeyCodes::R3, "R3" },
|
||||
{ DS4KeyCodes::Options, "Options" },
|
||||
{ DS4KeyCodes::Share, "Share" },
|
||||
{ DS4KeyCodes::PSButton, "PS Button" },
|
||||
{ DS4KeyCodes::TouchPad, "Touch Pad" },
|
||||
{ DS4KeyCodes::L1, "L1" },
|
||||
{ DS4KeyCodes::L2, "L2" },
|
||||
{ DS4KeyCodes::L3, "L3" },
|
||||
{ DS4KeyCodes::LSXNeg, "LS X-" },
|
||||
{ DS4KeyCodes::LSXPos, "LS X+" },
|
||||
{ DS4KeyCodes::LSYPos, "LS Y+" },
|
||||
{ DS4KeyCodes::LSYNeg, "LS Y-" },
|
||||
{ DS4KeyCodes::RSXNeg, "RS X-" },
|
||||
{ DS4KeyCodes::RSXPos, "RS X+" },
|
||||
{ DS4KeyCodes::RSYPos, "RS Y+" },
|
||||
{ DS4KeyCodes::RSYNeg, "RS Y-" }
|
||||
};
|
||||
|
||||
enum DS4CalibIndex
|
||||
{
|
||||
// gyro
|
||||
@ -32,6 +99,13 @@ class ds4_pad_handler final : public PadHandlerBase
|
||||
s32 sensDenom;
|
||||
};
|
||||
|
||||
enum class DS4DataStatus
|
||||
{
|
||||
NewData,
|
||||
NoNewData,
|
||||
ReadError,
|
||||
};
|
||||
|
||||
struct DS4Device
|
||||
{
|
||||
hid_device* hidDevice{ nullptr };
|
||||
@ -43,6 +117,10 @@ class ds4_pad_handler final : public PadHandlerBase
|
||||
u8 largeVibrate{ 0 };
|
||||
u8 smallVibrate{ 0 };
|
||||
std::array<u8, 64> padData;
|
||||
u8 batteryLevel{ 0 };
|
||||
u8 cableState{ 0 };
|
||||
u8 led_delay_on{ 0 };
|
||||
u8 led_delay_off{ 0 };
|
||||
};
|
||||
|
||||
const u16 DS4_VID = 0x054C;
|
||||
@ -63,6 +141,8 @@ public:
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& buttonCallback) override;
|
||||
void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override;
|
||||
|
||||
private:
|
||||
bool is_init;
|
||||
@ -73,11 +153,16 @@ private:
|
||||
std::vector<std::pair<std::shared_ptr<DS4Device>, std::shared_ptr<Pad>>> bindings;
|
||||
|
||||
private:
|
||||
void ProcessData();
|
||||
void UpdateRumble();
|
||||
bool GetCalibrationData(std::shared_ptr<DS4Device> ds4Device);
|
||||
void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) override;
|
||||
void ProcessDataToPad(const std::shared_ptr<DS4Device>& ds4Device, const std::shared_ptr<Pad>& pad);
|
||||
// Copies data into padData if status is NewData, otherwise buffer is untouched
|
||||
DS4DataStatus GetRawData(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
// This function gets us usuable buffer from the rawbuffer of padData
|
||||
// Todo: this currently only handles 'buttons' and not axis or sensors for the time being
|
||||
std::array<u16, DS4KeyCodes::KeyCodeCount> GetButtonValues(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
bool GetCalibrationData(const std::shared_ptr<DS4Device>& ds4Device);
|
||||
void CheckAddDevice(hid_device* hidDevice, hid_device_info* hidDevInfo);
|
||||
void SendVibrateData(const std::shared_ptr<DS4Device> device);
|
||||
int SendVibrateData(const std::shared_ptr<DS4Device>& device);
|
||||
inline s16 ApplyCalibration(s32 rawValue, const DS4CalibData& calibData)
|
||||
{
|
||||
const s32 biased = rawValue - calibData.bias;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,91 +8,259 @@
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
enum { EVDEV_DPAD_HAT_AXIS_X = -1, EVDEV_DPAD_HAT_AXIS_Y = -2 };
|
||||
|
||||
struct evdev_joystick_config final : cfg::node
|
||||
{
|
||||
const std::string cfg_name = fs::get_config_dir() + "/config_linuxjoystick.yml";
|
||||
|
||||
cfg::int32 select{this, "Select", 6};
|
||||
cfg::int32 start{this, "Start", 7};
|
||||
cfg::int32 cross{this, "Cross", 0};
|
||||
cfg::int32 circle{this, "Circle", 1};
|
||||
cfg::int32 square{this, "Square", 2};
|
||||
cfg::int32 triangle{this, "Triangle", 3};
|
||||
|
||||
cfg::int32 r1{this, "R1", 5};
|
||||
cfg::int32 r2{this, "R2", 11};
|
||||
cfg::int32 r3{this, "R3", 10};
|
||||
cfg::int32 l1{this, "L1", 4};
|
||||
cfg::int32 l2{this, "L2", 12};
|
||||
cfg::int32 l3{this, "L3", 9};
|
||||
|
||||
cfg::int32 up{this, "Up", EVDEV_DPAD_HAT_AXIS_Y};
|
||||
cfg::int32 down{this, "Down", EVDEV_DPAD_HAT_AXIS_Y};
|
||||
cfg::int32 left{this, "Left", EVDEV_DPAD_HAT_AXIS_X};
|
||||
cfg::int32 right{this, "Right", EVDEV_DPAD_HAT_AXIS_X};
|
||||
|
||||
cfg::int32 rxstick{this, "Right stick X axis", 0};
|
||||
cfg::int32 rystick{this, "Right stick Y axis", 1};
|
||||
cfg::int32 lxstick{this, "Left stick X axis", 2};
|
||||
cfg::int32 lystick{this, "Left stick Y axis", 3};
|
||||
|
||||
cfg::_bool rxreverse{this, "Reverse right stick X axis", false};
|
||||
cfg::_bool ryreverse{this, "Reverse right stick Y axis", false};
|
||||
cfg::_bool lxreverse{this, "Reverse left stick X axis", false};
|
||||
cfg::_bool lyreverse{this, "Reverse left stick Y axis", false};
|
||||
|
||||
cfg::_bool axistrigger{this, "Z axis triggers", true};
|
||||
cfg::_bool squirclejoysticks{this, "Squircle Joysticks", true};
|
||||
cfg::int32 squirclefactor{this, "Squircle Factor", 5000};
|
||||
cfg::int32 deadzone{this, "Deadzone", 10};
|
||||
cfg::_bool left_analog_to_dpad{this, "Left Analog to Dpad", false};
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class evdev_joystick_handler final : public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
evdev_joystick_handler();
|
||||
~evdev_joystick_handler();
|
||||
// Unique button names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u32, std::string> button_list =
|
||||
{
|
||||
//{ BTN_MISC , "Misc" }, same as BTN_0
|
||||
{ BTN_0 , "0" },
|
||||
{ BTN_1 , "1" },
|
||||
{ BTN_2 , "2" },
|
||||
{ BTN_3 , "3" },
|
||||
{ BTN_4 , "4" },
|
||||
{ BTN_5 , "5" },
|
||||
{ BTN_6 , "6" },
|
||||
{ BTN_7 , "7" },
|
||||
{ BTN_8 , "8" },
|
||||
{ BTN_9 , "9" },
|
||||
//{ BTN_MOUSE , "Mouse" }, same as BTN_LEFT
|
||||
{ BTN_LEFT , "Left" },
|
||||
{ BTN_RIGHT , "Right" },
|
||||
{ BTN_MIDDLE , "Middle" },
|
||||
{ BTN_SIDE , "Side" },
|
||||
{ BTN_EXTRA , "Extra" },
|
||||
{ BTN_FORWARD , "Forward" },
|
||||
{ BTN_BACK , "Back" },
|
||||
{ BTN_TASK , "Task" },
|
||||
{ BTN_JOYSTICK , "Joystick" },
|
||||
{ BTN_TRIGGER , "Trigger" },
|
||||
{ BTN_THUMB , "Thumb" },
|
||||
{ BTN_THUMB2 , "Thumb 2" },
|
||||
{ BTN_TOP , "Top" },
|
||||
{ BTN_TOP2 , "Top 2" },
|
||||
{ BTN_PINKIE , "Pinkie" },
|
||||
{ BTN_BASE , "Base" },
|
||||
{ BTN_BASE2 , "Base 2" },
|
||||
{ BTN_BASE3 , "Base 3" },
|
||||
{ BTN_BASE4 , "Base 4" },
|
||||
{ BTN_BASE5 , "Base 5" },
|
||||
{ BTN_BASE6 , "Base 6" },
|
||||
{ BTN_DEAD , "Dead" },
|
||||
//{ BTN_GAMEPAD , "Gamepad" }, same as BTN_A
|
||||
//{ BTN_SOUTH , "South" }, same as BTN_A
|
||||
{ BTN_A , "A" },
|
||||
//{ BTN_EAST , "South" }, same as BTN_B
|
||||
{ BTN_B , "B" },
|
||||
{ BTN_C , "C" },
|
||||
//{ BTN_NORTH , "North" }, same as BTN_X
|
||||
{ BTN_X , "X" },
|
||||
//{ BTN_WEST , "West" }, same as BTN_Y
|
||||
{ BTN_Y , "Y" },
|
||||
{ BTN_Z , "Z" },
|
||||
{ BTN_TL , "TL" },
|
||||
{ BTN_TR , "TR" },
|
||||
{ BTN_TL2 , "TL 2" },
|
||||
{ BTN_TR2 , "TR 2" },
|
||||
{ BTN_SELECT , "Select" },
|
||||
{ BTN_START , "Start" },
|
||||
{ BTN_MODE , "Mode" },
|
||||
{ BTN_THUMBL , "Thumb L" },
|
||||
{ BTN_THUMBR , "Thumb R" },
|
||||
//{ BTN_DIGI , "Digi" }, same as BTN_TOOL_PEN
|
||||
{ BTN_TOOL_PEN , "Pen" },
|
||||
{ BTN_TOOL_RUBBER , "Rubber" },
|
||||
{ BTN_TOOL_BRUSH , "Brush" },
|
||||
{ BTN_TOOL_PENCIL , "Pencil" },
|
||||
{ BTN_TOOL_AIRBRUSH , "Airbrush" },
|
||||
{ BTN_TOOL_FINGER , "Finger" },
|
||||
{ BTN_TOOL_MOUSE , "Mouse" },
|
||||
{ BTN_TOOL_LENS , "Lense" },
|
||||
{ BTN_TOOL_QUINTTAP , "Quinttap" },
|
||||
{ BTN_TOUCH , "Touch" },
|
||||
{ BTN_STYLUS , "Stylus" },
|
||||
{ BTN_STYLUS2 , "Stylus 2" },
|
||||
{ BTN_TOOL_DOUBLETAP , "Doubletap" },
|
||||
{ BTN_TOOL_TRIPLETAP , "Tripletap" },
|
||||
{ BTN_TOOL_QUADTAP , "Quadtap" },
|
||||
//{ BTN_WHEEL , "Wheel" }, same as BTN_GEAR_DOWN
|
||||
{ BTN_GEAR_DOWN , "Gear Up" },
|
||||
{ BTN_GEAR_UP , "Gear Down" },
|
||||
{ BTN_DPAD_UP , "D-Pad Up" },
|
||||
{ BTN_DPAD_DOWN , "D-Pad Down" },
|
||||
{ BTN_DPAD_LEFT , "D-Pad Left" },
|
||||
{ BTN_DPAD_RIGHT , "D-Pad Right" },
|
||||
{ BTN_TRIGGER_HAPPY , "Happy" },
|
||||
{ BTN_TRIGGER_HAPPY1 , "Happy 1" },
|
||||
{ BTN_TRIGGER_HAPPY2 , "Happy 2" },
|
||||
{ BTN_TRIGGER_HAPPY3 , "Happy 3" },
|
||||
{ BTN_TRIGGER_HAPPY4 , "Happy 4" },
|
||||
{ BTN_TRIGGER_HAPPY5 , "Happy 5" },
|
||||
{ BTN_TRIGGER_HAPPY6 , "Happy 6" },
|
||||
{ BTN_TRIGGER_HAPPY7 , "Happy 7" },
|
||||
{ BTN_TRIGGER_HAPPY8 , "Happy 8" },
|
||||
{ BTN_TRIGGER_HAPPY9 , "Happy 9" },
|
||||
{ BTN_TRIGGER_HAPPY10 , "Happy 10" },
|
||||
{ BTN_TRIGGER_HAPPY11 , "Happy 11" },
|
||||
{ BTN_TRIGGER_HAPPY12 , "Happy 12" },
|
||||
{ BTN_TRIGGER_HAPPY13 , "Happy 13" },
|
||||
{ BTN_TRIGGER_HAPPY14 , "Happy 14" },
|
||||
{ BTN_TRIGGER_HAPPY15 , "Happy 15" },
|
||||
{ BTN_TRIGGER_HAPPY16 , "Happy 16" },
|
||||
{ BTN_TRIGGER_HAPPY17 , "Happy 17" },
|
||||
{ BTN_TRIGGER_HAPPY18 , "Happy 18" },
|
||||
{ BTN_TRIGGER_HAPPY19 , "Happy 19" },
|
||||
{ BTN_TRIGGER_HAPPY20 , "Happy 20" },
|
||||
{ BTN_TRIGGER_HAPPY21 , "Happy 21" },
|
||||
{ BTN_TRIGGER_HAPPY22 , "Happy 22" },
|
||||
{ BTN_TRIGGER_HAPPY23 , "Happy 23" },
|
||||
{ BTN_TRIGGER_HAPPY24 , "Happy 24" },
|
||||
{ BTN_TRIGGER_HAPPY25 , "Happy 25" },
|
||||
{ BTN_TRIGGER_HAPPY26 , "Happy 26" },
|
||||
{ BTN_TRIGGER_HAPPY27 , "Happy 27" },
|
||||
{ BTN_TRIGGER_HAPPY28 , "Happy 28" },
|
||||
{ BTN_TRIGGER_HAPPY29 , "Happy 29" },
|
||||
{ BTN_TRIGGER_HAPPY30 , "Happy 30" },
|
||||
{ BTN_TRIGGER_HAPPY31 , "Happy 31" },
|
||||
{ BTN_TRIGGER_HAPPY32 , "Happy 32" },
|
||||
{ BTN_TRIGGER_HAPPY33 , "Happy 33" },
|
||||
{ BTN_TRIGGER_HAPPY34 , "Happy 34" },
|
||||
{ BTN_TRIGGER_HAPPY35 , "Happy 35" },
|
||||
{ BTN_TRIGGER_HAPPY36 , "Happy 36" },
|
||||
{ BTN_TRIGGER_HAPPY37 , "Happy 37" },
|
||||
{ BTN_TRIGGER_HAPPY38 , "Happy 38" },
|
||||
{ BTN_TRIGGER_HAPPY39 , "Happy 39" },
|
||||
{ BTN_TRIGGER_HAPPY40 , "Happy 40" },
|
||||
};
|
||||
|
||||
bool Init() override;
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void Close();
|
||||
// Unique positive axis names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u32, std::string> axis_list =
|
||||
{
|
||||
{ ABS_X , "LX+" },
|
||||
{ ABS_Y , "LY+" },
|
||||
{ ABS_Z , "LZ+" },
|
||||
{ ABS_RX , "RX+" },
|
||||
{ ABS_RY , "RY+" },
|
||||
{ ABS_RZ , "RZ+" },
|
||||
{ ABS_THROTTLE , "Throttle+" },
|
||||
{ ABS_RUDDER , "Rudder+" },
|
||||
{ ABS_WHEEL , "Wheel+" },
|
||||
{ ABS_GAS , "Gas+" },
|
||||
{ ABS_BRAKE , "Brake+" },
|
||||
{ ABS_HAT0X , "Hat0 X+" },
|
||||
{ ABS_HAT0Y , "Hat0 Y+" },
|
||||
{ ABS_HAT1X , "Hat1 X+" },
|
||||
{ ABS_HAT1Y , "Hat1 Y+" },
|
||||
{ ABS_HAT2X , "Hat2 X+" },
|
||||
{ ABS_HAT2Y , "Hat2 Y+" },
|
||||
{ ABS_HAT3X , "Hat3 X+" },
|
||||
{ ABS_HAT3Y , "Hat3 Y+" },
|
||||
{ ABS_PRESSURE , "Pressure+" },
|
||||
{ ABS_DISTANCE , "Distance+" },
|
||||
{ ABS_TILT_X , "Tilt X+" },
|
||||
{ ABS_TILT_Y , "Tilt Y+" },
|
||||
{ ABS_TOOL_WIDTH , "Width+" },
|
||||
{ ABS_VOLUME , "Volume+" },
|
||||
{ ABS_MISC , "Misc+" },
|
||||
{ ABS_MT_SLOT , "Slot+" },
|
||||
{ ABS_MT_TOUCH_MAJOR , "MT TMaj+" },
|
||||
{ ABS_MT_TOUCH_MINOR , "MT TMin+" },
|
||||
{ ABS_MT_WIDTH_MAJOR , "MT WMaj+" },
|
||||
{ ABS_MT_WIDTH_MINOR , "MT WMin+" },
|
||||
{ ABS_MT_ORIENTATION , "MT Orient+" },
|
||||
{ ABS_MT_POSITION_X , "MT PosX+" },
|
||||
{ ABS_MT_POSITION_Y , "MT PosY+" },
|
||||
{ ABS_MT_TOOL_TYPE , "MT TType+" },
|
||||
{ ABS_MT_BLOB_ID , "MT Blob ID+" },
|
||||
{ ABS_MT_TRACKING_ID , "MT Track ID+" },
|
||||
{ ABS_MT_PRESSURE , "MT Pressure+" },
|
||||
{ ABS_MT_DISTANCE , "MT Distance+" },
|
||||
{ ABS_MT_TOOL_X , "MT Tool X+" },
|
||||
{ ABS_MT_TOOL_Y , "MT Tool Y+" },
|
||||
};
|
||||
|
||||
// Unique negative axis names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u32, std::string> rev_axis_list =
|
||||
{
|
||||
{ ABS_X , "LX-" },
|
||||
{ ABS_Y , "LY-" },
|
||||
{ ABS_Z , "LZ-" },
|
||||
{ ABS_RX , "RX-" },
|
||||
{ ABS_RY , "RY-" },
|
||||
{ ABS_RZ , "RZ-" },
|
||||
{ ABS_THROTTLE , "Throttle-" },
|
||||
{ ABS_RUDDER , "Rudder-" },
|
||||
{ ABS_WHEEL , "Wheel-" },
|
||||
{ ABS_GAS , "Gas-" },
|
||||
{ ABS_BRAKE , "Brake-" },
|
||||
{ ABS_HAT0X , "Hat0 X-" },
|
||||
{ ABS_HAT0Y , "Hat0 Y-" },
|
||||
{ ABS_HAT1X , "Hat1 X-" },
|
||||
{ ABS_HAT1Y , "Hat1 Y-" },
|
||||
{ ABS_HAT2X , "Hat2 X-" },
|
||||
{ ABS_HAT2Y , "Hat2 Y-" },
|
||||
{ ABS_HAT3X , "Hat3 X-" },
|
||||
{ ABS_HAT3Y , "Hat3 Y-" },
|
||||
{ ABS_PRESSURE , "Pressure-" },
|
||||
{ ABS_DISTANCE , "Distance-" },
|
||||
{ ABS_TILT_X , "Tilt X-" },
|
||||
{ ABS_TILT_Y , "Tilt Y-" },
|
||||
{ ABS_TOOL_WIDTH , "Width-" },
|
||||
{ ABS_VOLUME , "Volume-" },
|
||||
{ ABS_MISC , "Misc-" },
|
||||
{ ABS_MT_SLOT , "Slot-" },
|
||||
{ ABS_MT_TOUCH_MAJOR , "MT TMaj-" },
|
||||
{ ABS_MT_TOUCH_MINOR , "MT TMin-" },
|
||||
{ ABS_MT_WIDTH_MAJOR , "MT WMaj-" },
|
||||
{ ABS_MT_WIDTH_MINOR , "MT WMin-" },
|
||||
{ ABS_MT_ORIENTATION , "MT Orient-" },
|
||||
{ ABS_MT_POSITION_X , "MT PosX-" },
|
||||
{ ABS_MT_POSITION_Y , "MT PosY-" },
|
||||
{ ABS_MT_TOOL_TYPE , "MT TType-" },
|
||||
{ ABS_MT_BLOB_ID , "MT Blob ID-" },
|
||||
{ ABS_MT_TRACKING_ID , "MT Track ID-" },
|
||||
{ ABS_MT_PRESSURE , "MT Pressure-" },
|
||||
{ ABS_MT_DISTANCE , "MT Distance-" },
|
||||
{ ABS_MT_TOOL_X , "MT Tool X-" },
|
||||
{ ABS_MT_TOOL_Y , "MT Tool Y-" },
|
||||
};
|
||||
|
||||
struct EvdevDevice
|
||||
{
|
||||
libevdev* device;
|
||||
std::string path;
|
||||
std::shared_ptr<Pad> pad;
|
||||
std::unordered_map<int, bool> axis_orientations; // value is true if key was found in rev_axis_list
|
||||
float stick_val[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
u16 val_min[4] = { 0, 0, 0, 0 };
|
||||
u16 val_max[4] = { 0, 0, 0, 0 };
|
||||
};
|
||||
|
||||
const int BUTTON_COUNT = 17;
|
||||
|
||||
public:
|
||||
evdev_joystick_handler();
|
||||
~evdev_joystick_handler();
|
||||
|
||||
bool Init() override;
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void Close();
|
||||
void GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback) override;
|
||||
void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override;
|
||||
|
||||
private:
|
||||
void update_devs();
|
||||
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY);
|
||||
bool try_open_dev(u32 index);
|
||||
int scale_axis(int axis, int value);
|
||||
void TranslateButtonPress(u64 keyCode, bool& pressed, u16& value, bool ignore_threshold = false) override;
|
||||
bool update_device(EvdevDevice& device, bool use_cell = true);
|
||||
void update_devs(bool use_cell = true);
|
||||
int add_device(const std::string& device, bool in_settings = false, std::shared_ptr<Pad> pad = nullptr, const std::unordered_map<int, bool>& axis_map = std::unordered_map<int, bool>());
|
||||
int GetButtonInfo(const input_event& evt, libevdev* dev, int& button_code, bool& is_negative);
|
||||
std::unordered_map<u64, std::pair<u16, bool>> GetButtonValues(libevdev* dev);
|
||||
|
||||
std::vector<std::string> joy_paths;
|
||||
std::vector<std::shared_ptr<Pad>> pads;
|
||||
std::vector<libevdev*> joy_devs;
|
||||
std::vector<std::vector<int>> joy_button_maps;
|
||||
std::vector<std::vector<int>> joy_axis_maps;
|
||||
// joy_axis is only used for squircling
|
||||
std::vector<std::vector<int>> joy_axis;
|
||||
std::vector<int> joy_hat_ids;
|
||||
bool axistrigger;
|
||||
std::map<int, std::pair<int, int>> axis_ranges;
|
||||
std::vector<bool> revaxis;
|
||||
// Search axis_orientations map for the direction by index, returns -1 if not found, 0 for positive and 1 for negative
|
||||
int FindAxisDirection(const std::unordered_map<int, bool>& map, int index);
|
||||
|
||||
std::vector<EvdevDevice> devices;
|
||||
int m_pad_index = -1;
|
||||
};
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
#include "rpcs3qt/pad_settings_dialog.h"
|
||||
|
||||
keyboard_pad_config g_kbpad_config;
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
||||
bool keyboard_pad_handler::Init()
|
||||
{
|
||||
@ -15,17 +16,48 @@ bool keyboard_pad_handler::Init()
|
||||
|
||||
keyboard_pad_handler::keyboard_pad_handler() : QObject()
|
||||
{
|
||||
b_has_config = true;
|
||||
}
|
||||
// Set this handler's type and save location
|
||||
m_pad_config.cfg_type = "keyboard";
|
||||
m_pad_config.cfg_name = fs::get_config_dir() + "/config_kbpad_qt.yml";
|
||||
|
||||
void keyboard_pad_handler::ConfigController(std::string device)
|
||||
{
|
||||
pad_settings_dialog dlg;
|
||||
dlg.exec();
|
||||
// Set default button mapping
|
||||
m_pad_config.ls_left.def = GetKeyName(Qt::Key_A);
|
||||
m_pad_config.ls_down.def = GetKeyName(Qt::Key_S);
|
||||
m_pad_config.ls_right.def = GetKeyName(Qt::Key_D);
|
||||
m_pad_config.ls_up.def = GetKeyName(Qt::Key_W);
|
||||
m_pad_config.rs_left.def = GetKeyName(Qt::Key_Home);
|
||||
m_pad_config.rs_down.def = GetKeyName(Qt::Key_PageDown);
|
||||
m_pad_config.rs_right.def = GetKeyName(Qt::Key_End);
|
||||
m_pad_config.rs_up.def = GetKeyName(Qt::Key_PageUp);
|
||||
m_pad_config.start.def = GetKeyName(Qt::Key_Return);
|
||||
m_pad_config.select.def = GetKeyName(Qt::Key_Space);
|
||||
m_pad_config.ps.def = GetKeyName(Qt::Key_Backspace);
|
||||
m_pad_config.square.def = GetKeyName(Qt::Key_Z);
|
||||
m_pad_config.cross.def = GetKeyName(Qt::Key_X);
|
||||
m_pad_config.circle.def = GetKeyName(Qt::Key_C);
|
||||
m_pad_config.triangle.def = GetKeyName(Qt::Key_V);
|
||||
m_pad_config.left.def = GetKeyName(Qt::Key_Left);
|
||||
m_pad_config.down.def = GetKeyName(Qt::Key_Down);
|
||||
m_pad_config.right.def = GetKeyName(Qt::Key_Right);
|
||||
m_pad_config.up.def = GetKeyName(Qt::Key_Up);
|
||||
m_pad_config.r1.def = GetKeyName(Qt::Key_E);
|
||||
m_pad_config.r2.def = GetKeyName(Qt::Key_T);
|
||||
m_pad_config.r3.def = GetKeyName(Qt::Key_G);
|
||||
m_pad_config.l1.def = GetKeyName(Qt::Key_Q);
|
||||
m_pad_config.l2.def = GetKeyName(Qt::Key_R);
|
||||
m_pad_config.l3.def = GetKeyName(Qt::Key_F);
|
||||
|
||||
// apply defaults
|
||||
m_pad_config.from_default();
|
||||
|
||||
// set capabilities
|
||||
b_has_config = true;
|
||||
}
|
||||
|
||||
void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value)
|
||||
{
|
||||
value = Clamp0To255(value);
|
||||
|
||||
for (auto pad : bindings)
|
||||
{
|
||||
for (Button& button : pad->m_buttons)
|
||||
@ -33,13 +65,10 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value)
|
||||
if (button.m_keyCode != code)
|
||||
continue;
|
||||
|
||||
if (value >= 256) { value = 255; }
|
||||
|
||||
//Todo: Is this flush necessary once games hit decent speeds?
|
||||
if (button.m_pressed && !pressed)
|
||||
{
|
||||
button.m_flush = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -76,7 +105,6 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool keyboard_pad_handler::eventFilter(QObject* target, QEvent* ev)
|
||||
{
|
||||
// !m_target is for future proofing when gsrender isn't automatically initialized on load.
|
||||
@ -123,36 +151,29 @@ void keyboard_pad_handler::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
case Qt::Key_L:
|
||||
if (!(event->modifiers() == Qt::AltModifier)) { Key(event->key(), 1); }
|
||||
event->ignore();
|
||||
break;
|
||||
case Qt::Key_Return:
|
||||
if (!(event->modifiers() == Qt::AltModifier)) { Key(event->key(), 1); }
|
||||
event->ignore();
|
||||
break;
|
||||
case Qt::Key_Escape:
|
||||
event->ignore();
|
||||
break;
|
||||
case Qt::Key_P:
|
||||
if (!(event->modifiers() == Qt::ControlModifier)) { Key(event->key(), 1); }
|
||||
event->ignore();
|
||||
break;
|
||||
case Qt::Key_S:
|
||||
if (!(event->modifiers() == Qt::ControlModifier)) { Key(event->key(), 1); }
|
||||
event->ignore();
|
||||
break;
|
||||
case Qt::Key_R:
|
||||
if (!(event->modifiers() == Qt::ControlModifier)) { Key(event->key(), 1); }
|
||||
event->ignore();
|
||||
break;
|
||||
case Qt::Key_E:
|
||||
if (!(event->modifiers() == Qt::ControlModifier)) { Key(event->key(), 1); }
|
||||
event->ignore();
|
||||
break;
|
||||
default:
|
||||
Key(event->key(), 1);
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void keyboard_pad_handler::keyReleaseEvent(QKeyEvent* event)
|
||||
@ -174,42 +195,119 @@ std::vector<std::string> keyboard_pad_handler::ListDevices()
|
||||
return list_devices;
|
||||
}
|
||||
|
||||
std::string keyboard_pad_handler::GetKeyName(const QKeyEvent* keyEvent)
|
||||
{
|
||||
//TODO what about numpad?
|
||||
// Fix some unknown button names
|
||||
switch (keyEvent->key())
|
||||
{
|
||||
case Qt::Key_Alt:
|
||||
return "Alt";
|
||||
case Qt::Key_AltGr:
|
||||
return "AltGr";
|
||||
case Qt::Key_Shift:
|
||||
return "Shift";
|
||||
case Qt::Key_Control:
|
||||
return "Ctrl";
|
||||
case Qt::Key_NumLock:
|
||||
return sstr(QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return sstr(QKeySequence(keyEvent->key() | keyEvent->modifiers()).toString(QKeySequence::NativeText));
|
||||
}
|
||||
|
||||
std::string keyboard_pad_handler::GetKeyName(const u32& keyCode)
|
||||
{
|
||||
//TODO what about numpad?
|
||||
return sstr(QKeySequence(keyCode).toString(QKeySequence::NativeText));
|
||||
}
|
||||
|
||||
u32 keyboard_pad_handler::GetKeyCode(const std::string& keyName)
|
||||
{
|
||||
if (keyName == "Alt")
|
||||
return Qt::Key_Alt;
|
||||
else if (keyName == "AltGr")
|
||||
return Qt::Key_AltGr;
|
||||
else if (keyName == "Shift")
|
||||
return Qt::Key_Shift;
|
||||
else if (keyName == "Ctrl")
|
||||
return Qt::Key_Control;
|
||||
|
||||
QString key = qstr(keyName);
|
||||
QKeySequence seq(key);
|
||||
u32 keyCode;
|
||||
// We should only working with a single key here
|
||||
if (seq.count() == 1)
|
||||
{
|
||||
keyCode = seq[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should be here only if a modifier key (e.g. Ctrl, Alt) is pressed.
|
||||
if (seq.count() != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Add a non-modifier key "A" to the picture because QKeySequence
|
||||
// seems to need that to acknowledge the modifier. We know that A has
|
||||
// a keyCode of 65 (or 0x41 in hex)
|
||||
seq = QKeySequence(key + "+A");
|
||||
if (seq.count() != 0 || seq[0] <= 65)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
keyCode = seq[0] - 65;
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device)
|
||||
{
|
||||
if (device != "Keyboard") return false;
|
||||
|
||||
g_kbpad_config.load();
|
||||
m_pad_config.load();
|
||||
|
||||
//Fixed assign change, default is both sensor and press off
|
||||
pad->Init(
|
||||
pad->Init
|
||||
(
|
||||
CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.left, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.down, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.right, CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.up, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.start, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_kbpad_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.left), CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.down), CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.right), CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.up), CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.start), CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.r3), CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.l3), CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, GetKeyCode(m_pad_config.select), CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.ps), 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.square), CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.cross), CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.circle), CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.triangle), CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.r1), CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.l1), CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.r2), CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, GetKeyCode(m_pad_config.l2), CELL_PAD_CTRL_L2);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_kbpad_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, GetKeyCode(m_pad_config.ls_left), GetKeyCode(m_pad_config.ls_right));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, GetKeyCode(m_pad_config.ls_up), GetKeyCode(m_pad_config.ls_down));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, GetKeyCode(m_pad_config.rs_left), GetKeyCode(m_pad_config.rs_right));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, GetKeyCode(m_pad_config.rs_up), GetKeyCode(m_pad_config.rs_down));
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, g_kbpad_config.left_stick_left, g_kbpad_config.left_stick_right);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, g_kbpad_config.left_stick_up, g_kbpad_config.left_stick_down);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, g_kbpad_config.right_stick_left, g_kbpad_config.right_stick_right);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, g_kbpad_config.right_stick_up, g_kbpad_config.right_stick_down);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_X, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Y, 399);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Z, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_G, 512);
|
||||
|
||||
pad->m_vibrateMotors.emplace_back(true, 0);
|
||||
pad->m_vibrateMotors.emplace_back(false, 0);
|
||||
|
||||
bindings.push_back(pad);
|
||||
|
||||
|
@ -8,51 +8,6 @@
|
||||
#include <QWindow>
|
||||
#include <QKeyEvent>
|
||||
|
||||
struct keyboard_pad_config final : cfg::node
|
||||
{
|
||||
const std::string cfg_name = fs::get_config_dir() + "/config_kbpad_qt.yml";
|
||||
|
||||
cfg::int32 left_stick_left{ this, "Left Analog Stick Left", Qt::Key_A };
|
||||
cfg::int32 left_stick_down{ this, "Left Analog Stick Down", Qt::Key_S };
|
||||
cfg::int32 left_stick_right{ this, "Left Analog Stick Right", Qt::Key_D };
|
||||
cfg::int32 left_stick_up{ this, "Left Analog Stick Up", Qt::Key_W };
|
||||
cfg::int32 right_stick_left{ this, "Right Analog Stick Left", Qt::Key_Home };
|
||||
cfg::int32 right_stick_down{ this, "Right Analog Stick Down", Qt::Key_PageDown };
|
||||
cfg::int32 right_stick_right{ this, "Right Analog Stick Right", Qt::Key_End };
|
||||
cfg::int32 right_stick_up{ this, "Right Analog Stick Up", Qt::Key_PageUp };
|
||||
cfg::int32 start{ this, "Start", Qt::Key_Return };
|
||||
cfg::int32 select{ this, "Select", Qt::Key_Space };
|
||||
cfg::int32 square{ this, "Square", Qt::Key_Z };
|
||||
cfg::int32 cross{ this, "Cross", Qt::Key_X };
|
||||
cfg::int32 circle{ this, "Circle", Qt::Key_C };
|
||||
cfg::int32 triangle{ this, "Triangle", Qt::Key_V };
|
||||
cfg::int32 left{ this, "Left", Qt::Key_Left };
|
||||
cfg::int32 down{ this, "Down", Qt::Key_Down };
|
||||
cfg::int32 right{ this, "Right", Qt::Key_Right };
|
||||
cfg::int32 up{ this, "Up", Qt::Key_Up };
|
||||
cfg::int32 r1{ this, "R1", Qt::Key_E };
|
||||
cfg::int32 r2{ this, "R2", Qt::Key_T };
|
||||
cfg::int32 r3{ this, "R3", Qt::Key_G };
|
||||
cfg::int32 l1{ this, "L1", Qt::Key_Q };
|
||||
cfg::int32 l2{ this, "L2", Qt::Key_R };
|
||||
cfg::int32 l3{ this, "L3", Qt::Key_F };
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
};
|
||||
|
||||
class keyboard_pad_handler final : public QObject, public PadHandlerBase
|
||||
{
|
||||
public:
|
||||
@ -69,7 +24,10 @@ public:
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void ConfigController(std::string device) override;
|
||||
|
||||
std::string GetKeyName(const QKeyEvent* keyEvent);
|
||||
std::string GetKeyName(const u32& keyCode);
|
||||
u32 GetKeyCode(const std::string& keyName);
|
||||
|
||||
protected:
|
||||
void Key(const u32 code, bool pressed, u16 value = 255);
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
template <typename... Args>
|
||||
inline auto tr(Args&&... args)
|
||||
|
@ -2,9 +2,8 @@
|
||||
#ifdef _WIN32
|
||||
#include "mm_joystick_handler.h"
|
||||
|
||||
mm_joystick_config g_mmjoystick_config;
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
const DWORD THREAD_SLEEP = 10;
|
||||
const DWORD THREAD_SLEEP_INACTIVE = 100;
|
||||
const DWORD THREAD_TIMEOUT = 1000;
|
||||
@ -15,9 +14,56 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mm_joystick_handler::mm_joystick_handler() : is_init(false)
|
||||
{
|
||||
// Set this handler's type and save location
|
||||
m_pad_config.cfg_type = "mmjoystick";
|
||||
m_pad_config.cfg_name = fs::get_config_dir() + "/config_mmjoystick.yml";
|
||||
|
||||
// Set default button mapping
|
||||
m_pad_config.ls_left.def = axis_list.at(mmjoy_axis::joy_x_neg);
|
||||
m_pad_config.ls_down.def = axis_list.at(mmjoy_axis::joy_y_neg);
|
||||
m_pad_config.ls_right.def = axis_list.at(mmjoy_axis::joy_x_pos);
|
||||
m_pad_config.ls_up.def = axis_list.at(mmjoy_axis::joy_y_pos);
|
||||
m_pad_config.rs_left.def = axis_list.at(mmjoy_axis::joy_u_neg);
|
||||
m_pad_config.rs_down.def = axis_list.at(mmjoy_axis::joy_r_neg);
|
||||
m_pad_config.rs_right.def = axis_list.at(mmjoy_axis::joy_u_pos);
|
||||
m_pad_config.rs_up.def = axis_list.at(mmjoy_axis::joy_r_pos);
|
||||
m_pad_config.start.def = button_list.at(JOY_BUTTON8);
|
||||
m_pad_config.select.def = button_list.at(JOY_BUTTON7);
|
||||
m_pad_config.ps.def = button_list.at(JOY_BUTTON17);
|
||||
m_pad_config.square.def = button_list.at(JOY_BUTTON3);
|
||||
m_pad_config.cross.def = button_list.at(JOY_BUTTON1);
|
||||
m_pad_config.circle.def = button_list.at(JOY_BUTTON2);
|
||||
m_pad_config.triangle.def = button_list.at(JOY_BUTTON4);
|
||||
m_pad_config.left.def = pov_list.at(JOY_POVLEFT);
|
||||
m_pad_config.down.def = pov_list.at(JOY_POVBACKWARD);
|
||||
m_pad_config.right.def = pov_list.at(JOY_POVRIGHT);
|
||||
m_pad_config.up.def = pov_list.at(JOY_POVFORWARD);
|
||||
m_pad_config.r1.def = button_list.at(JOY_BUTTON6);
|
||||
m_pad_config.r2.def = axis_list.at(mmjoy_axis::joy_z_pos);
|
||||
m_pad_config.r3.def = button_list.at(JOY_BUTTON10);
|
||||
m_pad_config.l1.def = button_list.at(JOY_BUTTON5);
|
||||
m_pad_config.l2.def = axis_list.at(mmjoy_axis::joy_z_neg);
|
||||
m_pad_config.l3.def = button_list.at(JOY_BUTTON9);
|
||||
|
||||
// Set default misc variables
|
||||
m_pad_config.lstickdeadzone.def = 0; // between 0 and 255
|
||||
m_pad_config.rstickdeadzone.def = 0; // between 0 and 255
|
||||
m_pad_config.ltriggerthreshold.def = 0; // between 0 and 255
|
||||
m_pad_config.rtriggerthreshold.def = 0; // between 0 and 255
|
||||
m_pad_config.padsquircling.def = 8000;
|
||||
|
||||
// apply defaults
|
||||
m_pad_config.from_default();
|
||||
|
||||
// set capabilities
|
||||
b_has_config = true;
|
||||
b_has_rumble = true;
|
||||
b_has_deadzones = true;
|
||||
|
||||
m_trigger_threshold = trigger_max / 2;
|
||||
m_thumb_threshold = thumb_max / 2;
|
||||
}
|
||||
|
||||
mm_joystick_handler::~mm_joystick_handler()
|
||||
@ -50,6 +96,7 @@ bool mm_joystick_handler::Init()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pad_config.load();
|
||||
is_init = true;
|
||||
return true;
|
||||
}
|
||||
@ -69,38 +116,60 @@ bool mm_joystick_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::s
|
||||
{
|
||||
if (!Init()) return false;
|
||||
|
||||
g_mmjoystick_config.load();
|
||||
auto find_key = [=](const std::string& name)
|
||||
{
|
||||
long key = FindKeyCode(button_list, name);
|
||||
if (key < 0)
|
||||
key = FindKeyCode(pov_list, name);
|
||||
if (key < 0)
|
||||
key = FindKeyCode(axis_list, name);
|
||||
if (key < 0)
|
||||
{
|
||||
LOG_ERROR(HLE, "mmjoystick FindKey(%s) returned value %d", name, key);
|
||||
key = -1;
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
pad->Init
|
||||
(
|
||||
CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.triangle, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.circle, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.cross, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.square, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l2, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r2, CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.l1, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, g_mmjoystick_config.r1, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.start, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.select, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.l3, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, g_mmjoystick_config.r3, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.triangle), CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.circle), CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.cross), CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.square), CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.l2), CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.r2), CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.l1), CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.r1), CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.start), CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.select), CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.l3), CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.r3), CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_key(m_pad_config.ps), 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.up), CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.down), CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.left), CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_key(m_pad_config.right), CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVFORWARD, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVBACKWARD, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVLEFT, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, JOY_POVRIGHT, CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, find_key(m_pad_config.ls_left), find_key(m_pad_config.ls_right));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, find_key(m_pad_config.ls_down), find_key(m_pad_config.ls_up));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, find_key(m_pad_config.rs_left), find_key(m_pad_config.rs_right));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, find_key(m_pad_config.rs_down), find_key(m_pad_config.rs_up));
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_X, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Y, 399);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Z, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_G, 512);
|
||||
|
||||
pad->m_vibrateMotors.emplace_back(true, 0);
|
||||
pad->m_vibrateMotors.emplace_back(false, 0);
|
||||
|
||||
bindings.push_back(pad);
|
||||
|
||||
@ -132,25 +201,205 @@ void mm_joystick_handler::ThreadProc()
|
||||
pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES;
|
||||
last_connection_status[i] = true;
|
||||
pad->m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
for (DWORD j = 0; j <= 12; j++)
|
||||
|
||||
auto button_values = GetButtonValues();
|
||||
|
||||
// Translate any corresponding keycodes to our normal DS3 buttons and triggers
|
||||
for (auto& btn : pad->m_buttons)
|
||||
{
|
||||
bool pressed = js_info.dwButtons & pad->m_buttons[j].m_keyCode;
|
||||
pad->m_buttons[j].m_pressed = pressed;
|
||||
pad->m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
btn.m_value = button_values[btn.m_keyCode];
|
||||
TranslateButtonPress(btn.m_keyCode, btn.m_pressed, btn.m_value);
|
||||
}
|
||||
for (DWORD j = 13; j <= 16; j++)//POV aka digital pad
|
||||
|
||||
float stick_val[4];
|
||||
|
||||
// Translate any corresponding keycodes to our two sticks. (ignoring thresholds for now)
|
||||
for (int i = 0; i < static_cast<int>(pad->m_sticks.size()); i++)
|
||||
{
|
||||
bool pressed = js_info.dwPOV == pad->m_buttons[j].m_keyCode;
|
||||
pad->m_buttons[j].m_pressed = pressed;
|
||||
pad->m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
bool pressed;
|
||||
|
||||
// m_keyCodeMin is the mapped key for left or down
|
||||
u32 key_min = pad->m_sticks[i].m_keyCodeMin;
|
||||
u16 val_min = button_values[key_min];
|
||||
TranslateButtonPress(key_min, pressed, val_min, true);
|
||||
|
||||
// m_keyCodeMax is the mapped key for right or up
|
||||
u32 key_max = pad->m_sticks[i].m_keyCodeMax;
|
||||
u16 val_max = button_values[key_max];
|
||||
TranslateButtonPress(key_max, pressed, val_max, true);
|
||||
|
||||
// cancel out opposing values and get the resulting difference
|
||||
stick_val[i] = val_max - val_min;
|
||||
}
|
||||
pad->m_sticks[0].m_value = ConvertAxis(js_info.dwXpos);
|
||||
pad->m_sticks[1].m_value = ConvertAxis(js_info.dwYpos);
|
||||
pad->m_sticks[2].m_value = ConvertAxis(js_info.dwZpos);
|
||||
pad->m_sticks[3].m_value = ConvertAxis(js_info.dwRpos);
|
||||
|
||||
u16 lx, ly, rx, ry;
|
||||
|
||||
// Normalize our two stick's axis based on the thresholds
|
||||
std::tie(lx, ly) = NormalizeStickDeadzone(stick_val[0], stick_val[1], m_pad_config.lstickdeadzone);
|
||||
std::tie(rx, ry) = NormalizeStickDeadzone(stick_val[2], stick_val[3], m_pad_config.rstickdeadzone);
|
||||
|
||||
if (m_pad_config.padsquircling != 0)
|
||||
{
|
||||
std::tie(lx, ly) = ConvertToSquirclePoint(lx, ly, m_pad_config.padsquircling);
|
||||
std::tie(rx, ry) = ConvertToSquirclePoint(rx, ry, m_pad_config.padsquircling);
|
||||
}
|
||||
|
||||
pad->m_sticks[0].m_value = lx;
|
||||
pad->m_sticks[1].m_value = 255 - ly;
|
||||
pad->m_sticks[2].m_value = rx;
|
||||
pad->m_sticks[3].m_value = 255 - ry;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mm_joystick_handler::GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback)
|
||||
{
|
||||
if (!Init())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MMRESULT status = joyGetPosEx(JOYSTICKID1, &js_info);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case JOYERR_UNPLUGGED:
|
||||
break;
|
||||
|
||||
case JOYERR_NOERROR:
|
||||
auto data = GetButtonValues();
|
||||
|
||||
// Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed.
|
||||
// Return the new value if the button was pressed (aka. its value was bigger than 0 or the defined threshold)
|
||||
// Use a pair to get all the legally pressed buttons and use the one with highest value (prioritize first)
|
||||
std::pair<u16, std::string> pressed_button = { 0, "" };
|
||||
for (const auto& button : button_list)
|
||||
{
|
||||
u16 value = data[button.first];
|
||||
if (value > 0 && value > pressed_button.first)
|
||||
pressed_button = { value, button.second };
|
||||
}
|
||||
for (const auto& button : pov_list)
|
||||
{
|
||||
u16 value = data[button.first];
|
||||
if (value > 0 && value > pressed_button.first)
|
||||
pressed_button = { value, button.second };
|
||||
}
|
||||
for (const auto& button : axis_list)
|
||||
{
|
||||
u32 keycode = button.first;
|
||||
u16 value = data[keycode];
|
||||
|
||||
if (((keycode == mmjoy_axis::joy_z_neg) && (value > m_trigger_threshold))
|
||||
|| ((keycode == mmjoy_axis::joy_z_pos) && (value > m_trigger_threshold))
|
||||
|| ((keycode <= mmjoy_axis::joy_y_neg) && (value > m_thumb_threshold))
|
||||
|| ((keycode <= mmjoy_axis::joy_u_neg && keycode > mmjoy_axis::joy_z_neg) && (value > m_thumb_threshold)))
|
||||
{
|
||||
if (value > pressed_button.first)
|
||||
{
|
||||
pressed_button = { value, button.second };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int preview_values[6] =
|
||||
{
|
||||
data[mmjoy_axis::joy_z_neg],
|
||||
data[mmjoy_axis::joy_z_pos],
|
||||
data[mmjoy_axis::joy_x_pos] - data[mmjoy_axis::joy_x_neg],
|
||||
data[mmjoy_axis::joy_y_pos] - data[mmjoy_axis::joy_y_neg],
|
||||
data[mmjoy_axis::joy_u_pos] - data[mmjoy_axis::joy_u_neg],
|
||||
data[mmjoy_axis::joy_r_pos] - data[mmjoy_axis::joy_r_neg]
|
||||
};
|
||||
|
||||
if (pressed_button.first > 0)
|
||||
return callback(pressed_button.first, pressed_button.second, preview_values);
|
||||
else
|
||||
return callback(0, "", preview_values);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mm_joystick_handler::TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold)
|
||||
{
|
||||
// Update the pad button values based on their type and thresholds.
|
||||
// With this you can use axis or triggers as buttons or vice versa
|
||||
switch (keyCode)
|
||||
{
|
||||
case mmjoy_axis::joy_z_neg:
|
||||
pressed = val > m_pad_config.ltriggerthreshold;
|
||||
val = pressed ? NormalizeTriggerInput(val, m_pad_config.ltriggerthreshold) : 0;
|
||||
break;
|
||||
case mmjoy_axis::joy_z_pos:
|
||||
pressed = val > m_pad_config.rtriggerthreshold;
|
||||
val = pressed ? NormalizeTriggerInput(val, m_pad_config.rtriggerthreshold) : 0;
|
||||
break;
|
||||
case mmjoy_axis::joy_x_pos:
|
||||
case mmjoy_axis::joy_x_neg:
|
||||
case mmjoy_axis::joy_y_pos:
|
||||
case mmjoy_axis::joy_y_neg:
|
||||
pressed = val > (ignore_threshold ? 0 : m_pad_config.lstickdeadzone);
|
||||
val = pressed ? NormalizeStickInput(val, m_pad_config.lstickdeadzone, ignore_threshold) : 0;
|
||||
break;
|
||||
case mmjoy_axis::joy_r_pos:
|
||||
case mmjoy_axis::joy_r_neg:
|
||||
case mmjoy_axis::joy_u_pos:
|
||||
case mmjoy_axis::joy_u_neg:
|
||||
pressed = val > (ignore_threshold ? 0 : m_pad_config.rstickdeadzone);
|
||||
val = pressed ? NormalizeStickInput(val, m_pad_config.rstickdeadzone, ignore_threshold) : 0;
|
||||
break;
|
||||
default: // normal button (should in theory also support sensitive buttons)
|
||||
pressed = val > 0;
|
||||
val = pressed ? val : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<u64, u16> mm_joystick_handler::GetButtonValues()
|
||||
{
|
||||
std::unordered_map<u64, u16> button_values;
|
||||
|
||||
for (auto entry : button_list)
|
||||
{
|
||||
button_values.emplace(entry.first, js_info.dwButtons & entry.first ? 255 : 0);
|
||||
}
|
||||
|
||||
for (auto entry : pov_list)
|
||||
{
|
||||
button_values.emplace(entry.first, js_info.dwPOV == entry.first ? 255 : 0);
|
||||
}
|
||||
|
||||
auto add_axis_value = [&](DWORD axis, u64 pos, u64 neg)
|
||||
{
|
||||
u16 value = 0;
|
||||
float fvalue = ScaleStickInput(axis, 0, 65535);
|
||||
bool is_negative = fvalue <= 127.5;
|
||||
|
||||
if (is_negative)
|
||||
{
|
||||
value = Clamp0To255((127.5f - fvalue) * 2.0f);
|
||||
button_values.emplace(pos, 0);
|
||||
button_values.emplace(neg, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Clamp0To255((fvalue - 127.5f) * 2.0f);
|
||||
button_values.emplace(pos, value);
|
||||
button_values.emplace(neg, 0);
|
||||
}
|
||||
};
|
||||
|
||||
add_axis_value(js_info.dwXpos, mmjoy_axis::joy_x_pos, mmjoy_axis::joy_x_neg);
|
||||
add_axis_value(js_info.dwYpos, mmjoy_axis::joy_y_neg, mmjoy_axis::joy_y_pos);
|
||||
add_axis_value(js_info.dwZpos, mmjoy_axis::joy_z_neg, mmjoy_axis::joy_z_pos);
|
||||
add_axis_value(js_info.dwRpos, mmjoy_axis::joy_r_neg, mmjoy_axis::joy_r_pos);
|
||||
add_axis_value(js_info.dwUpos, mmjoy_axis::joy_u_pos, mmjoy_axis::joy_u_neg);
|
||||
add_axis_value(js_info.dwVpos, mmjoy_axis::joy_v_pos, mmjoy_axis::joy_v_neg);
|
||||
|
||||
return button_values;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -5,53 +5,87 @@
|
||||
#include <mmsystem.h>
|
||||
#include "Utilities/Config.h"
|
||||
|
||||
struct mm_joystick_config final : cfg::node
|
||||
{
|
||||
const std::string cfg_name = fs::get_config_dir() + "/config_mmjoystick.yml";
|
||||
|
||||
//cfg::int32 left_stick_left{this, "Left Analog Stick Left", static_cast<int>('A')};
|
||||
//cfg::int32 left_stick_down{this, "Left Analog Stick Down", static_cast<int>('S')};
|
||||
//cfg::int32 left_stick_right{this, "Left Analog Stick Right", static_cast<int>('D')};
|
||||
//cfg::int32 left_stick_up{this, "Left Analog Stick Up", static_cast<int>('W')};
|
||||
//cfg::int32 right_stick_left{this, "Right Analog Stick Left", 313};
|
||||
//cfg::int32 right_stick_down{this, "Right Analog Stick Down", 367};
|
||||
//cfg::int32 right_stick_right{this, "Right Analog Stick Right", 312};
|
||||
//cfg::int32 right_stick_up{this, "Right Analog Stick Up", 366};
|
||||
cfg::int32 start{this, "Start", JOY_BUTTON9};
|
||||
cfg::int32 select{this, "Select", JOY_BUTTON10};
|
||||
cfg::int32 square{this, "Square", JOY_BUTTON4};
|
||||
cfg::int32 cross{this, "Cross", JOY_BUTTON3};
|
||||
cfg::int32 circle{this, "Circle", JOY_BUTTON2};
|
||||
cfg::int32 triangle{this, "Triangle", JOY_BUTTON1};
|
||||
//cfg::int32 left{this, "Left", 314};
|
||||
//cfg::int32 down{this, "Down", 317};
|
||||
//cfg::int32 right{this, "Right", 316};
|
||||
//cfg::int32 up{this, "Up", 315};
|
||||
cfg::int32 r1{this, "R1", JOY_BUTTON8};
|
||||
cfg::int32 r2{this, "R2", JOY_BUTTON6};
|
||||
cfg::int32 r3{this, "R3", JOY_BUTTON12};
|
||||
cfg::int32 l1{this, "L1", JOY_BUTTON7};
|
||||
cfg::int32 l2{this, "L2", JOY_BUTTON5};
|
||||
cfg::int32 l3{this, "L3", JOY_BUTTON11};
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
};
|
||||
|
||||
class mm_joystick_handler final : public PadHandlerBase
|
||||
{
|
||||
// Unique names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u64, std::string> button_list =
|
||||
{
|
||||
{ JOY_BUTTON1 , "Button 1" },
|
||||
{ JOY_BUTTON2 , "Button 2" },
|
||||
{ JOY_BUTTON3 , "Button 3" },
|
||||
{ JOY_BUTTON4 , "Button 4" },
|
||||
{ JOY_BUTTON5 , "Button 5" },
|
||||
{ JOY_BUTTON6 , "Button 6" },
|
||||
{ JOY_BUTTON7 , "Button 7" },
|
||||
{ JOY_BUTTON8 , "Button 8" },
|
||||
{ JOY_BUTTON9 , "Button 9" },
|
||||
{ JOY_BUTTON10, "Button 10" },
|
||||
{ JOY_BUTTON11, "Button 11" },
|
||||
{ JOY_BUTTON12, "Button 12" },
|
||||
{ JOY_BUTTON13, "Button 13" },
|
||||
{ JOY_BUTTON14, "Button 14" },
|
||||
{ JOY_BUTTON15, "Button 15" },
|
||||
{ JOY_BUTTON16, "Button 16" },
|
||||
{ JOY_BUTTON17, "Button 17" },
|
||||
{ JOY_BUTTON18, "Button 18" },
|
||||
{ JOY_BUTTON19, "Button 19" },
|
||||
{ JOY_BUTTON20, "Button 20" },
|
||||
{ JOY_BUTTON21, "Button 21" },
|
||||
{ JOY_BUTTON22, "Button 22" },
|
||||
{ JOY_BUTTON23, "Button 23" },
|
||||
{ JOY_BUTTON24, "Button 24" },
|
||||
{ JOY_BUTTON25, "Button 25" },
|
||||
{ JOY_BUTTON26, "Button 26" },
|
||||
{ JOY_BUTTON27, "Button 27" },
|
||||
{ JOY_BUTTON28, "Button 28" },
|
||||
{ JOY_BUTTON29, "Button 29" },
|
||||
{ JOY_BUTTON30, "Button 30" },
|
||||
{ JOY_BUTTON31, "Button 31" },
|
||||
{ JOY_BUTTON32, "Button 32" },
|
||||
};
|
||||
|
||||
// Unique names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u64, std::string> pov_list =
|
||||
{
|
||||
{ JOY_POVFORWARD, "POV Up" },
|
||||
{ JOY_POVRIGHT, "POV Right" },
|
||||
{ JOY_POVBACKWARD, "POV Down" },
|
||||
{ JOY_POVLEFT, "POV Left" },
|
||||
};
|
||||
|
||||
enum mmjoy_axis
|
||||
{
|
||||
joy_x_pos = 9700,
|
||||
joy_x_neg,
|
||||
joy_y_pos,
|
||||
joy_y_neg,
|
||||
joy_z_pos,
|
||||
joy_z_neg,
|
||||
joy_r_pos,
|
||||
joy_r_neg,
|
||||
joy_u_pos,
|
||||
joy_u_neg,
|
||||
joy_v_pos,
|
||||
joy_v_neg,
|
||||
};
|
||||
|
||||
// Unique names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u64, std::string> axis_list =
|
||||
{
|
||||
{ joy_x_pos, "X+" },
|
||||
{ joy_x_neg, "X-" },
|
||||
{ joy_y_pos, "Y+" },
|
||||
{ joy_y_neg, "Y-" },
|
||||
{ joy_z_pos, "Z+" },
|
||||
{ joy_z_neg, "Z-" },
|
||||
{ joy_r_pos, "R+" },
|
||||
{ joy_r_neg, "R-" },
|
||||
{ joy_u_pos, "U+" },
|
||||
{ joy_u_neg, "U-" },
|
||||
{ joy_v_pos, "V+" },
|
||||
{ joy_v_neg, "V-" },
|
||||
};
|
||||
|
||||
public:
|
||||
mm_joystick_handler();
|
||||
~mm_joystick_handler();
|
||||
@ -61,14 +95,17 @@ public:
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback) override;
|
||||
|
||||
private:
|
||||
void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) override;
|
||||
std::unordered_map<u64, u16> GetButtonValues();
|
||||
|
||||
bool is_init;
|
||||
u32 supportedJoysticks;
|
||||
JOYINFOEX js_info;
|
||||
JOYCAPS js_caps;
|
||||
JOYINFOEX js_info;
|
||||
JOYCAPS js_caps;
|
||||
|
||||
std::vector<std::shared_ptr<Pad>> bindings;
|
||||
std::array<bool, 7> last_connection_status = {};
|
||||
|
||||
};
|
||||
|
@ -27,8 +27,8 @@ pad_thread::~pad_thread()
|
||||
void pad_thread::Init(const u32 max_connect)
|
||||
{
|
||||
std::memset(&m_info, 0, sizeof(m_info));
|
||||
m_info.max_connect = max_connect;
|
||||
m_info.now_connect = std::min(max_connect, (u32)7); // max 7 pads
|
||||
m_info.max_connect = std::min(max_connect, (u32)7); // max 7 pads
|
||||
m_info.now_connect = 0;
|
||||
|
||||
input_cfg.load();
|
||||
|
||||
@ -38,17 +38,19 @@ void pad_thread::Init(const u32 max_connect)
|
||||
std::shared_ptr<NullPadHandler> nullpad = std::make_shared<NullPadHandler>();
|
||||
handlers.emplace(pad_handler::null, nullpad);
|
||||
|
||||
for (u32 i = 0; i < m_info.now_connect; i++)
|
||||
for (u32 i = 0; i < m_info.max_connect; i++)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler;
|
||||
|
||||
if (handlers.count(input_cfg.player_input[i]) != 0)
|
||||
const auto &handler_type = input_cfg.player_input[i];
|
||||
|
||||
if (handlers.count(handler_type) != 0)
|
||||
{
|
||||
cur_pad_handler = handlers[input_cfg.player_input[i]];
|
||||
cur_pad_handler = handlers[handler_type];
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (input_cfg.player_input[i])
|
||||
switch (handler_type)
|
||||
{
|
||||
case pad_handler::keyboard:
|
||||
keyptr = std::make_shared<keyboard_pad_handler>();
|
||||
@ -75,7 +77,7 @@ void pad_thread::Init(const u32 max_connect)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
handlers.emplace(input_cfg.player_input[i], cur_pad_handler);
|
||||
handlers.emplace(handler_type, cur_pad_handler);
|
||||
}
|
||||
cur_pad_handler->Init();
|
||||
|
||||
@ -88,9 +90,11 @@ void pad_thread::Init(const u32 max_connect)
|
||||
if (cur_pad_handler->bindPadToDevice(m_pads.back(), input_cfg.player_device[i]->to_string()) == false)
|
||||
{
|
||||
//Failed to bind the device to cur_pad_handler so binds to NullPadHandler
|
||||
LOG_ERROR(GENERAL, "Failed to bind device %s to handler %s", input_cfg.player_device[i]->to_string(), input_cfg.player_input[i].to_string());
|
||||
LOG_ERROR(GENERAL, "Failed to bind device %s to handler %s", input_cfg.player_device[i]->to_string(), handler_type.to_string());
|
||||
nullpad->bindPadToDevice(m_pads.back(), input_cfg.player_device[i]->to_string());
|
||||
}
|
||||
else if (handler_type != pad_handler::null)
|
||||
m_info.now_connect++;
|
||||
}
|
||||
|
||||
thread = std::make_shared<std::thread>(&pad_thread::ThreadFunc, this);
|
||||
|
@ -336,6 +336,7 @@
|
||||
<ClCompile Include="basic_keyboard_handler.cpp" />
|
||||
<ClCompile Include="basic_mouse_handler.cpp" />
|
||||
<ClCompile Include="ds4_pad_handler.cpp" />
|
||||
<ClCompile Include="evdev_joystick_handler.cpp" />
|
||||
<ClCompile Include="keyboard_pad_handler.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="mm_joystick_handler.cpp" />
|
||||
@ -985,6 +986,7 @@
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="ds4_pad_handler.h" />
|
||||
<ClInclude Include="evdev_joystick_handler.h" />
|
||||
<ClInclude Include="keyboard_pad_handler.h" />
|
||||
<CustomBuild Include="rpcs3qt\gs_frame.h">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing gs_frame.h...</Message>
|
||||
|
@ -96,6 +96,9 @@
|
||||
<Filter Include="Gui\syntax highlighter">
|
||||
<UniqueIdentifier>{adb985ae-88db-4274-a6fd-6bcf230451ca}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Io\evdev">
|
||||
<UniqueIdentifier>{6992629a-48f1-43ec-8070-d1dba81bcbf9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="\rpcs3qt\*.cpp">
|
||||
@ -554,6 +557,9 @@
|
||||
<ClCompile Include="rpcs3qt\syntax_highlighter.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="evdev_joystick_handler.cpp">
|
||||
<Filter>Io\evdev</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\save_manager_dialog.cpp">
|
||||
<Filter>Gui\save</Filter>
|
||||
</ClCompile>
|
||||
@ -637,6 +643,9 @@
|
||||
<ClInclude Include="rpcs3qt\trophy_tree_widget_item.h">
|
||||
<Filter>Gui\trophy</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="evdev_joystick_handler.h">
|
||||
<Filter>Io\evdev</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "Emu/RSX/CgBinaryProgram.h"
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
cg_disasm_window::cg_disasm_window(std::shared_ptr<gui_settings> xSettings): xgui_settings(xSettings)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
extern std::string g_cfg_defaults; //! Default settings grabbed from Utilities/Config.h
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
inline std::string sstr(const QVariant& _in) { return sstr(_in.toString()); }
|
||||
|
||||
// Emit sorted YAML
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <QLabel>
|
||||
#include <QScrollBar>
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
inline QSize sizeFromSlider(const int& pos) { return gui::gl_icon_size_min + (gui::gl_icon_size_max - gui::gl_icon_size_min) * (pos / (float)gui::gl_max_slider_pos); }
|
||||
|
||||
game_list_frame::game_list_frame(std::shared_ptr<gui_settings> guiSettings, std::shared_ptr<emu_settings> emuSettings, QWidget *parent)
|
||||
|
@ -236,7 +236,7 @@ protected:
|
||||
/** Override inherited method from Qt to allow signalling when close happened.*/
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
bool eventFilter(QObject *object, QEvent *event);
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
private:
|
||||
QPixmap PaintedPixmap(const QImage& img, bool paintConfigIcon = false);
|
||||
bool Boot(const GameInfo& info);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "gamepads_settings_dialog.h"
|
||||
#include "pad_settings_dialog.h"
|
||||
#include "../Emu/Io/PadHandler.h"
|
||||
#include "../ds4_pad_handler.h"
|
||||
#ifdef _WIN32
|
||||
@ -16,6 +17,9 @@
|
||||
|
||||
input_config input_cfg;
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
||||
// taken from https://stackoverflow.com/a/30818424/8353754
|
||||
// because size policies won't work as expected (see similar bugs in Qt bugtracker)
|
||||
inline void resizeComboBoxView(QComboBox* combo)
|
||||
@ -54,43 +58,6 @@ gamepads_settings_dialog::gamepads_settings_dialog(QWidget* parent)
|
||||
input_cfg.from_default();
|
||||
input_cfg.load();
|
||||
|
||||
auto fill_device_combo = [](QComboBox *combo)
|
||||
{
|
||||
std::vector<std::string> str_inputs = input_cfg.player_input[0].to_list();
|
||||
for (int index = 0; index < str_inputs.size(); index++)
|
||||
{
|
||||
combo->addItem(str_inputs[index].c_str());
|
||||
}
|
||||
resizeComboBoxView(combo);
|
||||
};
|
||||
|
||||
auto configure_combos = [=]
|
||||
{
|
||||
//Set the values from config
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
for (int j = 0; j < co_inputtype[i]->count(); j++)
|
||||
{
|
||||
if (co_inputtype[i]->itemText(j).toStdString() == input_cfg.player_input[i].to_string())
|
||||
{
|
||||
co_inputtype[i]->setCurrentIndex(j);
|
||||
ChangeInputType(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < co_deviceID[i]->count(); j++)
|
||||
{
|
||||
if (co_deviceID[i]->itemText(j).toStdString() == input_cfg.player_device[i]->to_string())
|
||||
{
|
||||
co_deviceID[i]->setCurrentIndex(j);
|
||||
ChangeDevice(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
QGroupBox *grp_player = new QGroupBox(QString(tr("Player %1").arg(i+1)));
|
||||
@ -126,7 +93,12 @@ gamepads_settings_dialog::gamepads_settings_dialog(QWidget* parent)
|
||||
grp_player->setFixedSize(grp_player->sizeHint());
|
||||
|
||||
// fill comboboxes after setting the groupbox's size to prevent stretch
|
||||
fill_device_combo(co_inputtype[i]);
|
||||
std::vector<std::string> str_inputs = input_cfg.player_input[0].to_list();
|
||||
for (int index = 0; index < str_inputs.size(); index++)
|
||||
{
|
||||
co_inputtype[i]->addItem(qstr(str_inputs[index]));
|
||||
}
|
||||
resizeComboBoxView(co_inputtype[i]);
|
||||
|
||||
all_players->addWidget(grp_player);
|
||||
|
||||
@ -154,17 +126,38 @@ gamepads_settings_dialog::gamepads_settings_dialog(QWidget* parent)
|
||||
setLayout(dialog_layout);
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
configure_combos();
|
||||
auto configure_combos = [=]
|
||||
{
|
||||
//Set the values from config
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
// No extra loops are necessary because setCurrentText does it for us
|
||||
co_inputtype[i]->setCurrentText(qstr(input_cfg.player_input[i].to_string()));
|
||||
// Device will be empty on some rare occasions, so fill them by force
|
||||
ChangeInputType(i);
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
connect(co_inputtype[i], &QComboBox::currentTextChanged, [=] { ChangeInputType(i); });
|
||||
connect(co_deviceID[i], &QComboBox::currentTextChanged, [=] { ChangeDevice(i); });
|
||||
connect(co_deviceID[i], &QComboBox::currentTextChanged, [=](const QString& dev)
|
||||
{
|
||||
std::string device = sstr(dev);
|
||||
if (!input_cfg.player_device[i]->from_string(device))
|
||||
{
|
||||
//Something went wrong
|
||||
LOG_ERROR(GENERAL, "Failed to convert device string: %s", device);
|
||||
return;
|
||||
}
|
||||
});
|
||||
connect(bu_config[i], &QAbstractButton::clicked, [=] { ClickConfigButton(i); });
|
||||
}
|
||||
connect(ok_button, &QPushButton::pressed, this, &gamepads_settings_dialog::SaveExit);
|
||||
connect(cancel_button, &QPushButton::pressed, this, &gamepads_settings_dialog::CancelExit);
|
||||
connect(refresh_button, &QPushButton::pressed, [=] { configure_combos(); });
|
||||
|
||||
configure_combos();
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::SaveExit()
|
||||
@ -193,20 +186,6 @@ void gamepads_settings_dialog::CancelExit()
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::ChangeDevice(int player)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = input_cfg.player_device[player]->from_string(co_deviceID[player]->currentText().toStdString());
|
||||
|
||||
if (!success)
|
||||
{
|
||||
//Something went wrong
|
||||
LOG_ERROR(GENERAL, "Failed to convert device string:%s", co_deviceID[player]->currentText().toStdString().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PadHandlerBase> gamepads_settings_dialog::GetHandler(pad_handler type)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> ret_handler;
|
||||
@ -244,24 +223,28 @@ std::shared_ptr<PadHandlerBase> gamepads_settings_dialog::GetHandler(pad_handler
|
||||
|
||||
void gamepads_settings_dialog::ChangeInputType(int player)
|
||||
{
|
||||
bool success;
|
||||
std::string device = sstr(co_inputtype[player]->currentText());
|
||||
|
||||
success = input_cfg.player_input[player].from_string(co_inputtype[player]->currentText().toStdString());
|
||||
|
||||
if (!success)
|
||||
// Change this player's current handler
|
||||
if (!input_cfg.player_input[player].from_string(device))
|
||||
{
|
||||
//Something went wrong
|
||||
LOG_ERROR(GENERAL, "Failed to convert input string:%s", co_inputtype[player]->currentText().toStdString().c_str());
|
||||
LOG_ERROR(GENERAL, "Failed to convert input string:%s", device);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get this player's current handler and it's currently available devices
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler = GetHandler(input_cfg.player_input[player]);
|
||||
|
||||
std::vector<std::string> list_devices = cur_pad_handler->ListDevices();
|
||||
|
||||
// Refill the device combobox with currently available devices
|
||||
co_deviceID[player]->clear();
|
||||
for (int i = 0; i < list_devices.size(); i++) co_deviceID[player]->addItem(list_devices[i].c_str(), i);
|
||||
for (int i = 0; i < list_devices.size(); i++)
|
||||
{
|
||||
co_deviceID[player]->addItem(qstr(list_devices[i]), i);
|
||||
}
|
||||
|
||||
// Handle empty device list
|
||||
if (list_devices.size() == 0)
|
||||
{
|
||||
co_deviceID[player]->addItem(tr("No Device Detected"), -1);
|
||||
@ -270,14 +253,22 @@ void gamepads_settings_dialog::ChangeInputType(int player)
|
||||
else
|
||||
{
|
||||
co_deviceID[player]->setEnabled(true);
|
||||
co_deviceID[player]->setCurrentText(qstr(device));
|
||||
}
|
||||
|
||||
// Update view and enable configuration if possible
|
||||
resizeComboBoxView(co_deviceID[player]);
|
||||
bu_config[player]->setEnabled(cur_pad_handler->has_config());
|
||||
}
|
||||
|
||||
void gamepads_settings_dialog::ClickConfigButton(int player)
|
||||
{
|
||||
// Get this player's current handler and open its pad settings dialog
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler = GetHandler(input_cfg.player_input[player]);
|
||||
if (cur_pad_handler->has_config()) cur_pad_handler->ConfigController(*input_cfg.player_device[player]);
|
||||
if (cur_pad_handler->has_config())
|
||||
{
|
||||
std::string device = sstr(co_deviceID[player]->currentText());
|
||||
pad_settings_dialog dlg(device, cur_pad_handler);
|
||||
dlg.exec();
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,6 @@ class gamepads_settings_dialog : public QDialog
|
||||
protected:
|
||||
std::shared_ptr<PadHandlerBase> GetHandler(pad_handler type);
|
||||
void ChangeInputType(int player);
|
||||
void ChangeDevice(int player);
|
||||
void ClickConfigButton(int player);
|
||||
void SaveExit();
|
||||
void CancelExit();
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
gui_settings::gui_settings(QObject* parent) : QObject(parent), m_settings(ComputeSettingsDir() + tr("CurrentSettings") + ".ini", QSettings::Format::IniFormat, parent),
|
||||
m_settingsDir(ComputeSettingsDir())
|
||||
|
@ -47,7 +47,7 @@
|
||||
|
||||
#include "ui_main_window.h"
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
main_window::main_window(std::shared_ptr<gui_settings> guiSettings, std::shared_ptr<emu_settings> emuSettings, QWidget *parent)
|
||||
: QMainWindow(parent), guiSettings(guiSettings), emuSettings(emuSettings), m_sys_menu_opened(false), ui(new Ui::main_window)
|
||||
|
@ -5,19 +5,19 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QAction>
|
||||
#include <QButtonGroup>
|
||||
#include <QPainter>
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "pad_settings_dialog.h"
|
||||
#include "ui_pad_settings_dialog.h"
|
||||
|
||||
// TODO: rewrite with std::chrono or QTimer
|
||||
#include <time.h>
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
||||
extern keyboard_pad_config g_kbpad_config;
|
||||
|
||||
pad_settings_dialog::pad_settings_dialog(QWidget *parent) : QDialog(parent), ui(new Ui::pad_settings_dialog)
|
||||
pad_settings_dialog::pad_settings_dialog(const std::string& device, std::shared_ptr<PadHandlerBase> handler, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::pad_settings_dialog), m_handler_cfg(handler->GetConfig()), m_device_name(device), m_handler(handler)
|
||||
{
|
||||
m_handler_cfg->load();
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
@ -25,46 +25,225 @@ pad_settings_dialog::pad_settings_dialog(QWidget *parent) : QDialog(parent), ui(
|
||||
ui->b_cancel->setDefault(true);
|
||||
connect(ui->b_cancel, &QAbstractButton::clicked, this, &QWidget::close);
|
||||
|
||||
// Handling
|
||||
QButtonGroup *padButtons = new QButtonGroup(this);
|
||||
padButtons->addButton(ui->b_left_lstick, 1);
|
||||
padButtons->addButton(ui->b_down_lstick, 2);
|
||||
padButtons->addButton(ui->b_right_lstick, 3);
|
||||
padButtons->addButton(ui->b_up_lstick, 4);
|
||||
|
||||
padButtons->addButton(ui->b_left, 5);
|
||||
padButtons->addButton(ui->b_down, 6);
|
||||
padButtons->addButton(ui->b_right, 7);
|
||||
padButtons->addButton(ui->b_up, 8);
|
||||
|
||||
padButtons->addButton(ui->b_shift_l1, 9);
|
||||
padButtons->addButton(ui->b_shift_l2, 10);
|
||||
padButtons->addButton(ui->b_shift_l3, 11);
|
||||
|
||||
padButtons->addButton(ui->b_start, 12);
|
||||
padButtons->addButton(ui->b_select, 13);
|
||||
|
||||
padButtons->addButton(ui->b_shift_r1, 14);
|
||||
padButtons->addButton(ui->b_shift_r2, 15);
|
||||
padButtons->addButton(ui->b_shift_r3, 16);
|
||||
|
||||
padButtons->addButton(ui->b_square, 17);
|
||||
padButtons->addButton(ui->b_cross, 18);
|
||||
padButtons->addButton(ui->b_circle, 19);
|
||||
padButtons->addButton(ui->b_triangle, 20);
|
||||
|
||||
padButtons->addButton(ui->b_left_rstick, 21);
|
||||
padButtons->addButton(ui->b_down_rstick, 22);
|
||||
padButtons->addButton(ui->b_right_rstick, 23);
|
||||
padButtons->addButton(ui->b_up_rstick, 24);
|
||||
|
||||
padButtons->addButton(ui->b_reset, 25);
|
||||
padButtons->addButton(ui->b_ok, 26);
|
||||
padButtons->addButton(ui->b_cancel, 27);
|
||||
m_padButtons = new QButtonGroup(this);
|
||||
m_palette = ui->b_left->palette(); // save normal palette
|
||||
|
||||
connect(padButtons, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &pad_settings_dialog::OnPadButtonClicked);
|
||||
ui->chb_vibration_large->setChecked((bool)m_handler_cfg->enable_vibration_motor_large);
|
||||
ui->chb_vibration_small->setChecked((bool)m_handler_cfg->enable_vibration_motor_small);
|
||||
ui->chb_vibration_switch->setChecked((bool)m_handler_cfg->switch_vibration_motors);
|
||||
|
||||
// Adjust to the different pad handlers
|
||||
if (m_handler_cfg->cfg_type == "keyboard")
|
||||
{
|
||||
setWindowTitle(tr("Configure Keyboard"));
|
||||
m_handler_type = handler_type::handler_type_keyboard;
|
||||
}
|
||||
else if (m_handler_cfg->cfg_type == "xinput")
|
||||
{
|
||||
setWindowTitle(tr("Configure XInput"));
|
||||
m_handler_type = handler_type::handler_type_xinput;
|
||||
}
|
||||
else if (m_handler_cfg->cfg_type == "ds4")
|
||||
{
|
||||
setWindowTitle(tr("Configure DS4"));
|
||||
m_handler_type = handler_type::handler_type_ds4;
|
||||
}
|
||||
else if (m_handler_cfg->cfg_type == "mmjoystick")
|
||||
{
|
||||
setWindowTitle(tr("Configure MMJoystick"));
|
||||
m_handler_type = handler_type::handler_type_mmjoy;
|
||||
}
|
||||
else if (m_handler_cfg->cfg_type == "evdev")
|
||||
{
|
||||
setWindowTitle(tr("Configure evdev"));
|
||||
m_handler_type = handler_type::handler_type_evdev;
|
||||
}
|
||||
|
||||
// Enable Button Remapping
|
||||
if (m_handler->has_config())
|
||||
{
|
||||
// Use timer to get button input
|
||||
const auto& callback = [=](u16 val, std::string name, int preview_values[6])
|
||||
{
|
||||
if (m_handler->has_deadzones())
|
||||
{
|
||||
ui->preview_trigger_left->setValue(preview_values[0]);
|
||||
ui->preview_trigger_right->setValue(preview_values[1]);
|
||||
|
||||
if (lx != preview_values[2] || ly != preview_values[3])
|
||||
{
|
||||
lx = preview_values[2], ly = preview_values[3];
|
||||
RepaintPreviewLabel(ui->preview_stick_left, ui->slider_stick_left->value(), ui->slider_stick_left->sizeHint().width(), lx, ly);
|
||||
}
|
||||
if (rx != preview_values[4] || ry != preview_values[5])
|
||||
{
|
||||
rx = preview_values[4], ry = preview_values[5];
|
||||
RepaintPreviewLabel(ui->preview_stick_right, ui->slider_stick_right->value(), ui->slider_stick_right->sizeHint().width(), rx, ry);
|
||||
}
|
||||
}
|
||||
|
||||
if (val <= 0) return;
|
||||
|
||||
LOG_NOTICE(HLE, "GetNextButtonPress: %s button %s pressed with value %d", m_handler_cfg->cfg_type, name, val);
|
||||
if (m_button_id > button_ids::id_pad_begin && m_button_id < button_ids::id_pad_end)
|
||||
{
|
||||
m_cfg_entries[m_button_id].key = name;
|
||||
m_cfg_entries[m_button_id].text = qstr(name);
|
||||
ReactivateButtons();
|
||||
}
|
||||
};
|
||||
|
||||
connect(&m_timer_input, &QTimer::timeout, [=]()
|
||||
{
|
||||
m_handler->GetNextButtonPress(m_device_name, callback);
|
||||
});
|
||||
|
||||
m_timer_input.start(1);
|
||||
};
|
||||
|
||||
// Enable Vibration Checkboxes
|
||||
if (m_handler->has_rumble())
|
||||
{
|
||||
const s32 min_force = m_handler->vibration_min;
|
||||
const s32 max_force = m_handler->vibration_max;
|
||||
|
||||
ui->chb_vibration_large->setEnabled(true);
|
||||
ui->chb_vibration_small->setEnabled(true);
|
||||
ui->chb_vibration_switch->setEnabled(true);
|
||||
|
||||
connect(ui->chb_vibration_large, &QCheckBox::clicked, [=](bool checked)
|
||||
{
|
||||
if (!checked) return;
|
||||
|
||||
ui->chb_vibration_switch->isChecked() ? m_handler->TestVibration(m_device_name, min_force, max_force)
|
||||
: m_handler->TestVibration(m_device_name, max_force, min_force);
|
||||
|
||||
QTimer::singleShot(300, [=]()
|
||||
{
|
||||
m_handler->TestVibration(m_device_name, min_force, min_force);
|
||||
});
|
||||
});
|
||||
|
||||
connect(ui->chb_vibration_small, &QCheckBox::clicked, [=](bool checked)
|
||||
{
|
||||
if (!checked) return;
|
||||
|
||||
ui->chb_vibration_switch->isChecked() ? m_handler->TestVibration(m_device_name, max_force, min_force)
|
||||
: m_handler->TestVibration(m_device_name, min_force, max_force);
|
||||
|
||||
QTimer::singleShot(300, [=]()
|
||||
{
|
||||
m_handler->TestVibration(m_device_name, min_force, min_force);
|
||||
});
|
||||
});
|
||||
|
||||
connect(ui->chb_vibration_switch, &QCheckBox::clicked, [=](bool checked)
|
||||
{
|
||||
checked ? m_handler->TestVibration(m_device_name, min_force, max_force)
|
||||
: m_handler->TestVibration(m_device_name, max_force, min_force);
|
||||
|
||||
QTimer::singleShot(200, [=]()
|
||||
{
|
||||
checked ? m_handler->TestVibration(m_device_name, max_force, min_force)
|
||||
: m_handler->TestVibration(m_device_name, min_force, max_force);
|
||||
|
||||
QTimer::singleShot(200, [=]()
|
||||
{
|
||||
m_handler->TestVibration(m_device_name, min_force, min_force);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Enable Deadzone Settings
|
||||
if (m_handler->has_deadzones())
|
||||
{
|
||||
auto initSlider = [=](QSlider* slider, const s32& value, const s32& min, const s32& max)
|
||||
{
|
||||
slider->setEnabled(true);
|
||||
slider->setRange(min, max);
|
||||
slider->setValue(value);
|
||||
};
|
||||
|
||||
// Enable Trigger Thresholds
|
||||
initSlider(ui->slider_trigger_left, m_handler_cfg->ltriggerthreshold, 0, m_handler->trigger_max);
|
||||
initSlider(ui->slider_trigger_right, m_handler_cfg->rtriggerthreshold, 0, m_handler->trigger_max);
|
||||
ui->preview_trigger_left->setRange(0, m_handler->trigger_max);
|
||||
ui->preview_trigger_right->setRange(0, m_handler->trigger_max);
|
||||
|
||||
// Enable Stick Deadzones
|
||||
initSlider(ui->slider_stick_left, m_handler_cfg->lstickdeadzone, 0, m_handler->thumb_max);
|
||||
initSlider(ui->slider_stick_right, m_handler_cfg->rstickdeadzone, 0, m_handler->thumb_max);
|
||||
|
||||
RepaintPreviewLabel(ui->preview_stick_left, ui->slider_stick_left->value(), ui->slider_stick_left->sizeHint().width(), lx, ly);
|
||||
connect(ui->slider_stick_left, &QSlider::valueChanged, [&](int value)
|
||||
{
|
||||
RepaintPreviewLabel(ui->preview_stick_left, value, ui->slider_stick_left->sizeHint().width(), lx, ly);
|
||||
});
|
||||
|
||||
RepaintPreviewLabel(ui->preview_stick_right, ui->slider_stick_right->value(), ui->slider_stick_right->sizeHint().width(), rx, ry);
|
||||
connect(ui->slider_stick_right, &QSlider::valueChanged, [&](int value)
|
||||
{
|
||||
RepaintPreviewLabel(ui->preview_stick_right, value, ui->slider_stick_right->sizeHint().width(), rx, ry);
|
||||
});
|
||||
}
|
||||
|
||||
auto insertButton = [this](int id, QPushButton* button, cfg::string* cfg_name)
|
||||
{
|
||||
QString name = qstr(*cfg_name);
|
||||
m_cfg_entries.insert(std::make_pair(id, pad_button{ cfg_name, *cfg_name, name }));
|
||||
m_padButtons->addButton(button, id);
|
||||
button->setText(name);
|
||||
};
|
||||
|
||||
insertButton(button_ids::id_pad_lstick_left, ui->b_lstick_left, &m_handler_cfg->ls_left);
|
||||
insertButton(button_ids::id_pad_lstick_down, ui->b_lstick_down, &m_handler_cfg->ls_down);
|
||||
insertButton(button_ids::id_pad_lstick_right, ui->b_lstick_right, &m_handler_cfg->ls_right);
|
||||
insertButton(button_ids::id_pad_lstick_up, ui->b_lstick_up, &m_handler_cfg->ls_up);
|
||||
|
||||
insertButton(button_ids::id_pad_left, ui->b_left, &m_handler_cfg->left);
|
||||
insertButton(button_ids::id_pad_down, ui->b_down, &m_handler_cfg->down);
|
||||
insertButton(button_ids::id_pad_right, ui->b_right, &m_handler_cfg->right);
|
||||
insertButton(button_ids::id_pad_up, ui->b_up, &m_handler_cfg->up);
|
||||
|
||||
insertButton(button_ids::id_pad_l1, ui->b_shift_l1, &m_handler_cfg->l1);
|
||||
insertButton(button_ids::id_pad_l2, ui->b_shift_l2, &m_handler_cfg->l2);
|
||||
insertButton(button_ids::id_pad_l3, ui->b_shift_l3, &m_handler_cfg->l3);
|
||||
|
||||
insertButton(button_ids::id_pad_start, ui->b_start, &m_handler_cfg->start);
|
||||
insertButton(button_ids::id_pad_select, ui->b_select, &m_handler_cfg->select);
|
||||
insertButton(button_ids::id_pad_ps, ui->b_ps, &m_handler_cfg->ps);
|
||||
|
||||
insertButton(button_ids::id_pad_r1, ui->b_shift_r1, &m_handler_cfg->r1);
|
||||
insertButton(button_ids::id_pad_r2, ui->b_shift_r2, &m_handler_cfg->r2);
|
||||
insertButton(button_ids::id_pad_r3, ui->b_shift_r3, &m_handler_cfg->r3);
|
||||
|
||||
insertButton(button_ids::id_pad_square, ui->b_square, &m_handler_cfg->square);
|
||||
insertButton(button_ids::id_pad_cross, ui->b_cross, &m_handler_cfg->cross);
|
||||
insertButton(button_ids::id_pad_circle, ui->b_circle, &m_handler_cfg->circle);
|
||||
insertButton(button_ids::id_pad_triangle, ui->b_triangle, &m_handler_cfg->triangle);
|
||||
|
||||
insertButton(button_ids::id_pad_rstick_left, ui->b_rstick_left, &m_handler_cfg->rs_left);
|
||||
insertButton(button_ids::id_pad_rstick_down, ui->b_rstick_down, &m_handler_cfg->rs_down);
|
||||
insertButton(button_ids::id_pad_rstick_right, ui->b_rstick_right, &m_handler_cfg->rs_right);
|
||||
insertButton(button_ids::id_pad_rstick_up, ui->b_rstick_up, &m_handler_cfg->rs_up);
|
||||
|
||||
m_padButtons->addButton(ui->b_reset, button_ids::id_reset_parameters);
|
||||
m_padButtons->addButton(ui->b_ok, button_ids::id_ok);
|
||||
m_padButtons->addButton(ui->b_cancel, button_ids::id_cancel);
|
||||
|
||||
connect(m_padButtons, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &pad_settings_dialog::OnPadButtonClicked);
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, [&]()
|
||||
{
|
||||
if (--m_seconds <= 0)
|
||||
{
|
||||
ReactivateButtons();
|
||||
return;
|
||||
}
|
||||
m_padButtons->button(m_button_id)->setText(tr("[ Waiting %1 ]").arg(m_seconds));
|
||||
});
|
||||
|
||||
g_kbpad_config.load();
|
||||
UpdateLabel();
|
||||
|
||||
gui_settings settings(this);
|
||||
@ -81,224 +260,154 @@ pad_settings_dialog::~pad_settings_dialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void pad_settings_dialog::ReactivateButtons()
|
||||
{
|
||||
m_timer.stop();
|
||||
m_seconds = MAX_SECONDS;
|
||||
|
||||
if (m_button_id == button_ids::id_pad_begin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_padButtons->button(m_button_id))
|
||||
{
|
||||
m_padButtons->button(m_button_id)->setPalette(m_palette);
|
||||
}
|
||||
|
||||
m_button_id = button_ids::id_pad_begin;
|
||||
UpdateLabel();
|
||||
SwitchButtons(true);
|
||||
}
|
||||
|
||||
void pad_settings_dialog::RepaintPreviewLabel(QLabel* l, int dz, int w, int x, int y)
|
||||
{
|
||||
int max = m_handler->thumb_max;
|
||||
int origin = w * 0.1;
|
||||
int width = w * 0.8;
|
||||
int dz_width = width * dz / max;
|
||||
int dz_origin = (w - dz_width) / 2;
|
||||
|
||||
x = (w + (x * width / max)) / 2;
|
||||
y = (w + (y * -1 * width / max)) / 2;
|
||||
|
||||
QPixmap pm(w, w);
|
||||
pm.fill(Qt::transparent);
|
||||
QPainter p(&pm);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
QPen pen(Qt::black, 2);
|
||||
p.setPen(pen);
|
||||
QBrush brush(Qt::white);
|
||||
p.setBrush(brush);
|
||||
p.drawEllipse(origin, origin, width, width);
|
||||
pen = QPen(Qt::red, 2);
|
||||
p.setPen(pen);
|
||||
p.drawEllipse(dz_origin, dz_origin, dz_width, dz_width);
|
||||
pen = QPen(Qt::blue, 2);
|
||||
p.setPen(pen);
|
||||
p.drawEllipse(x, y, 1, 1);
|
||||
l->setPixmap(pm);
|
||||
}
|
||||
|
||||
void pad_settings_dialog::keyPressEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
m_key_pressed = true;
|
||||
|
||||
cfg::int32* entry = nullptr;
|
||||
|
||||
switch (m_button_id)
|
||||
if (m_handler_type != handler_type::handler_type_keyboard)
|
||||
{
|
||||
case id_pad_lstick_left: entry = &g_kbpad_config.left_stick_left; break;
|
||||
case id_pad_lstick_down: entry = &g_kbpad_config.left_stick_down; break;
|
||||
case id_pad_lstick_right: entry = &g_kbpad_config.left_stick_right; break;
|
||||
case id_pad_lstick_up: entry = &g_kbpad_config.left_stick_up; break;
|
||||
|
||||
case id_pad_left: entry = &g_kbpad_config.left; break;
|
||||
case id_pad_down: entry = &g_kbpad_config.down; break;
|
||||
case id_pad_right: entry = &g_kbpad_config.right; break;
|
||||
case id_pad_up: entry = &g_kbpad_config.up; break;
|
||||
|
||||
case id_pad_l1: entry = &g_kbpad_config.l1; break;
|
||||
case id_pad_l2: entry = &g_kbpad_config.l2; break;
|
||||
case id_pad_l3: entry = &g_kbpad_config.l3; break;
|
||||
|
||||
case id_pad_start: entry = &g_kbpad_config.start; break;
|
||||
case id_pad_select: entry = &g_kbpad_config.select; break;
|
||||
|
||||
case id_pad_r1: entry = &g_kbpad_config.r1; break;
|
||||
case id_pad_r2: entry = &g_kbpad_config.r2; break;
|
||||
case id_pad_r3: entry = &g_kbpad_config.r3; break;
|
||||
|
||||
case id_pad_square: entry = &g_kbpad_config.square; break;
|
||||
case id_pad_cross: entry = &g_kbpad_config.cross; break;
|
||||
case id_pad_circle: entry = &g_kbpad_config.circle; break;
|
||||
case id_pad_triangle: entry = &g_kbpad_config.triangle; break;
|
||||
case id_pad_rstick_left: entry = &g_kbpad_config.right_stick_left; break;
|
||||
case id_pad_rstick_down: entry = &g_kbpad_config.right_stick_down; break;
|
||||
case id_pad_rstick_right: entry = &g_kbpad_config.right_stick_right; break;
|
||||
case id_pad_rstick_up: entry = &g_kbpad_config.right_stick_up; break;
|
||||
|
||||
case 0: break;
|
||||
default: LOG_ERROR(HLE, "Unknown button ID: %d", m_button_id); break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry)
|
||||
if (m_button_id == button_ids::id_pad_begin)
|
||||
{
|
||||
// TODO: do not modify config
|
||||
entry->from_string(std::to_string(keyEvent->key()));
|
||||
return;
|
||||
}
|
||||
|
||||
SwitchButtons(true); // enable all buttons
|
||||
m_button_id = 0; // reset current button id
|
||||
m_key_pressed = false;
|
||||
UpdateLabel();
|
||||
}
|
||||
|
||||
void pad_settings_dialog::UpdateLabel()
|
||||
{
|
||||
// Get button labels from .ini
|
||||
ui->b_up_lstick->setText(GetKeyName(g_kbpad_config.left_stick_up));
|
||||
ui->b_down_lstick->setText(GetKeyName(g_kbpad_config.left_stick_down));
|
||||
ui->b_left_lstick->setText(GetKeyName(g_kbpad_config.left_stick_left));
|
||||
ui->b_right_lstick->setText(GetKeyName(g_kbpad_config.left_stick_right));
|
||||
|
||||
ui->b_up->setText(GetKeyName(g_kbpad_config.up));
|
||||
ui->b_down->setText(GetKeyName(g_kbpad_config.down));
|
||||
ui->b_left->setText(GetKeyName(g_kbpad_config.left));
|
||||
ui->b_right->setText(GetKeyName(g_kbpad_config.right));
|
||||
|
||||
ui->b_shift_l1->setText(GetKeyName(g_kbpad_config.l1));
|
||||
ui->b_shift_l2->setText(GetKeyName(g_kbpad_config.l2));
|
||||
ui->b_shift_l3->setText(GetKeyName(g_kbpad_config.l3));
|
||||
|
||||
ui->b_start->setText(GetKeyName(g_kbpad_config.start));
|
||||
ui->b_select->setText(GetKeyName(g_kbpad_config.select));
|
||||
|
||||
ui->b_shift_r1->setText(GetKeyName(g_kbpad_config.r1));
|
||||
ui->b_shift_r2->setText(GetKeyName(g_kbpad_config.r2));
|
||||
ui->b_shift_r3->setText(GetKeyName(g_kbpad_config.r3));
|
||||
|
||||
ui->b_square->setText(GetKeyName(g_kbpad_config.square));
|
||||
ui->b_cross->setText(GetKeyName(g_kbpad_config.cross));
|
||||
ui->b_circle->setText(GetKeyName(g_kbpad_config.circle));
|
||||
ui->b_triangle->setText(GetKeyName(g_kbpad_config.triangle));
|
||||
|
||||
ui->b_up_rstick->setText(GetKeyName(g_kbpad_config.right_stick_up));
|
||||
ui->b_down_rstick->setText(GetKeyName(g_kbpad_config.right_stick_down));
|
||||
ui->b_left_rstick->setText(GetKeyName(g_kbpad_config.right_stick_left));
|
||||
ui->b_right_rstick->setText(GetKeyName(g_kbpad_config.right_stick_right));
|
||||
}
|
||||
|
||||
void pad_settings_dialog::UpdateTimerLabel(const u32 id)
|
||||
{
|
||||
// Lambda used to update label. The 47 is magical.
|
||||
auto UpdateLabel = [=](QPushButton* target)
|
||||
if (m_button_id <= button_ids::id_pad_begin || m_button_id >= button_ids::id_pad_end)
|
||||
{
|
||||
target->setText(QString::number(m_seconds + 47));
|
||||
};
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case id_pad_lstick_left: UpdateLabel(ui->b_left_lstick); break;
|
||||
case id_pad_lstick_down: UpdateLabel(ui->b_down_lstick); break;
|
||||
case id_pad_lstick_right: UpdateLabel(ui->b_right_lstick); break;
|
||||
case id_pad_lstick_up: UpdateLabel(ui->b_up_lstick); break;
|
||||
|
||||
case id_pad_left: UpdateLabel(ui->b_left); break;
|
||||
case id_pad_down: UpdateLabel(ui->b_down); break;
|
||||
case id_pad_right: UpdateLabel(ui->b_right); break;
|
||||
case id_pad_up: UpdateLabel(ui->b_up); break;
|
||||
|
||||
case id_pad_l1: UpdateLabel(ui->b_shift_l1); break;
|
||||
case id_pad_l2: UpdateLabel(ui->b_shift_l2); break;
|
||||
case id_pad_l3: UpdateLabel(ui->b_shift_l3); break;
|
||||
|
||||
case id_pad_start: UpdateLabel(ui->b_start); break;
|
||||
case id_pad_select: UpdateLabel(ui->b_select); break;
|
||||
|
||||
case id_pad_r1: UpdateLabel(ui->b_shift_r1); break;
|
||||
case id_pad_r2: UpdateLabel(ui->b_shift_r2); break;
|
||||
case id_pad_r3: UpdateLabel(ui->b_shift_r3); break;
|
||||
|
||||
case id_pad_square: UpdateLabel(ui->b_square); break;
|
||||
case id_pad_cross: UpdateLabel(ui->b_cross); break;
|
||||
case id_pad_circle: UpdateLabel(ui->b_circle); break;
|
||||
case id_pad_triangle: UpdateLabel(ui->b_triangle); break;
|
||||
|
||||
case id_pad_rstick_left: UpdateLabel(ui->b_left_rstick); break;
|
||||
case id_pad_rstick_down: UpdateLabel(ui->b_down_rstick); break;
|
||||
case id_pad_rstick_right: UpdateLabel(ui->b_right_rstick); break;
|
||||
case id_pad_rstick_up: UpdateLabel(ui->b_up_rstick); break;
|
||||
|
||||
default: LOG_ERROR(HLE, "Unknown button ID: %d", id); break;
|
||||
LOG_NOTICE(HLE, "Pad Settings: Handler Type: %d, Unknown button ID: %d", static_cast<int>(m_handler_type), m_button_id);
|
||||
}
|
||||
}
|
||||
|
||||
void pad_settings_dialog::SwitchButtons(const bool IsEnabled)
|
||||
{
|
||||
ui->b_up_lstick->setEnabled(IsEnabled);
|
||||
ui->b_down_lstick->setEnabled(IsEnabled);
|
||||
ui->b_left_lstick->setEnabled(IsEnabled);
|
||||
ui->b_right_lstick->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_up->setEnabled(IsEnabled);
|
||||
ui->b_down->setEnabled(IsEnabled);
|
||||
ui->b_left->setEnabled(IsEnabled);
|
||||
ui->b_right->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_shift_l1->setEnabled(IsEnabled);
|
||||
ui->b_shift_l2->setEnabled(IsEnabled);
|
||||
ui->b_shift_l3->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_start->setEnabled(IsEnabled);
|
||||
ui->b_select->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_shift_r1->setEnabled(IsEnabled);
|
||||
ui->b_shift_r2->setEnabled(IsEnabled);
|
||||
ui->b_shift_r3->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_square->setEnabled(IsEnabled);
|
||||
ui->b_cross->setEnabled(IsEnabled);
|
||||
ui->b_circle->setEnabled(IsEnabled);
|
||||
ui->b_triangle->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_up_rstick->setEnabled(IsEnabled);
|
||||
ui->b_down_rstick->setEnabled(IsEnabled);
|
||||
ui->b_left_rstick->setEnabled(IsEnabled);
|
||||
ui->b_right_rstick->setEnabled(IsEnabled);
|
||||
|
||||
ui->b_ok->setEnabled(IsEnabled);
|
||||
ui->b_cancel->setEnabled(IsEnabled);
|
||||
ui->b_reset->setEnabled(IsEnabled);
|
||||
}
|
||||
|
||||
void pad_settings_dialog::RunTimer(const u32 seconds, const u32 id)
|
||||
{
|
||||
m_seconds = seconds;
|
||||
clock_t t1, t2;
|
||||
t1 = t2 = clock() / CLOCKS_PER_SEC;
|
||||
while (m_seconds)
|
||||
else
|
||||
{
|
||||
if (t1 / CLOCKS_PER_SEC + 1 <= (t2 = clock()) / CLOCKS_PER_SEC)
|
||||
m_cfg_entries[m_button_id].key = ((keyboard_pad_handler*)m_handler.get())->GetKeyName(keyEvent);
|
||||
m_cfg_entries[m_button_id].text = qstr(m_cfg_entries[m_button_id].key);
|
||||
}
|
||||
|
||||
ReactivateButtons();
|
||||
}
|
||||
|
||||
void pad_settings_dialog::UpdateLabel(bool is_reset)
|
||||
{
|
||||
if (is_reset)
|
||||
{
|
||||
ui->chb_vibration_large->setChecked((bool)m_handler_cfg->enable_vibration_motor_large);
|
||||
ui->chb_vibration_small->setChecked((bool)m_handler_cfg->enable_vibration_motor_small);
|
||||
ui->chb_vibration_switch->setChecked((bool)m_handler_cfg->switch_vibration_motors);
|
||||
|
||||
ui->slider_trigger_left->setValue(m_handler_cfg->ltriggerthreshold);
|
||||
ui->slider_trigger_right->setValue(m_handler_cfg->rtriggerthreshold);
|
||||
ui->slider_stick_left->setValue(m_handler_cfg->lstickdeadzone);
|
||||
ui->slider_stick_right->setValue(m_handler_cfg->rstickdeadzone);
|
||||
}
|
||||
|
||||
for (auto& entry : m_cfg_entries)
|
||||
{
|
||||
if (is_reset)
|
||||
{
|
||||
UpdateTimerLabel(id);
|
||||
m_seconds--;
|
||||
t1 = t2;
|
||||
entry.second.key = *entry.second.cfg_name;
|
||||
entry.second.text = qstr(entry.second.key);
|
||||
}
|
||||
|
||||
if (m_key_pressed)
|
||||
{
|
||||
m_seconds = 0;
|
||||
break;
|
||||
}
|
||||
m_padButtons->button(entry.first)->setText(entry.second.text);
|
||||
}
|
||||
}
|
||||
|
||||
const QString pad_settings_dialog::GetKeyName(const u32 keyCode)
|
||||
void pad_settings_dialog::SwitchButtons(bool is_enabled)
|
||||
{
|
||||
//TODO what about numpad?
|
||||
return QKeySequence(keyCode).toString();
|
||||
for (int i = button_ids::id_pad_begin + 1; i < button_ids::id_pad_end; i++)
|
||||
{
|
||||
m_padButtons->button(i)->setEnabled(is_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void pad_settings_dialog::SaveConfig()
|
||||
{
|
||||
for (const auto& entry : m_cfg_entries)
|
||||
{
|
||||
entry.second.cfg_name->from_string(entry.second.key);
|
||||
}
|
||||
m_handler_cfg->enable_vibration_motor_large.set(ui->chb_vibration_large->isChecked());
|
||||
m_handler_cfg->enable_vibration_motor_small.set(ui->chb_vibration_small->isChecked());
|
||||
m_handler_cfg->switch_vibration_motors.set(ui->chb_vibration_switch->isChecked());
|
||||
m_handler_cfg->ltriggerthreshold.set(ui->slider_trigger_left->value());
|
||||
m_handler_cfg->rtriggerthreshold.set(ui->slider_trigger_right->value());
|
||||
m_handler_cfg->lstickdeadzone.set(ui->slider_stick_left->value());
|
||||
m_handler_cfg->rstickdeadzone.set(ui->slider_stick_right->value());
|
||||
m_handler_cfg->save();
|
||||
}
|
||||
|
||||
void pad_settings_dialog::OnPadButtonClicked(int id)
|
||||
{
|
||||
if (id != id_reset_parameters && id != id_ok)
|
||||
switch (id)
|
||||
{
|
||||
m_button_id = id;
|
||||
SwitchButtons(false); // disable all buttons, needed for using Space, Enter and other specific buttons
|
||||
//RunTimer(3, event.GetId()); // TODO: Currently, timer disabled. Use by later, have some strange problems
|
||||
//SwitchButtons(true); // needed, if timer enabled
|
||||
UpdateLabel();
|
||||
case button_ids::id_pad_begin:
|
||||
case button_ids::id_pad_end:
|
||||
case button_ids::id_cancel:
|
||||
return;
|
||||
case button_ids::id_reset_parameters:
|
||||
ReactivateButtons();
|
||||
m_handler_cfg->from_default();
|
||||
UpdateLabel(true);
|
||||
return;
|
||||
case button_ids::id_ok:
|
||||
SaveConfig();
|
||||
QDialog::accept();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case id_reset_parameters: g_kbpad_config.from_default(); UpdateLabel(); break;
|
||||
case id_ok: g_kbpad_config.save(); QDialog::accept(); break;
|
||||
case id_cancel: break;
|
||||
}
|
||||
}
|
||||
m_button_id = id;
|
||||
m_padButtons->button(m_button_id)->setText(tr("[ Waiting %1 ]").arg(MAX_SECONDS));
|
||||
m_padButtons->button(m_button_id)->setPalette(QPalette(Qt::blue));
|
||||
SwitchButtons(false); // disable all buttons, needed for using Space, Enter and other specific buttons
|
||||
m_timer.start(1000);
|
||||
}
|
||||
|
@ -3,6 +3,9 @@
|
||||
#include <QDialog>
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QTimer>
|
||||
#include <QButtonGroup>
|
||||
|
||||
#include "keyboard_pad_handler.h"
|
||||
#include "Utilities/types.h"
|
||||
#include "Utilities/Config.h"
|
||||
@ -11,43 +14,10 @@
|
||||
#include "Emu/System.h"
|
||||
#include "gui_settings.h"
|
||||
|
||||
enum button_ids
|
||||
{
|
||||
id_pad_lstick_left = 0x1,
|
||||
id_pad_lstick_down,
|
||||
id_pad_lstick_right,
|
||||
id_pad_lstick_up,
|
||||
|
||||
id_pad_left,
|
||||
id_pad_down,
|
||||
id_pad_right,
|
||||
id_pad_up,
|
||||
|
||||
id_pad_l1,
|
||||
id_pad_l2,
|
||||
id_pad_l3,
|
||||
|
||||
id_pad_start,
|
||||
id_pad_select,
|
||||
|
||||
id_pad_r1,
|
||||
id_pad_r2,
|
||||
id_pad_r3,
|
||||
|
||||
id_pad_square,
|
||||
id_pad_cross,
|
||||
id_pad_circle,
|
||||
id_pad_triangle,
|
||||
|
||||
id_pad_rstick_left,
|
||||
id_pad_rstick_down,
|
||||
id_pad_rstick_right,
|
||||
id_pad_rstick_up,
|
||||
|
||||
id_reset_parameters,
|
||||
id_ok,
|
||||
id_cancel
|
||||
};
|
||||
#ifdef _WIN32
|
||||
#include "xinput_pad_handler.h"
|
||||
#endif
|
||||
#include "ds4_pad_handler.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@ -58,22 +28,118 @@ class pad_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum button_ids
|
||||
{
|
||||
id_pad_begin, // begin
|
||||
|
||||
id_pad_lstick_left,
|
||||
id_pad_lstick_down,
|
||||
id_pad_lstick_right,
|
||||
id_pad_lstick_up,
|
||||
|
||||
id_pad_left,
|
||||
id_pad_down,
|
||||
id_pad_right,
|
||||
id_pad_up,
|
||||
|
||||
id_pad_l1,
|
||||
id_pad_l2,
|
||||
id_pad_l3,
|
||||
|
||||
id_pad_start,
|
||||
id_pad_select,
|
||||
id_pad_ps,
|
||||
|
||||
id_pad_r1,
|
||||
id_pad_r2,
|
||||
id_pad_r3,
|
||||
|
||||
id_pad_square,
|
||||
id_pad_cross,
|
||||
id_pad_circle,
|
||||
id_pad_triangle,
|
||||
|
||||
id_pad_rstick_left,
|
||||
id_pad_rstick_down,
|
||||
id_pad_rstick_right,
|
||||
id_pad_rstick_up,
|
||||
|
||||
id_pad_end, // end
|
||||
|
||||
id_reset_parameters,
|
||||
id_ok,
|
||||
id_cancel
|
||||
};
|
||||
|
||||
enum handler_type
|
||||
{
|
||||
handler_type_keyboard,
|
||||
handler_type_xinput,
|
||||
handler_type_ds4,
|
||||
handler_type_mmjoy,
|
||||
handler_type_evdev
|
||||
};
|
||||
|
||||
struct pad_button
|
||||
{
|
||||
cfg::string* cfg_name;
|
||||
std::string key;
|
||||
QString text;
|
||||
};
|
||||
|
||||
private Q_SLOTS:
|
||||
void OnPadButtonClicked(int id);
|
||||
|
||||
private:
|
||||
u32 m_seconds;
|
||||
u32 m_button_id;
|
||||
bool m_key_pressed;
|
||||
Ui::pad_settings_dialog *ui;
|
||||
|
||||
// Button Mapping
|
||||
QButtonGroup* m_padButtons;
|
||||
u32 m_button_id = id_pad_begin;
|
||||
std::map<int /*id*/, pad_button /*info*/> m_cfg_entries;
|
||||
|
||||
// Real time stick values
|
||||
int lx = 0;
|
||||
int ly = 0;
|
||||
int rx = 0;
|
||||
int ry = 0;
|
||||
|
||||
// Backup for standard button palette
|
||||
QPalette m_palette;
|
||||
|
||||
// Pad Handlers
|
||||
std::shared_ptr<PadHandlerBase> m_handler;
|
||||
handler_type m_handler_type;
|
||||
pad_config* m_handler_cfg;
|
||||
std::string m_device_name;
|
||||
|
||||
// Remap Timer
|
||||
const int MAX_SECONDS = 5;
|
||||
int m_seconds = MAX_SECONDS;
|
||||
QTimer m_timer;
|
||||
|
||||
// Input timer. Its Callback handles the input
|
||||
QTimer m_timer_input;
|
||||
|
||||
/** Resets the view to default. Resets the Remap Timer */
|
||||
void ReactivateButtons();
|
||||
|
||||
/** Repaints a stick deadzone preview label */
|
||||
void RepaintPreviewLabel(QLabel* l, int dz, int w, int x, int y);
|
||||
|
||||
public:
|
||||
explicit pad_settings_dialog(QWidget *parent = nullptr);
|
||||
explicit pad_settings_dialog(const std::string& device, std::shared_ptr<PadHandlerBase> handler, QWidget *parent = nullptr);
|
||||
~pad_settings_dialog();
|
||||
|
||||
/** Handle keyboard handler input */
|
||||
void keyPressEvent(QKeyEvent *keyEvent) override;
|
||||
void UpdateLabel();
|
||||
void UpdateTimerLabel(const u32 id);
|
||||
void SwitchButtons(const bool IsEnabled);
|
||||
void RunTimer(const u32 seconds, const u32 id);
|
||||
const QString GetKeyName(const u32 keyCode);
|
||||
|
||||
/** Update all the Button Labels with current button mapping */
|
||||
void UpdateLabel(bool is_reset = false);
|
||||
|
||||
/** Enable/Disable Buttons while trying to remap an other */
|
||||
void SwitchButtons(bool is_enabled);
|
||||
|
||||
/** Save the Pad Configuration to the current Pad Handler Config File */
|
||||
void SaveConfig();
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>892</width>
|
||||
<height>554</height>
|
||||
<height>568</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -23,64 +23,6 @@
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Controller to Configure</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cb_controller"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Profiles</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_profile">
|
||||
<property name="text">
|
||||
<string>Manage Profiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_15">
|
||||
<property name="title">
|
||||
@ -389,7 +331,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_up_lstick">
|
||||
<widget class="QPushButton" name="b_lstick_up">
|
||||
<property name="text">
|
||||
<string>Up</string>
|
||||
</property>
|
||||
@ -438,7 +380,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_left_lstick">
|
||||
<widget class="QPushButton" name="b_lstick_left">
|
||||
<property name="text">
|
||||
<string>Left</string>
|
||||
</property>
|
||||
@ -469,7 +411,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_right_lstick">
|
||||
<widget class="QPushButton" name="b_lstick_right">
|
||||
<property name="text">
|
||||
<string>Right</string>
|
||||
</property>
|
||||
@ -530,7 +472,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_down_lstick">
|
||||
<widget class="QPushButton" name="b_lstick_down">
|
||||
<property name="text">
|
||||
<string>Down</string>
|
||||
</property>
|
||||
@ -561,6 +503,138 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_triggers">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Trigger Thresholds</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="gb_trigger_left">
|
||||
<item>
|
||||
<widget class="QSlider" name="slider_trigger_left">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="preview_trigger_left">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="gb_trigger_right">
|
||||
<item>
|
||||
<widget class="QSlider" name="slider_trigger_right">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="preview_trigger_right">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_vibration">
|
||||
<property name="title">
|
||||
<string>Enable Vibration</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chb_vibration_large">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Large</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chb_vibration_small">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Small</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chb_vibration_switch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Switch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
@ -818,11 +892,8 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_ps">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Null</string>
|
||||
<string>Backspace</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
@ -1107,86 +1178,6 @@
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Controller Status</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cb_status"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Features</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chb_sixaxis">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sixaxis</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="chb_vibration">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Vibration</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_20">
|
||||
<property name="title">
|
||||
@ -1489,7 +1480,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_up_rstick">
|
||||
<widget class="QPushButton" name="b_rstick_up">
|
||||
<property name="text">
|
||||
<string>PgUp</string>
|
||||
</property>
|
||||
@ -1538,7 +1529,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_left_rstick">
|
||||
<widget class="QPushButton" name="b_rstick_left">
|
||||
<property name="text">
|
||||
<string>Home</string>
|
||||
</property>
|
||||
@ -1569,7 +1560,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_right_rstick">
|
||||
<widget class="QPushButton" name="b_rstick_right">
|
||||
<property name="text">
|
||||
<string>End</string>
|
||||
</property>
|
||||
@ -1630,7 +1621,7 @@
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="b_down_rstick">
|
||||
<widget class="QPushButton" name="b_rstick_down">
|
||||
<property name="text">
|
||||
<string>PgDown</string>
|
||||
</property>
|
||||
@ -1661,6 +1652,69 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_sticks">
|
||||
<property name="title">
|
||||
<string>Analog Deadzones</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_22">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="gb_stick_left">
|
||||
<item>
|
||||
<widget class="QSlider" name="slider_stick_left">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="preview_stick_left">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="gb_stick_right">
|
||||
<item>
|
||||
<widget class="QSlider" name="slider_stick_right">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="preview_stick_right">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "register_editor_dialog.h"
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
inline std::string sstr(const QVariant& _in) { return sstr(_in.toString()); }
|
||||
|
||||
register_editor_dialog::register_editor_dialog(QWidget *parent, u32 _pc, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm)
|
||||
|
@ -21,7 +21,7 @@ namespace
|
||||
{
|
||||
// Helper converters
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
/**
|
||||
* This certainly isn't ideal for this code, as it essentially copies cellSaveData. But, I have no other choice without adding public methods to cellSaveData.
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
inline std::string sstr(const QVariant& _in) { return sstr(_in.toString()); }
|
||||
|
||||
settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std::shared_ptr<emu_settings> emuSettings, const int& tabIndex, QWidget *parent, const GameInfo* game)
|
||||
|
@ -22,7 +22,7 @@ class settings_dialog : public QDialog
|
||||
public:
|
||||
explicit settings_dialog(std::shared_ptr<gui_settings> guiSettings, std::shared_ptr<emu_settings> emuSettings, const int& tabIndex = 0, QWidget *parent = 0, const GameInfo *game = nullptr);
|
||||
~settings_dialog();
|
||||
int exec();
|
||||
int exec() override;
|
||||
Q_SIGNALS:
|
||||
void GuiSettingsSyncRequest(bool configure_all);
|
||||
void GuiStylesheetRequest(const QString& path);
|
||||
@ -51,5 +51,5 @@ private:
|
||||
QHash<QObject*, QString> m_descriptions;
|
||||
void SubscribeDescription(QLabel* description);
|
||||
void SubscribeTooltip(QObject* object, const QString& tooltip);
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
vfs_dialog::vfs_dialog(std::shared_ptr<gui_settings> guiSettings, std::shared_ptr<emu_settings> emuSettings, QWidget* parent)
|
||||
: QDialog(parent), m_gui_settings(guiSettings), m_emu_settings(emuSettings)
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toUtf8().toStdString(); }
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
|
||||
vfs_dialog_tab::vfs_dialog_tab(const vfs_settings_info& settingsInfo, std::shared_ptr<gui_settings> guiSettings, std::shared_ptr<emu_settings> emuSettings, QWidget* parent)
|
||||
: QWidget(parent), m_info(settingsInfo), m_gui_settings(guiSettings), m_emu_settings(emuSettings)
|
||||
|
@ -1,41 +1,68 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "xinput_pad_handler.h"
|
||||
#include "rpcs3qt/pad_settings_dialog.h"
|
||||
|
||||
xinput_config xinput_cfg;
|
||||
|
||||
namespace {
|
||||
const DWORD THREAD_TIMEOUT = 1000;
|
||||
const DWORD THREAD_SLEEP = 10;
|
||||
const DWORD THREAD_SLEEP_INACTIVE = 100;
|
||||
const DWORD MAX_GAMEPADS = 4;
|
||||
const DWORD XINPUT_GAMEPAD_GUIDE = 0x0400;
|
||||
const DWORD XINPUT_GAMEPAD_BUTTONS = 16;
|
||||
const LPCWSTR LIBRARY_FILENAMES[] = {
|
||||
L"xinput1_4.dll",
|
||||
L"xinput1_3.dll",
|
||||
L"xinput1_2.dll",
|
||||
L"xinput9_1_0.dll"
|
||||
};
|
||||
|
||||
inline u16 Clamp0To255(f32 input)
|
||||
{
|
||||
if (input > 255.f)
|
||||
return 255;
|
||||
else if (input < 0.f)
|
||||
return 0;
|
||||
else return static_cast<u16>(input);
|
||||
}
|
||||
|
||||
inline u16 ConvertAxis(float value)
|
||||
{
|
||||
return static_cast<u16>((value + 1.0)*(255.0 / 2.0));
|
||||
}
|
||||
}
|
||||
|
||||
xinput_pad_handler::xinput_pad_handler() : library(nullptr), xinputGetState(nullptr), xinputEnable(nullptr), xinputSetState(nullptr), is_init(false)
|
||||
xinput_pad_handler::xinput_pad_handler() :
|
||||
library(nullptr), xinputGetState(nullptr), xinputEnable(nullptr),
|
||||
xinputSetState(nullptr), xinputGetBatteryInformation(nullptr), is_init(false)
|
||||
{
|
||||
// Define border values
|
||||
thumb_min = -32768;
|
||||
thumb_max = 32767;
|
||||
trigger_min = 0;
|
||||
trigger_max = 255;
|
||||
vibration_min = 0;
|
||||
vibration_max = 65535;
|
||||
|
||||
// Set this handler's type and save location
|
||||
m_pad_config.cfg_type = "xinput";
|
||||
m_pad_config.cfg_name = fs::get_config_dir() + "/config_xinput.yml";
|
||||
|
||||
// Set default button mapping
|
||||
m_pad_config.ls_left.def = button_list.at(XInputKeyCodes::LSXNeg);
|
||||
m_pad_config.ls_down.def = button_list.at(XInputKeyCodes::LSYNeg);
|
||||
m_pad_config.ls_right.def = button_list.at(XInputKeyCodes::LSXPos);
|
||||
m_pad_config.ls_up.def = button_list.at(XInputKeyCodes::LSYPos);
|
||||
m_pad_config.rs_left.def = button_list.at(XInputKeyCodes::RSXNeg);
|
||||
m_pad_config.rs_down.def = button_list.at(XInputKeyCodes::RSYNeg);
|
||||
m_pad_config.rs_right.def = button_list.at(XInputKeyCodes::RSXPos);
|
||||
m_pad_config.rs_up.def = button_list.at(XInputKeyCodes::RSYPos);
|
||||
m_pad_config.start.def = button_list.at(XInputKeyCodes::Start);
|
||||
m_pad_config.select.def = button_list.at(XInputKeyCodes::Back);
|
||||
m_pad_config.ps.def = button_list.at(XInputKeyCodes::Guide);
|
||||
m_pad_config.square.def = button_list.at(XInputKeyCodes::X);
|
||||
m_pad_config.cross.def = button_list.at(XInputKeyCodes::A);
|
||||
m_pad_config.circle.def = button_list.at(XInputKeyCodes::B);
|
||||
m_pad_config.triangle.def = button_list.at(XInputKeyCodes::Y);
|
||||
m_pad_config.left.def = button_list.at(XInputKeyCodes::Left);
|
||||
m_pad_config.down.def = button_list.at(XInputKeyCodes::Down);
|
||||
m_pad_config.right.def = button_list.at(XInputKeyCodes::Right);
|
||||
m_pad_config.up.def = button_list.at(XInputKeyCodes::Up);
|
||||
m_pad_config.r1.def = button_list.at(XInputKeyCodes::RB);
|
||||
m_pad_config.r2.def = button_list.at(XInputKeyCodes::RT);
|
||||
m_pad_config.r3.def = button_list.at(XInputKeyCodes::RS);
|
||||
m_pad_config.l1.def = button_list.at(XInputKeyCodes::RB);
|
||||
m_pad_config.l2.def = button_list.at(XInputKeyCodes::LT);
|
||||
m_pad_config.l3.def = button_list.at(XInputKeyCodes::LS);
|
||||
|
||||
// Set default misc variables
|
||||
m_pad_config.lstickdeadzone.def = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; // between 0 and 32767
|
||||
m_pad_config.rstickdeadzone.def = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; // between 0 and 32767
|
||||
m_pad_config.ltriggerthreshold.def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD; // between 0 and 255
|
||||
m_pad_config.rtriggerthreshold.def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD; // between 0 and 255
|
||||
m_pad_config.padsquircling.def = 8000;
|
||||
|
||||
// apply defaults
|
||||
m_pad_config.from_default();
|
||||
|
||||
// set capabilities
|
||||
b_has_config = true;
|
||||
b_has_rumble = true;
|
||||
b_has_deadzones = true;
|
||||
|
||||
m_trigger_threshold = trigger_max / 2;
|
||||
m_thumb_threshold = thumb_max / 2;
|
||||
}
|
||||
|
||||
xinput_pad_handler::~xinput_pad_handler()
|
||||
@ -43,11 +70,198 @@ xinput_pad_handler::~xinput_pad_handler()
|
||||
Close();
|
||||
}
|
||||
|
||||
void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback)
|
||||
{
|
||||
if (!Init())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = padId.find("Xinput Pad #");
|
||||
int device_number;
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
device_number = std::stoul(padId.substr(pos + 12));
|
||||
}
|
||||
|
||||
if (pos == std::string::npos || device_number >= XUSER_MAX_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD dwResult;
|
||||
XINPUT_STATE state;
|
||||
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
||||
|
||||
// Simply get the state of the controller from XInput.
|
||||
dwResult = (*xinputGetState)(device_number, &state);
|
||||
|
||||
if (dwResult != ERROR_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed.
|
||||
// Return the new value if the button was pressed (aka. its value was bigger than 0 or the defined threshold)
|
||||
// Use a pair to get all the legally pressed buttons and use the one with highest value (prioritize first)
|
||||
std::pair<u16, std::string> pressed_button = { 0, "" };
|
||||
auto data = GetButtonValues(state);
|
||||
for (const auto& button : button_list)
|
||||
{
|
||||
u32 keycode = button.first;
|
||||
u16 value = data[keycode];
|
||||
|
||||
if (((keycode < XInputKeyCodes::LT) && (value > 0))
|
||||
|| ((keycode == XInputKeyCodes::LT) && (value > m_trigger_threshold))
|
||||
|| ((keycode == XInputKeyCodes::RT) && (value > m_trigger_threshold))
|
||||
|| ((keycode >= XInputKeyCodes::LSXNeg && keycode <= XInputKeyCodes::LSYPos) && (value > m_thumb_threshold))
|
||||
|| ((keycode >= XInputKeyCodes::RSXNeg && keycode <= XInputKeyCodes::RSYPos) && (value > m_thumb_threshold)))
|
||||
{
|
||||
if (value > pressed_button.first)
|
||||
{
|
||||
pressed_button = { value, button.second };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int preview_values[6] = { data[LT], data[RT], data[LSXPos] - data[LSXNeg], data[LSYPos] - data[LSYNeg], data[RSXPos] - data[RSXNeg], data[RSYPos] - data[RSYNeg] };
|
||||
|
||||
if (pressed_button.first > 0)
|
||||
return callback(pressed_button.first, pressed_button.second, preview_values);
|
||||
else
|
||||
return callback(0, "", preview_values);
|
||||
}
|
||||
|
||||
void xinput_pad_handler::TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor)
|
||||
{
|
||||
if (!Init())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
u32 device_number = 0;
|
||||
size_t pos = padId.find("Xinput Pad #");
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
device_number = std::stoul(padId.substr(pos + 12));
|
||||
}
|
||||
|
||||
if (pos == std::string::npos || device_number >= XUSER_MAX_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor.
|
||||
// The two motors are not the same, and they create different vibration effects.
|
||||
XINPUT_VIBRATION vibrate;
|
||||
|
||||
vibrate.wLeftMotorSpeed = largeMotor; // between 0 to 65535
|
||||
vibrate.wRightMotorSpeed = smallMotor; // between 0 to 65535
|
||||
|
||||
(*xinputSetState)(device_number, &vibrate);
|
||||
}
|
||||
|
||||
void xinput_pad_handler::TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold)
|
||||
{
|
||||
// Update the pad button values based on their type and thresholds.
|
||||
// With this you can use axis or triggers as buttons or vice versa
|
||||
switch (keyCode)
|
||||
{
|
||||
case XInputKeyCodes::LT:
|
||||
pressed = val > m_pad_config.ltriggerthreshold;
|
||||
val = pressed ? NormalizeTriggerInput(val, m_pad_config.ltriggerthreshold) : 0;
|
||||
break;
|
||||
case XInputKeyCodes::RT:
|
||||
pressed = val > m_pad_config.rtriggerthreshold;
|
||||
val = pressed ? NormalizeTriggerInput(val, m_pad_config.rtriggerthreshold) : 0;
|
||||
break;
|
||||
case XInputKeyCodes::LSXNeg:
|
||||
case XInputKeyCodes::LSXPos:
|
||||
case XInputKeyCodes::LSYPos:
|
||||
case XInputKeyCodes::LSYNeg:
|
||||
pressed = val > (ignore_threshold ? 0 : m_pad_config.lstickdeadzone);
|
||||
val = pressed ? NormalizeStickInput(val, m_pad_config.lstickdeadzone, ignore_threshold) : 0;
|
||||
break;
|
||||
case XInputKeyCodes::RSXNeg:
|
||||
case XInputKeyCodes::RSXPos:
|
||||
case XInputKeyCodes::RSYPos:
|
||||
case XInputKeyCodes::RSYNeg:
|
||||
pressed = val > (ignore_threshold ? 0 : m_pad_config.rstickdeadzone);
|
||||
val = pressed ? NormalizeStickInput(val, m_pad_config.rstickdeadzone, ignore_threshold) : 0;
|
||||
break;
|
||||
default: // normal button (should in theory also support sensitive buttons)
|
||||
pressed = val > 0;
|
||||
val = pressed ? val : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> xinput_pad_handler::GetButtonValues(const XINPUT_STATE& state)
|
||||
{
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> values;
|
||||
|
||||
// Triggers
|
||||
values[XInputKeyCodes::LT] = state.Gamepad.bLeftTrigger;
|
||||
values[XInputKeyCodes::RT] = state.Gamepad.bRightTrigger;
|
||||
|
||||
// Sticks
|
||||
int lx = state.Gamepad.sThumbLX;
|
||||
int ly = state.Gamepad.sThumbLY;
|
||||
int rx = state.Gamepad.sThumbRX;
|
||||
int ry = state.Gamepad.sThumbRY;
|
||||
|
||||
// Left Stick X Axis
|
||||
values[XInputKeyCodes::LSXNeg] = lx < 0 ? abs(lx) - 1 : 0;
|
||||
values[XInputKeyCodes::LSXPos] = lx > 0 ? lx : 0;
|
||||
|
||||
// Left Stick Y Axis
|
||||
values[XInputKeyCodes::LSYNeg] = ly < 0 ? abs(ly) - 1 : 0;
|
||||
values[XInputKeyCodes::LSYPos] = ly > 0 ? ly : 0;
|
||||
|
||||
// Right Stick X Axis
|
||||
values[XInputKeyCodes::RSXNeg] = rx < 0 ? abs(rx) - 1 : 0;
|
||||
values[XInputKeyCodes::RSXPos] = rx > 0 ? rx : 0;
|
||||
|
||||
// Right Stick Y Axis
|
||||
values[XInputKeyCodes::RSYNeg] = ry < 0 ? abs(ry) - 1 : 0;
|
||||
values[XInputKeyCodes::RSYPos] = ry > 0 ? ry : 0;
|
||||
|
||||
// Buttons
|
||||
WORD buttons = state.Gamepad.wButtons;
|
||||
|
||||
// A, B, X, Y
|
||||
values[XInputKeyCodes::A] = buttons & XINPUT_GAMEPAD_A ? 255 : 0;
|
||||
values[XInputKeyCodes::B] = buttons & XINPUT_GAMEPAD_B ? 255 : 0;
|
||||
values[XInputKeyCodes::X] = buttons & XINPUT_GAMEPAD_X ? 255 : 0;
|
||||
values[XInputKeyCodes::Y] = buttons & XINPUT_GAMEPAD_Y ? 255 : 0;
|
||||
|
||||
// D-Pad
|
||||
values[XInputKeyCodes::Left] = buttons & XINPUT_GAMEPAD_DPAD_LEFT ? 255 : 0;
|
||||
values[XInputKeyCodes::Right] = buttons & XINPUT_GAMEPAD_DPAD_RIGHT ? 255 : 0;
|
||||
values[XInputKeyCodes::Up] = buttons & XINPUT_GAMEPAD_DPAD_UP ? 255 : 0;
|
||||
values[XInputKeyCodes::Down] = buttons & XINPUT_GAMEPAD_DPAD_DOWN ? 255 : 0;
|
||||
|
||||
// LB, RB, LS, RS
|
||||
values[XInputKeyCodes::LB] = buttons & XINPUT_GAMEPAD_LEFT_SHOULDER ? 255 : 0;
|
||||
values[XInputKeyCodes::RB] = buttons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? 255 : 0;
|
||||
values[XInputKeyCodes::LS] = buttons & XINPUT_GAMEPAD_LEFT_THUMB ? 255 : 0;
|
||||
values[XInputKeyCodes::RS] = buttons & XINPUT_GAMEPAD_RIGHT_THUMB ? 255 : 0;
|
||||
|
||||
// Start, Back, Guide
|
||||
values[XInputKeyCodes::Start] = buttons & XINPUT_GAMEPAD_START ? 255 : 0;
|
||||
values[XInputKeyCodes::Back] = buttons & XINPUT_GAMEPAD_BACK ? 255 : 0;
|
||||
values[XInputKeyCodes::Guide] = buttons & XINPUT_INFO::GUIDE_BUTTON ? 255 : 0;
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
bool xinput_pad_handler::Init()
|
||||
{
|
||||
if (is_init) return true;
|
||||
|
||||
for (auto it : LIBRARY_FILENAMES)
|
||||
for (auto it : XINPUT_INFO::LIBRARY_FILENAMES)
|
||||
{
|
||||
library = LoadLibrary(it);
|
||||
if (library)
|
||||
@ -60,8 +274,9 @@ bool xinput_pad_handler::Init()
|
||||
}
|
||||
|
||||
xinputSetState = reinterpret_cast<PFN_XINPUTSETSTATE>(GetProcAddress(library, "XInputSetState"));
|
||||
xinputGetBatteryInformation = reinterpret_cast<PFN_XINPUTGETBATTERYINFORMATION>(GetProcAddress(library, "XInputGetBatteryInformation"));
|
||||
|
||||
if (xinputEnable && xinputGetState && xinputSetState)
|
||||
if (xinputEnable && xinputGetState && xinputSetState && xinputGetBatteryInformation)
|
||||
{
|
||||
is_init = true;
|
||||
break;
|
||||
@ -71,17 +286,14 @@ bool xinput_pad_handler::Init()
|
||||
library = nullptr;
|
||||
xinputEnable = nullptr;
|
||||
xinputGetState = nullptr;
|
||||
xinputGetBatteryInformation = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_init) return false;
|
||||
|
||||
xinput_cfg.load();
|
||||
if (!xinput_cfg.exist()) xinput_cfg.save();
|
||||
|
||||
squircle_factor = xinput_cfg.padsquircling / 1000.f;
|
||||
left_stick_deadzone = xinput_cfg.lstickdeadzone;
|
||||
right_stick_deadzone = xinput_cfg.rstickdeadzone;
|
||||
m_pad_config.load();
|
||||
if (!m_pad_config.exist()) m_pad_config.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -94,36 +306,17 @@ void xinput_pad_handler::Close()
|
||||
library = nullptr;
|
||||
xinputGetState = nullptr;
|
||||
xinputEnable = nullptr;
|
||||
xinputGetBatteryInformation = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<u16, u16> xinput_pad_handler::ConvertToSquirclePoint(u16 inX, u16 inY)
|
||||
{
|
||||
// convert inX and Y to a (-1, 1) vector;
|
||||
const f32 x = (inX - 127) / 127.f;
|
||||
const f32 y = ((inY - 127) / 127.f);
|
||||
|
||||
// compute angle and len of given point to be used for squircle radius
|
||||
const f32 angle = std::atan2(y, x);
|
||||
const f32 r = std::sqrt(std::pow(x, 2.f) + std::pow(y, 2.f));
|
||||
|
||||
// now find len/point on the given squircle from our current angle and radius in polar coords
|
||||
// https://thatsmaths.com/2016/07/14/squircles/
|
||||
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / squircle_factor) * r;
|
||||
|
||||
// we now have len and angle, convert to cartisian
|
||||
|
||||
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127);
|
||||
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127);
|
||||
return std::tuple<u16, u16>(newX, newY);
|
||||
}
|
||||
|
||||
void xinput_pad_handler::ThreadProc()
|
||||
{
|
||||
for (u32 index = 0; index != bindings.size(); index++)
|
||||
for (auto &bind : bindings)
|
||||
{
|
||||
auto padnum = bindings[index].first;
|
||||
auto pad = bindings[index].second;
|
||||
auto device = bind.first;
|
||||
auto padnum = device->deviceNumber;
|
||||
auto pad = bind.second;
|
||||
|
||||
result = (*xinputGetState)(padnum, &state);
|
||||
switch (result)
|
||||
@ -142,77 +335,95 @@ void xinput_pad_handler::ThreadProc()
|
||||
last_connection_status[padnum] = true;
|
||||
pad->m_port_status |= CELL_PAD_STATUS_CONNECTED;
|
||||
|
||||
for (DWORD j = 0; j != XINPUT_GAMEPAD_BUTTONS; ++j)
|
||||
std::array<u16, XInputKeyCodes::KeyCodeCount> button_values = GetButtonValues(state);
|
||||
|
||||
// Translate any corresponding keycodes to our normal DS3 buttons and triggers
|
||||
for (auto& btn : pad->m_buttons)
|
||||
{
|
||||
bool pressed = state.Gamepad.wButtons & (1 << j);
|
||||
pad->m_buttons[j].m_pressed = pressed;
|
||||
pad->m_buttons[j].m_value = pressed ? 255 : 0;
|
||||
btn.m_value = button_values[btn.m_keyCode];
|
||||
TranslateButtonPress(btn.m_keyCode, btn.m_pressed, btn.m_value);
|
||||
}
|
||||
|
||||
for (int i = 6; i < 16; i++)
|
||||
for (const auto& btn : pad->m_buttons)
|
||||
{
|
||||
if (pad->m_buttons[i].m_pressed)
|
||||
if (btn.m_pressed)
|
||||
{
|
||||
SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS].m_pressed = state.Gamepad.bLeftTrigger > 0;
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS].m_value = state.Gamepad.bLeftTrigger;
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_pressed = state.Gamepad.bRightTrigger > 0;
|
||||
pad->m_buttons[XINPUT_GAMEPAD_BUTTONS + 1].m_value = state.Gamepad.bRightTrigger;
|
||||
// used to get the absolute value of an axis
|
||||
float stick_val[4];
|
||||
|
||||
float LX, LY, RX, RY;
|
||||
|
||||
LX = state.Gamepad.sThumbLX;
|
||||
LY = state.Gamepad.sThumbLY;
|
||||
RX = state.Gamepad.sThumbRX;
|
||||
RY = state.Gamepad.sThumbRY;
|
||||
|
||||
auto normalize_input = [](float& X, float& Y, float deadzone)
|
||||
// Translate any corresponding keycodes to our two sticks. (ignoring thresholds for now)
|
||||
for (int i = 0; i < static_cast<int>(pad->m_sticks.size()); i++)
|
||||
{
|
||||
X /= 32767.0f;
|
||||
Y /= 32767.0f;
|
||||
deadzone /= 32767.0f;
|
||||
bool pressed;
|
||||
|
||||
float mag = sqrtf(X*X + Y*Y);
|
||||
// m_keyCodeMin is the mapped key for left or down
|
||||
u32 key_min = pad->m_sticks[i].m_keyCodeMin;
|
||||
u16 val_min = button_values[key_min];
|
||||
TranslateButtonPress(key_min, pressed, val_min, true);
|
||||
|
||||
if (mag > deadzone)
|
||||
{
|
||||
float legalRange = 1.0f - deadzone;
|
||||
float normalizedMag = std::min(1.0f, (mag - deadzone) / legalRange);
|
||||
float scale = normalizedMag / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
}
|
||||
};
|
||||
// m_keyCodeMax is the mapped key for right or up
|
||||
u32 key_max = pad->m_sticks[i].m_keyCodeMax;
|
||||
u16 val_max = button_values[key_max];
|
||||
TranslateButtonPress(key_max, pressed, val_max, true);
|
||||
|
||||
normalize_input(LX, LY, left_stick_deadzone);
|
||||
normalize_input(RX, RY, right_stick_deadzone);
|
||||
|
||||
pad->m_sticks[0].m_value = ConvertAxis(LX);
|
||||
pad->m_sticks[1].m_value = 255 - ConvertAxis(LY);
|
||||
pad->m_sticks[2].m_value = ConvertAxis(RX);
|
||||
pad->m_sticks[3].m_value = 255 - ConvertAxis(RY);
|
||||
|
||||
if (squircle_factor != 0.f)
|
||||
{
|
||||
std::tie(pad->m_sticks[0].m_value, pad->m_sticks[1].m_value) = ConvertToSquirclePoint(pad->m_sticks[0].m_value, pad->m_sticks[1].m_value);
|
||||
std::tie(pad->m_sticks[2].m_value, pad->m_sticks[3].m_value) = ConvertToSquirclePoint(pad->m_sticks[2].m_value, pad->m_sticks[3].m_value);
|
||||
// cancel out opposing values and get the resulting difference
|
||||
stick_val[i] = val_max - val_min;
|
||||
}
|
||||
|
||||
XINPUT_VIBRATION vibrate;
|
||||
u16 lx, ly, rx, ry;
|
||||
|
||||
vibrate.wLeftMotorSpeed = pad->m_vibrateMotors[0].m_value * 257;
|
||||
vibrate.wRightMotorSpeed = pad->m_vibrateMotors[1].m_value * 257;
|
||||
// Normalize our two stick's axis based on the thresholds
|
||||
std::tie(lx, ly) = NormalizeStickDeadzone(stick_val[0], stick_val[1], m_pad_config.lstickdeadzone);
|
||||
std::tie(rx, ry) = NormalizeStickDeadzone(stick_val[2], stick_val[3], m_pad_config.rstickdeadzone);
|
||||
|
||||
(*xinputSetState)(padnum, &vibrate);
|
||||
if (m_pad_config.padsquircling != 0)
|
||||
{
|
||||
std::tie(lx, ly) = ConvertToSquirclePoint(lx, ly, m_pad_config.padsquircling);
|
||||
std::tie(rx, ry) = ConvertToSquirclePoint(rx, ry, m_pad_config.padsquircling);
|
||||
}
|
||||
|
||||
pad->m_sticks[0].m_value = lx;
|
||||
pad->m_sticks[1].m_value = 255 - ly;
|
||||
pad->m_sticks[2].m_value = rx;
|
||||
pad->m_sticks[3].m_value = 255 - ry;
|
||||
|
||||
// Receive Battery Info. If device is not on cable, get battery level, else assume full
|
||||
XINPUT_BATTERY_INFORMATION battery_info;
|
||||
(*xinputGetBatteryInformation)(padnum, BATTERY_DEVTYPE_GAMEPAD, &battery_info);
|
||||
pad->m_cable_state = battery_info.BatteryType == BATTERY_TYPE_WIRED ? 1 : 0;
|
||||
pad->m_battery_level = pad->m_cable_state ? BATTERY_LEVEL_FULL : battery_info.BatteryLevel;
|
||||
|
||||
// The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor.
|
||||
// The two motors are not the same, and they create different vibration effects. Values range between 0 to 65535.
|
||||
int idx_l = m_pad_config.switch_vibration_motors ? 1 : 0;
|
||||
int idx_s = m_pad_config.switch_vibration_motors ? 0 : 1;
|
||||
|
||||
int speed_large = m_pad_config.enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value * 257 : vibration_min;
|
||||
int speed_small = m_pad_config.enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value * 257 : vibration_min;
|
||||
|
||||
device->newVibrateData = device->newVibrateData || device->largeVibrate != speed_large || device->smallVibrate != speed_small;
|
||||
|
||||
device->largeVibrate = speed_large;
|
||||
device->smallVibrate = speed_small;
|
||||
|
||||
// XBox One Controller can't handle faster vibration updates than ~10ms. Elite is even worse. So I'll use 20ms to be on the safe side. No lag was noticable.
|
||||
if (device->newVibrateData && (clock() - device->last_vibration > 20))
|
||||
{
|
||||
XINPUT_VIBRATION vibrate;
|
||||
vibrate.wLeftMotorSpeed = speed_large;
|
||||
vibrate.wRightMotorSpeed = speed_small;
|
||||
|
||||
if ((*xinputSetState)(padnum, &vibrate) == ERROR_SUCCESS)
|
||||
{
|
||||
device->newVibrateData = false;
|
||||
device->last_vibration = clock();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@ -225,7 +436,7 @@ std::vector<std::string> xinput_pad_handler::ListDevices()
|
||||
|
||||
if (!Init()) return xinput_pads_list;
|
||||
|
||||
for (DWORD i = 0; i < MAX_GAMEPADS; i++)
|
||||
for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
|
||||
{
|
||||
XINPUT_STATE state;
|
||||
DWORD result = (*xinputGetState)(i, &state);
|
||||
@ -245,43 +456,54 @@ bool xinput_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, const std::st
|
||||
|
||||
if (pos != std::string::npos) device_number = std::stoul(device.substr(pos + 12));
|
||||
|
||||
if (pos == std::string::npos || device_number >= MAX_GAMEPADS) return false;
|
||||
if (pos == std::string::npos || device_number >= XUSER_MAX_COUNT) return false;
|
||||
|
||||
pad->Init(
|
||||
CELL_PAD_STATUS_DISCONNECTED,
|
||||
std::shared_ptr<XInputDevice> device_id = std::make_shared<XInputDevice>();
|
||||
device_id->deviceNumber = device_number;
|
||||
|
||||
m_pad_config.load();
|
||||
|
||||
pad->Init
|
||||
(
|
||||
CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES,
|
||||
CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR,
|
||||
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE,
|
||||
CELL_PAD_DEV_TYPE_STANDARD
|
||||
);
|
||||
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_UP, CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_DOWN, CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_LEFT, CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_DPAD_RIGHT, CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_START, CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_BACK, CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_LEFT_THUMB, CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, XINPUT_GAMEPAD_RIGHT_THUMB, CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_LEFT_SHOULDER, CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_RIGHT_SHOULDER, CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_GUIDE, 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.up), CELL_PAD_CTRL_UP);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.down), CELL_PAD_CTRL_DOWN);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.left), CELL_PAD_CTRL_LEFT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.right), CELL_PAD_CTRL_RIGHT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.start), CELL_PAD_CTRL_START);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.select), CELL_PAD_CTRL_SELECT);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.l3), CELL_PAD_CTRL_L3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, FindKeyCode(button_list, m_pad_config.r3), CELL_PAD_CTRL_R3);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.l1), CELL_PAD_CTRL_L1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.r1), CELL_PAD_CTRL_R1);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.ps), 0x100/*CELL_PAD_CTRL_PS*/);// TODO: PS button support
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.cross), CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.circle), CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.square), CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.triangle), CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.l2), CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, FindKeyCode(button_list, m_pad_config.r2), CELL_PAD_CTRL_R2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, 0x0); // Reserved
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_A, CELL_PAD_CTRL_CROSS);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_B, CELL_PAD_CTRL_CIRCLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_X, CELL_PAD_CTRL_SQUARE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, XINPUT_GAMEPAD_Y, CELL_PAD_CTRL_TRIANGLE);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_L2);
|
||||
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, 0, CELL_PAD_CTRL_R2);
|
||||
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, 0, 0);
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, FindKeyCode(button_list, m_pad_config.ls_left), FindKeyCode(button_list, m_pad_config.ls_right));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, FindKeyCode(button_list, m_pad_config.ls_down), FindKeyCode(button_list, m_pad_config.ls_up));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, FindKeyCode(button_list, m_pad_config.rs_left), FindKeyCode(button_list, m_pad_config.rs_right));
|
||||
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, FindKeyCode(button_list, m_pad_config.rs_down), FindKeyCode(button_list, m_pad_config.rs_up));
|
||||
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_X, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Y, 399);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_Z, 512);
|
||||
pad->m_sensors.emplace_back(CELL_PAD_BTN_OFFSET_SENSOR_G, 512);
|
||||
|
||||
pad->m_vibrateMotors.emplace_back(true, 0);
|
||||
pad->m_vibrateMotors.emplace_back(false, 0);
|
||||
|
||||
bindings.emplace_back(device_number, pad);
|
||||
bindings.emplace_back(device_id, pad);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,43 +1,102 @@
|
||||
#ifndef X_INPUT_PAD_HANDLER
|
||||
#define X_INPUT_PAD_HANDLER
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Utilities/Config.h"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <Xinput.h>
|
||||
#include <ctime>
|
||||
|
||||
struct xinput_config final : cfg::node
|
||||
namespace XINPUT_INFO
|
||||
{
|
||||
const std::string cfg_name = fs::get_config_dir() + "/config_xinput.yml";
|
||||
|
||||
cfg::_int<0, 1000000> lstickdeadzone{ this, "Left Stick Deadzone", 7849 };
|
||||
cfg::_int<0, 1000000> rstickdeadzone{ this, "Right Stick Deadzone", 8689 };
|
||||
cfg::_int<0, 1000000> padsquircling{ this, "Pad Squircling Factor", 8000 };
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
|
||||
bool exist()
|
||||
{
|
||||
return fs::is_file(cfg_name);
|
||||
}
|
||||
};
|
||||
const DWORD THREAD_TIMEOUT = 1000;
|
||||
const DWORD THREAD_SLEEP = 10;
|
||||
const DWORD THREAD_SLEEP_INACTIVE = 100;
|
||||
const DWORD GUIDE_BUTTON = 0x0400;
|
||||
const LPCWSTR LIBRARY_FILENAMES[] = {
|
||||
L"xinput1_4.dll",
|
||||
L"xinput1_3.dll",
|
||||
L"xinput1_2.dll",
|
||||
L"xinput9_1_0.dll"
|
||||
};
|
||||
}
|
||||
|
||||
class xinput_pad_handler final : public PadHandlerBase
|
||||
{
|
||||
// These are all the possible buttons on a standard xbox 360 or xbox one controller
|
||||
enum XInputKeyCodes
|
||||
{
|
||||
A,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
LB,
|
||||
RB,
|
||||
LS,
|
||||
RS,
|
||||
Start,
|
||||
Back,
|
||||
Guide,
|
||||
|
||||
LT,
|
||||
RT,
|
||||
|
||||
LSXNeg,
|
||||
LSXPos,
|
||||
LSYNeg,
|
||||
LSYPos,
|
||||
RSXNeg,
|
||||
RSXPos,
|
||||
RSYNeg,
|
||||
RSYPos,
|
||||
|
||||
KeyCodeCount
|
||||
};
|
||||
|
||||
// Unique names for the config files and our pad settings dialog
|
||||
const std::unordered_map<u32, std::string> button_list =
|
||||
{
|
||||
{ XInputKeyCodes::A, "A" },
|
||||
{ XInputKeyCodes::B, "B" },
|
||||
{ XInputKeyCodes::X, "X" },
|
||||
{ XInputKeyCodes::Y, "Y" },
|
||||
{ XInputKeyCodes::Left, "Left" },
|
||||
{ XInputKeyCodes::Right, "Right" },
|
||||
{ XInputKeyCodes::Up, "Up" },
|
||||
{ XInputKeyCodes::Down, "Down" },
|
||||
{ XInputKeyCodes::LB, "LB" },
|
||||
{ XInputKeyCodes::RB, "RB" },
|
||||
{ XInputKeyCodes::Back, "Back" },
|
||||
{ XInputKeyCodes::Start, "Start" },
|
||||
{ XInputKeyCodes::LS, "LS" },
|
||||
{ XInputKeyCodes::RS, "RS" },
|
||||
{ XInputKeyCodes::Guide, "Guide" },
|
||||
{ XInputKeyCodes::LT, "LT" },
|
||||
{ XInputKeyCodes::RT, "RT" },
|
||||
{ XInputKeyCodes::LSXNeg, "LS X-" },
|
||||
{ XInputKeyCodes::LSXPos, "LS X+" },
|
||||
{ XInputKeyCodes::LSYPos, "LS Y+" },
|
||||
{ XInputKeyCodes::LSYNeg, "LS Y-" },
|
||||
{ XInputKeyCodes::RSXNeg, "RS X-" },
|
||||
{ XInputKeyCodes::RSXPos, "RS X+" },
|
||||
{ XInputKeyCodes::RSYPos, "RS Y+" },
|
||||
{ XInputKeyCodes::RSYNeg, "RS Y-" }
|
||||
};
|
||||
|
||||
struct XInputDevice
|
||||
{
|
||||
u32 deviceNumber{ 0 };
|
||||
bool newVibrateData{ true };
|
||||
u8 largeVibrate{ 0 };
|
||||
u8 smallVibrate{ 0 };
|
||||
clock_t last_vibration{ 0 };
|
||||
};
|
||||
|
||||
public:
|
||||
xinput_pad_handler();
|
||||
~xinput_pad_handler();
|
||||
@ -48,25 +107,27 @@ public:
|
||||
std::vector<std::string> ListDevices() override;
|
||||
bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) override;
|
||||
void ThreadProc() override;
|
||||
void GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback) override;
|
||||
void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override;
|
||||
|
||||
private:
|
||||
typedef void (WINAPI * PFN_XINPUTENABLE)(BOOL);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTGETSTATE)(DWORD, XINPUT_STATE *);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTGETBATTERYINFORMATION)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
|
||||
|
||||
private:
|
||||
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY);
|
||||
std::array<u16, XInputKeyCodes::KeyCodeCount> GetButtonValues(const XINPUT_STATE& state);
|
||||
void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) override;
|
||||
|
||||
private:
|
||||
bool is_init;
|
||||
float squircle_factor;
|
||||
u32 left_stick_deadzone, right_stick_deadzone;
|
||||
HMODULE library;
|
||||
PFN_XINPUTGETSTATE xinputGetState;
|
||||
PFN_XINPUTSETSTATE xinputSetState;
|
||||
PFN_XINPUTENABLE xinputEnable;
|
||||
PFN_XINPUTGETBATTERYINFORMATION xinputGetBatteryInformation;
|
||||
|
||||
std::vector<std::pair<u32, std::shared_ptr<Pad>>> bindings;
|
||||
std::vector<std::pair<std::shared_ptr<XInputDevice>, std::shared_ptr<Pad>>> bindings;
|
||||
std::array<bool, 7> last_connection_status = {};
|
||||
|
||||
// holds internal controller state change
|
||||
@ -74,7 +135,3 @@ private:
|
||||
DWORD result;
|
||||
DWORD online = 0;
|
||||
};
|
||||
|
||||
extern xinput_config xinput_cfg;
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user