From ef73a3955a8904e3b248173bd7b05e1c92ca8315 Mon Sep 17 00:00:00 2001 From: Nicolas Lessard Date: Tue, 2 Feb 2021 15:21:56 -0500 Subject: [PATCH] Add custom rumble support --- JoyShockMapper/include/JoyShockMapper.h | 54 +++++-------------- JoyShockMapper/src/main.cpp | 47 ++++++++++------ .../src/win32/PlatformDefinitions.cpp | 20 +++++++ README.md | 6 ++- 4 files changed, 69 insertions(+), 58 deletions(-) diff --git a/JoyShockMapper/include/JoyShockMapper.h b/JoyShockMapper/include/JoyShockMapper.h index a58e9e1..90a0367 100644 --- a/JoyShockMapper/include/JoyShockMapper.h +++ b/JoyShockMapper/include/JoyShockMapper.h @@ -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; @@ -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 @@ -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 { @@ -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; } diff --git a/JoyShockMapper/src/main.cpp b/JoyShockMapper/src/main.cpp index 4cd2479..08d752d 100644 --- a/JoyShockMapper/src/main.cpp +++ b/JoyShockMapper/src/main.cpp @@ -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) @@ -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) @@ -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 : @@ -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 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 @@ -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()) @@ -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; @@ -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) @@ -3392,15 +3410,8 @@ int main(int argc, char *argv[]) { commandRegistry.Add(new HelpCmd(commandRegistry)); commandRegistry.Add((new JSMAssignment(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(scroll_sens)) + ->SetHelp("Scrolling sensitivity for sticks.")); bool quit = false; commandRegistry.Add((new JSMMacro("QUIT")) @@ -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; diff --git a/JoyShockMapper/src/win32/PlatformDefinitions.cpp b/JoyShockMapper/src/win32/PlatformDefinitions.cpp index e29dd4a..f5f5581 100644 --- a/JoyShockMapper/src/win32/PlatformDefinitions.cpp +++ b/JoyShockMapper/src/win32/PlatformDefinitions.cpp @@ -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; diff --git a/README.md b/README.md index ffc3171..319b5bd 100644 --- a/README.md +++ b/README.md @@ -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). @@ -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)** @@ -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: