Skip to content

Commit

Permalink
Add custom rumble support
Browse files Browse the repository at this point in the history
  • Loading branch information
Electronicks committed Feb 2, 2021
1 parent 97845ac commit ef73a39
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 58 deletions.
54 changes: 14 additions & 40 deletions JoyShockMapper/include/JoyShockMapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ constexpr WORD GYRO_TRACK_X = 0x8D;
constexpr WORD GYRO_TRACK_Y = 0x8E;
constexpr WORD GYRO_TRACKBALL = 0x8F;
constexpr WORD COMMAND_ACTION = 0x97; // Run command
constexpr WORD RUMBLE = 0xE6;

constexpr const char * SMALL_RUMBLE = "R0080";
constexpr const char * BIG_RUMBLE = "RFF00";

// Xinput buttons
constexpr WORD X_UP = 0xE8;
Expand Down Expand Up @@ -116,39 +120,6 @@ enum class ButtonID
// insert more analog triggers here
ZRF, // = LAST_ANALOG_TRIGGER
SIZE, // Not a button

// Virtual buttons configured on the touchpad. The number of buttons vary dynamically, but they each need a different ID
TUP, // FIRST_TOUCH_BUTTON
TDOWN,
TLEFT,
TRIGHT,
TRING,
T1,
T2,
T3,
T4,
T5,
T6,
T7,
T8,
T9,
T10,
T11,
T12,
T13,
T14,
T15,
T16,
T17,
T18,
T19,
T20,
T21,
T22,
T23,
T24,
T25,
// Add as necessary...
};

// Help strings for each button
Expand Down Expand Up @@ -264,13 +235,6 @@ enum class BtnEvent { OnPress, OnTap, OnHold, OnTurbo, OnRelease, OnTapRelease,
enum class Switch : char { OFF, ON, INVALID, }; // Used to parse autoload assignment
enum class ControllerScheme { NONE, XBOX, DS4, INVALID };

enum class TouchpadMode
{
GRID_AND_STICK, // Grid and Stick ?
MOUSE, // gestures to be added as part of this mode
INVALID
};

// Workaround default string streaming operator
class PathString : public string // Should be wstring
{
Expand Down Expand Up @@ -316,6 +280,16 @@ struct KeyCode
{
if (code == COMMAND_ACTION)
name = keyName.substr(1, keyName.size() - 2); // Remove opening and closing quotation marks
else if (keyName.compare("SMALL_RUMBLE") == 0)
{
name = SMALL_RUMBLE;
code = RUMBLE;
}
else if (keyName.compare("BIG_RUMBLE") == 0)
{
name = BIG_RUMBLE;
code = RUMBLE;
}
else if (code != 0)
name = keyName;
}
Expand Down
47 changes: 31 additions & 16 deletions JoyShockMapper/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ class DigitalButton
}
}

void SetRumble(int smallRumble, int bigRumble)
{
COUT << "Rumbling at " << smallRumble << " and " << bigRumble << endl;
JslSetRumble(_deviceHandle, smallRumble, bigRumble);
}

void ApplyBtnPress(KeyCode key)
{
if (key.code >= X_UP && key.code <= X_START || key.code == PS_HOME || key.code == PS_PAD_CLICK)
Expand Down Expand Up @@ -394,7 +400,7 @@ class DigitalButton

void handleButtonChange(bool pressed, chrono::steady_clock::time_point time_now, float turboTime, float holdTime)
{
if (_id < ButtonID::SIZE || _id >= ButtonID::T1) // Can't chord touch stick buttons?!?
if (_id < ButtonID::SIZE)
{
auto foundChord = find(_common->chordStack.begin(), _common->chordStack.end(), _id);
if (!pressed)
Expand Down Expand Up @@ -600,8 +606,8 @@ istream &operator >> (istream &in, Mapping &mapping)

mapping._command = valueName;
stringstream ss;

while (regex_match(valueName, results, regex(R"(\s*([!\^]?)((\".*?\")|\w*[0-9A-Z]|\W)([\\\/+'_]?)\s*(.*))")) && !results[0].str().empty())
const char * rgx = R"(\s*([!\^]?)((\".*?\")|\w*[0-9A-Z]|\W)([\\\/+'_]?)\s*(.*))";
while (regex_match(valueName, results, regex(rgx)) && !results[0].str().empty())
{
if (count > 0) ss << " and ";
Mapping::ActionModifier actMod = results[1].str().empty() ? Mapping::ActionModifier::None :
Expand Down Expand Up @@ -755,6 +761,17 @@ bool Mapping::AddMapping(KeyCode key, EventModifier evtMod, ActionModifier actMo
apply = bind(&WriteToConsole, key.name);
release = OnEventAction();
}
else if (key.code == RUMBLE)
{
union Rumble
{
int raw;
array<UCHAR, 2> bytes;
} rumble;
rumble.raw = stoi(key.name.substr(1, 4), nullptr, 16);
apply = bind(&DigitalButton::SetRumble, placeholders::_1, rumble.bytes[0], rumble.bytes[1]);
release = bind(&DigitalButton::SetRumble, placeholders::_1, 0, 0);
}
else // if (key.code != NO_HOLD_MAPPED)
{
_hasViGEmBtn |= (key.code >= X_UP && key.code <= X_START) || key.code == PS_HOME || key.code == PS_PAD_CLICK; // Set flag if vigem button
Expand Down Expand Up @@ -1828,8 +1845,9 @@ void connectDevices(bool mergeJoycons = true) {

if (numConnected == 1) {
COUT << "1 device connected" << endl;
}
else {
} else if(numConnected == 0) {
CERR << numConnected << " devices connected" << endl;
} else {
COUT << numConnected << " devices connected" << endl;
}
//if (!IsVisible())
Expand Down Expand Up @@ -1860,7 +1878,7 @@ bool do_RESET_MAPPINGS(CmdRegistry *registry) {
{
if (!registry->loadConfigFile("onreset.txt"))
{
COUT << "There is no onreset.txt file to load." << endl;
COUT_INFO << "There is no onreset.txt file to load." << endl;
}
}
return true;
Expand Down Expand Up @@ -2155,7 +2173,7 @@ JoyShock* getJoyShockFromHandle(int handle) {
void processStick(JoyShock* jc, float stickX, float stickY, float lastX, float lastY, float innerDeadzone, float outerDeadzone,
RingMode ringMode, StickMode stickMode, ButtonID ringId, ButtonID leftId, ButtonID rightId, ButtonID upId, ButtonID downId,
ControllerOrientation controllerOrientation, float mouseCalibrationFactor, float deltaTime, float &acceleration, FloatXY &lastAreaCal,
bool& isFlicking, bool &ignoreStickMode, bool &anyStickInput, bool &lockMouse, float &camSpeedX, float &camSpeedY, ScrollAxis *scroll, int touchpadIndex = -1)
bool& isFlicking, bool &ignoreStickMode, bool &anyStickInput, bool &lockMouse, float &camSpeedX, float &camSpeedY, ScrollAxis *scroll)
{
float temp;
switch (controllerOrientation)
Expand Down Expand Up @@ -3392,15 +3410,8 @@ int main(int argc, char *argv[]) {
commandRegistry.Add(new HelpCmd(commandRegistry));
commandRegistry.Add((new JSMAssignment<ControllerScheme>(magic_enum::enum_name(SettingID::VIRTUAL_CONTROLLER).data(), virtual_controller))
->SetHelp("Sets the vigem virtual controller type. Can be NONE (default), XBOX (360) or DS4 (PS4)."));
commandRegistry.Add((new JSMMacro("RUMBLE"))->SetMacro([](JSMMacro *, in_string) {
for (auto js : handle_to_joyshock)
{
JslSetRumble(js.first, 255, 255);
Sleep(2000);
JslSetRumble(js.first, 0, 0);
}
return true;
}));
commandRegistry.Add((new JSMAssignment<FloatXY>(scroll_sens))
->SetHelp("Scrolling sensitivity for sticks."));

bool quit = false;
commandRegistry.Add((new JSMMacro("QUIT"))
Expand All @@ -3423,6 +3434,10 @@ int main(int argc, char *argv[]) {
{
COUT << "Finished executing startup file." << endl;
}
else
{
COUT_INFO << "There is no onstartup.txt file to load." << endl;
}

// The main loop is simple and reads like pseudocode
string enteredCommand;
Expand Down
20 changes: 20 additions & 0 deletions JoyShockMapper/src/win32/PlatformDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,26 @@ WORD nameToKey(const std::string &name)
}
}
}
if (length == 5)
{
auto pchar = name.c_str();
if (*pchar++ == 'R')
{
while (*pchar != '\0')
{
if (*pchar < '0' && *pchar > 'F')
{
break;
}
pchar++;
}
if (*pchar == '\0')
{
return RUMBLE;
}
// Else it's not a rumble command. Could be RIGHT for example
}
}
if (length > 2 && name[0] == '"' && name[length - 1] == '"')
{
return COMMAND_ACTION;
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ The Sony PlayStation DualSense, DualShock 4, Nintendo Switch JoyCons (used in pa

My goal with JoyShockMapper is to enable you to play PC games with DS, DS4, JoyCons, and Pro Controllers even better than you can on their respective consoles -- and demonstrate that more games should use these features in these ways.

**Download JoyShockMapper to use right away [here](https://github.com/JibbSmart/JoyShockMapper/releases)**!
**Download JoyShockMapper to use right away [here](https://github.com/Electronicks/JoyShockMapper/releases)**!

For developers, this is also a reference implementation for using [JoyShockLibrary](https://github.com/jibbsmart/JoyShockLibrary) to read inputs from DualShock 4, JoyCons, and Pro Controller in your games. It's also a reference implementation for many of the best practices described on [GyroWiki](http://gyrowiki.jibbsmart.com).

Expand Down Expand Up @@ -35,6 +35,8 @@ JoyShockMapper works on Windows and uses JoyShockLibrary to read inputs from con
* **[Calculating the real world calibration in a 3D game](#52-calculating-the-real-world-calibration-in-a-3d-game)**
* **[Calculating the real world calibration in a 2D game](#53-calculating-the-real-world-calibration-in-a-2d-game)**
* **[ViGEm Virtual Controller](#6-vigem-virtual-controller)**
* **[Xbox bindings](#61-xbox-bindings)**
* **[DS4 bindings](#62-ds4-bindings)**
* **[Modeshifts](#7-modeshifts)**
* **[Miscellaneous Commands](#8-miscellaneous-commands)**
* **[Configuration Files](#configuration-files)**
Expand Down Expand Up @@ -690,7 +692,7 @@ With such a calibrated 2D game, you can choose your GYRO\_SENS or other settings

### 6. ViGEm Virtual Controller

JoyShockMapper can create a virtual xbox or DS4 controller thanks to Nefarius' ViGEm Bus and ViGEm Client softwares. The former needs to be installed by the user before the latter can be used. Once installed, you can set which virtual device you desire to create for each connected device using the command ```VIRTUAL_CONTROLLER = XBOX``` or ```VIRTUAL_CONTROLLER = DS4```. The default value is NONE, which is no virtual controller at all. Rumble doesn't work just yet, but should be fixed in a few updates. Using virtual controllers is most likely to work well only if whitelisting is active (HIDGuardian/HIDCerberus), in order to hide the original controller entry from the game.
JoyShockMapper can create a virtual xbox or DS4 controller thanks to Nefarius' ViGEm Bus and ViGEm Client softwares. The former needs to be installed by the user before the latter can be used. Once installed, you can set which virtual device you desire to create for each connected device using the command ```VIRTUAL_CONTROLLER = XBOX``` or ```VIRTUAL_CONTROLLER = DS4```. The default value is ```NONE```, which is no virtual controller at all. Rumble will then work on DS4 controllers, but obviously support is game dependant. Using virtual controllers is most likely to work well only if whitelisting is active (HIDGuardian/HIDCerberus), in order to hide the original controller entry from the game and only expose the virtual one. Funny thing to note is that hiding DS4s with HIDGuardian will also hide the virtual DS4 from ViGEm, since Windows cannot tell the virtual controller form the physical one.

#### 6.1 Xbox bindings
If you have set the virtual controller to the xbox scheme, then the following becomes available to you:
Expand Down

0 comments on commit ef73a39

Please sign in to comment.