diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f844a7..49bfb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,24 +4,34 @@ Most recent updates will appear first. This is a summary of new features and bugfixes. Read the README to learn how to use the features mentioned here. ## 3.0.0 +Jibb added JSL-specific features to SDL2 so that JSM could use SDL2 instead. This means support for many non-gyro controllers, including Xbox, Stadia, and almost every common generic PC controller. Also made it so that low report-rate controllers (eg Switch controllers) are sampled multiple times for smooth gyro on high refresh rate monitors. +**SDL2 is now the official version of JSM going forward.** JSM will also be released with JSL for the time being in case there are features there unavailable in SDL2 yet. Nicolas added ViGEm support for virtual xbox and DS4 for buttons, triggers and sticks as well as rumble forwarding. Added colored console lines -Added DS4 rumble commands and light bar setting +Added rumble commands and DS4 light bar setting and TOUCH binding +Added DS4 dual stage mode for toupad touch and click. Added hair trigger soft press as a negative threshold and trigger modes SCROLL_WHEEL and NO_SKIP_EXCLUSIVE Added ability to hide JSM from the taskbar when minimized, with a checkbox in the tray icon Added help strings for button mapping +Added argument to RECONNECT_CONTROLLERS [MERGE|SPLIT] used to determine joycon behaviour +Added handler to modeshift a gyro button to NONE\ as no button since NONE is used to remove the modeshift. Handle drag n drop files into the console better Improve command error handling +Improved README separation +Improved desktop recommended config -Jibb added JSL-specific features to SDL2 so that JSM could use SDL2 instead. This means support for many non-gyro controllers, including Xbox, Stadia, and almost every common generic PC controller. Also made it so that low report-rate controllers (eg Switch controllers) are sampled multiple times for smooth gyro on high refresh rate monitors. ### Features -* New Bindings: TOUCH, T1-T25 touch buttons, Touch stick bindings -* New settings for touch joystick such as mode, inner deadzone, and stick radius +* New Bindings: TOUCH for the touch pad and dual stage mode for TOUCH and CAPTURE bindings +* New Mappings for ViGEm controller bindings. See README +* Added Mappings SMALL_RUMBLE, BIG_RUMBLE and Rhhhh (h being hex digits) +* Added settings STICK_DEADZONE_INNER|OUTER, TICK_TIME (aka polling period), LIGHT_BAR, SCROLL_SENS, VIRTUAL_CONTROLLER, RUMBLE ( = ON|OFF) and TOUCHPAD_DUAL_STAGE_MODE +* Added JOYCON_SIDEWAYS as a controller orientation +* Added Stick Mode SCROLL_WHEEL +* Added TriggerMode NO_SKIP_EXCLUSIVE * Assigning a negative value to trigger threshold enables hair trigger * New setting HIDE\_MINIMIZED will hide JSM when set to ON. OFF is default -* Support for many non-gyro controllers: Xbox, Stadia, GameCube, PS3 (without motion), and many generic PC controllers. -* Added TICK\_TIME to set how many milliseconds between reading controllers (default of 3 means about 333Hz). +* Support for many non-gyro controllers: Xbox, Stadia, GameCube, PS3 (without motion), and many generic PC controllers via SDL2 ## 2.2.0 Nicolas added more keybinds. Robin fixed issues with building on Linux and improved PlayStation controller support. diff --git a/JoyShockMapper/.clang-format b/JoyShockMapper/.clang-format index da1cea4..adc5e12 100644 --- a/JoyShockMapper/.clang-format +++ b/JoyShockMapper/.clang-format @@ -12,7 +12,7 @@ AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None +AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false @@ -125,7 +125,7 @@ SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp11 +Standard: Cpp17 StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION diff --git a/JoyShockMapper/CMakeLists.txt b/JoyShockMapper/CMakeLists.txt index 596ba03..540c0cf 100644 --- a/JoyShockMapper/CMakeLists.txt +++ b/JoyShockMapper/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable ( include/JoyShockMapper.h include/ColorCodes.h include/GamepadMotion.hpp + include/Gamepad.h ) if (WINDOWS) @@ -39,23 +40,16 @@ if (WINDOWS) src/win32/PlatformDefinitions.cpp src/win32/WindowsTrayIcon.cpp include/win32/WindowsTrayIcon.h src/win32/Whitelister.cpp - src/win32/Gamepad.cpp include/win32/Gamepad.h + src/win32/Gamepad.cpp "Win32 Dialog.rc" include/win32/resource.h ) if(SDL) target_sources ( ${BINARY_NAME} PRIVATE - src/JoyShockLibrary.cpp include/JoyShockLibrary.h + src/SDL2Wrapper.cpp include/JoyShockLibrary.h ) endif() - - set_target_properties ( - ${BINARY_NAME} PROPERTIES - WIN32_EXECUTABLE ON - VS_STARTUP_PROJECT "${BINARY_NAME}" - VS_DEBUGGER_WORKING_DIRECTORY "$(ProjectDir)/$(Configuration)/" - ) # ViGEmClient CPMAddPackage ( @@ -65,26 +59,22 @@ if (WINDOWS) ) include_external_msproject(ViGEmClient - "_deps/vigemclient-src/src/ViGEmClient.vcxproj") - - SET(ViGEmClient_GUID_CMAKE "7DB06674-1F4F-464B-8E1C-172E9587F9DC" CACHE INTERNAL "Project GUID") - + "${PROJECT_BINARY_DIR}/_deps/vigemclient-src/src/ViGEmClient.vcxproj") + set_target_properties(ViGEmClient PROPERTIES MAP_IMPORTED_CONFIG_DEBUG DEBUG_LIB MAP_IMPORTED_CONFIG_RELEASE RELEASE_LIB - ) - - set_target_properties(ViGEmClient PROPERTIES VS_PLATFORM_TOOLSET "v141" - IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/lib/debug/$(PlatformShortName)/ViGEmClient.lib" + IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}/$(PlatformShortName)/ViGEmClient.lib" INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}/_deps/vigemclient-src/include" ) add_dependencies(${BINARY_NAME} ViGEmClient) - set_target_properties ( - ${BINARY_NAME} PROPERTIES - LINK_LIBRARIES "${PROJECT_BINARY_DIR}/lib/debug/$(PlatformShortName)/ViGEmClient.lib" + target_link_libraries ( + ${BINARY_NAME} PRIVATE + debug "${PROJECT_BINARY_DIR}/lib/debug/$(PlatformShortName)/ViGEmClient.lib" + optimized "${PROJECT_BINARY_DIR}/lib/release/$(PlatformShortName)/ViGEmClient.lib" ) target_include_directories ( @@ -109,6 +99,14 @@ if (WINDOWS) "$/$" ) endif() + + set_target_properties ( + ${BINARY_NAME} PROPERTIES + WIN32_EXECUTABLE ON + VS_STARTUP_PROJECT "${BINARY_NAME}" + VS_DEBUGGER_WORKING_DIRECTORY "$(ProjectDir)/$(Configuration)/" + VS_DEBUGGER_COMMAND_ARGUMENTS "$(SolutionDir)..\\$(ProjectName)\\dist" + ) add_definitions(/bigobj) endif () @@ -121,6 +119,7 @@ if (LINUX) src/linux/PlatformDefinitions.cpp src/linux/StatusNotifierItem.cpp include/linux/StatusNotifierItem.h src/linux/Whitelister.cpp + src/linux/Gamepad.cpp ) endif () @@ -145,6 +144,9 @@ if(SDL) # 2.0.15 hasn't released yet, so let's use a specific commit GIT_TAG c287087fcce57af086c0a52f74fcdb74e9084c55 ) + + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + target_link_libraries ( ${BINARY_NAME} PRIVATE Platform::Dependencies @@ -153,6 +155,7 @@ if(SDL) install ( TARGETS ${BINARY_NAME} SDL2 + RUNTIME DESTINATION ${PACKAGE_DIR} ) else() # JoyShockLibrary @@ -170,6 +173,7 @@ else() install ( TARGETS ${BINARY_NAME} JoyShockLibrary + RUNTIME DESTINATION ${PACKAGE_DIR} ) endif() diff --git a/JoyShockMapper/include/win32/Gamepad.h b/JoyShockMapper/include/Gamepad.h similarity index 89% rename from JoyShockMapper/include/win32/Gamepad.h rename to JoyShockMapper/include/Gamepad.h index 760aaa8..b69c410 100644 --- a/JoyShockMapper/include/win32/Gamepad.h +++ b/JoyShockMapper/include/Gamepad.h @@ -9,6 +9,12 @@ typedef struct _XUSB_REPORT XUSB_REPORT; typedef struct _DS4_REPORT DS4_REPORT; typedef enum _VIGEM_ERRORS VIGEM_ERRORS; +#if defined(WIN32) +#define VIGEM_CALLBACK _stdcall +#else +#define VIGEM_CALLBACK +#endif + union Indicator { uint8_t led; @@ -40,7 +46,7 @@ class Gamepad ControllerScheme getType() const; private: - static void x360Notification( + static void VIGEM_CALLBACK x360Notification( PVIGEM_CLIENT client, PVIGEM_TARGET target, uint8_t largeMotor, @@ -48,7 +54,7 @@ class Gamepad uint8_t ledNumber, void *userData); - static void ds4Notification( + static void VIGEM_CALLBACK ds4Notification( PVIGEM_CLIENT client, PVIGEM_TARGET target, uint8_t largeMotor, diff --git a/JoyShockMapper/include/JSMAssignment.hpp b/JoyShockMapper/include/JSMAssignment.hpp index 1e24478..9388179 100644 --- a/JoyShockMapper/include/JSMAssignment.hpp +++ b/JoyShockMapper/include/JSMAssignment.hpp @@ -29,45 +29,53 @@ class JSMAssignment : public JSMCommand { smatch results; _ASSERT_EXPR(_parse, L"There is no function defined to parse this command."); - if (arguments.compare(0, 4, "HELP") == 0 && !_help.empty()) + if (arguments.empty()) + { + DisplayCurrentValue(); + } + else if (arguments.compare(0, 4, "HELP") == 0 && !_help.empty()) { // Show help. COUT << _help << endl; } - else if (arguments.empty() || regex_match(arguments, results, regex(R"(\s*=\s*(.*))"))) + else if (regex_match(arguments, results, regex(R"(\s*=\s*(.*))"))) { - string fwd_args(results.empty() ? arguments : results[1].str()); - if (fwd_args.rfind("DEFAULT", 0) == 0) + string assignment(results.empty() ? arguments : results[1].str()); + if (assignment.rfind("DEFAULT", 0) == 0) { _var.Reset(); } - else if (!_parse(this, fwd_args) && !_help.empty()) + else if (!_parse(this, assignment)) { - COUT << _help << endl - << "The "; // Parsing has failed. Show help. + CERR << "Error assigning "; + COUT_INFO << assignment; + CERR << " to " << _displayName << endl; + CERR << "See "; + COUT_INFO << "HELP"; + CERR << " and "; COUT_INFO << "README"; - COUT << " command can lead you to further details on this command." << endl; + CERR << " commands for further details." << endl; } } else if (!_help.empty()) { - COUT << _help << endl - << "The "; // Parsing has failed. Show help. + // Parsing has failed. + CERR << "Error when processing the assignment. See the "; COUT_INFO << "README"; - COUT << " command can lead you to further details on this command." << endl; + CERR << " for details on valid assignment values" << endl; } return true; // Command is completely processed } - static bool ModeshiftParser(ButtonID modeshift, JSMSetting* setting, JSMCommand::ParseDelegate parser, JSMCommand* cmd, in_string argument) + static bool ModeshiftParser(ButtonID modeshift, JSMSetting* setting, JSMCommand::ParseDelegate* parser, JSMCommand* cmd, in_string argument) { if (setting && argument.compare("NONE") == 0) { setting->MarkModeshiftForRemoval(modeshift); - COUT << "Modeshift " << modeshift << "," << setting->_id << " has been removed." << endl; + COUT << "Modeshift " << modeshift << "," << cmd->_name << " has been removed." << endl; return true; } - return parser(cmd, argument); + return (*parser)(cmd, argument); } // The default parser uses the overloaded >> operator to parse @@ -76,17 +84,10 @@ class JSMAssignment : public JSMCommand static bool DefaultParser(JSMCommand* cmd, in_string data) { auto inst = dynamic_cast*>(cmd); - if (data.empty()) - { - //No assignment? Display current assignment - COUT << inst->_displayName << " = " << inst->_var.get() << endl; - return true; - } stringstream ss(data); // Read the value - T value = T(); - ss >> value; + T value(inst->ReadValue(ss)); if (!ss.fail()) { T oldVal = inst->_var; @@ -108,12 +109,25 @@ class JSMAssignment : public JSMCommand return false; } - void DisplayNewValue(T newValue) + virtual void DisplayNewValue(T newValue) { // See Specialization for T=Mapping at the end of this file COUT << _displayName << " has been set to " << newValue << endl; } + virtual void DisplayCurrentValue() + { + COUT << _displayName << " = " << _var.get() << endl; + } + + virtual T ReadValue(stringstream& in) + { + // Default value reader + T value = T(); + in >> value; + return value; + } + virtual unique_ptr GetModifiedCmd(char op, in_string chord) override { stringstream ss(chord); @@ -129,7 +143,7 @@ class JSMAssignment : public JSMCommand //Create Modeshift string name = chord + op + _displayName; unique_ptr chordAssignment(new JSMAssignment(name, *settingVar->AtChord(btn))); - chordAssignment->SetHelp(_help)->SetParser(bind(&JSMAssignment::ModeshiftParser, btn, settingVar, _parse, placeholders::_1, placeholders::_2))->SetTaskOnDestruction(bind(&JSMSetting::ProcessModeshiftRemoval, settingVar, btn)); + chordAssignment->SetHelp(_help)->SetParser(bind(&JSMAssignment::ModeshiftParser, btn, settingVar, &_parse, placeholders::_1, placeholders::_2))->SetTaskOnDestruction(bind(&JSMSetting::ProcessModeshiftRemoval, settingVar, btn)); return chordAssignment; } auto buttonVar = dynamic_cast(&_var); @@ -164,7 +178,6 @@ class JSMAssignment : public JSMCommand } unsigned int _listenerId; - bool _hasListener; public: JSMAssignment(in_string name, in_string displayName, JSMVariable& var, bool inNoListener = false) @@ -172,12 +185,11 @@ class JSMAssignment : public JSMCommand , _var(var) , _displayName(displayName) , _listenerId(0) - , _hasListener(!inNoListener) { // Child Classes assign their own parser. Use bind to convert instance function call // into a static function call. SetParser(&JSMAssignment::DefaultParser); - if (_hasListener) + if (!inNoListener) { _listenerId = _var.AddOnChangeListener(bind(&JSMAssignment::DisplayNewValue, this, placeholders::_1)); } @@ -193,9 +205,14 @@ class JSMAssignment : public JSMCommand { } + JSMAssignment(JSMButton& var) + : JSMAssignment(magic_enum::enum_name(var._id).data(), var) + { + } + virtual ~JSMAssignment() { - if (_hasListener) + if (_listenerId != 0) { _var.RemoveOnChangeListener(_listenerId); } diff --git a/JoyShockMapper/include/JSMVariable.hpp b/JoyShockMapper/include/JSMVariable.hpp index aee254d..7affa04 100644 --- a/JoyShockMapper/include/JSMVariable.hpp +++ b/JoyShockMapper/include/JSMVariable.hpp @@ -42,7 +42,7 @@ class JSMVariable // Default value of the variable. Cannot be changed after construction. const T _defVal; - JSMVariable(T defaultValue = T(0)) + JSMVariable(T defaultValue = T()) : _value(defaultValue) , _onChangeListeners() , _filter(&NoFiltering) // _filter is always valid diff --git a/JoyShockMapper/include/JoyShockLibrary.h b/JoyShockMapper/include/JoyShockLibrary.h index 28ad4b1..c963fab 100644 --- a/JoyShockMapper/include/JoyShockLibrary.h +++ b/JoyShockMapper/include/JoyShockLibrary.h @@ -7,11 +7,11 @@ #define JOY_SHOCK_API // XCode does not need annotating exported functions, so define is empty #endif -#define JS_TYPE_JOYCON_LEFT 1 -#define JS_TYPE_JOYCON_RIGHT 2 -#define JS_TYPE_PRO_CONTROLLER 3 -#define JS_TYPE_DS4 4 -#define JS_TYPE_DS 5 +#define JS_TYPE_JOYCON_LEFT 21 // unused in SDL_GameControllerType +#define JS_TYPE_JOYCON_RIGHT 22 // Unused in SDL_GameControllerType +#define JS_TYPE_PRO_CONTROLLER 5 // SDL_GameControllerType::SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO +#define JS_TYPE_DS4 4 // SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS4 +#define JS_TYPE_DS 7 // SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS5 #define JS_SPLIT_TYPE_LEFT 1 #define JS_SPLIT_TYPE_RIGHT 2 @@ -153,7 +153,8 @@ extern "C" JOY_SHOCK_API void JslDisconnectAndDisposeAll(); extern "C" JOY_SHOCK_API JOY_SHOCK_STATE JslGetSimpleState(int deviceId); extern "C" JOY_SHOCK_API IMU_STATE JslGetIMUState(int deviceId); extern "C" JOY_SHOCK_API MOTION_STATE JslGetMotionState(int deviceId); -extern "C" JOY_SHOCK_API TOUCH_STATE JslGetTouchState(int deviceId); +extern "C" JOY_SHOCK_API TOUCH_STATE JslGetTouchState(int deviceId, bool previous = false); +extern "C" JOY_SHOCK_API bool JslGetTouchpadDimension(int deviceId, int& sizeX, int& sizeY); extern "C" JOY_SHOCK_API int JslGetButtons(int deviceId); diff --git a/JoyShockMapper/include/JoyShockMapper.h b/JoyShockMapper/include/JoyShockMapper.h index 7412693..277927d 100644 --- a/JoyShockMapper/include/JoyShockMapper.h +++ b/JoyShockMapper/include/JoyShockMapper.h @@ -86,7 +86,6 @@ enum class ButtonID L, ZL, MINUS, - CAPTURE, E, S, N, @@ -118,11 +117,12 @@ enum class ButtonID MRING, LEAN_LEFT, LEAN_RIGHT, - TOUCH, // Touch anywhere on the touchpad - ZLF, // = FIRST_ANALOG_TRIGGER - // insert more analog triggers here - ZRF, // = LAST_ANALOG_TRIGGER - SIZE, // Not a button + TOUCH, // Touch anywhere on the touchpad + ZLF, // = FIRST_ANALOG_TRIGGER + CAPTURE, // Full press of touchpad touch + press + // insert more analog triggers here + ZRF, // = LAST_ANALOG_TRIGGER + SIZE, // Not a button }; // Help strings for each button @@ -207,6 +207,8 @@ enum class SettingID LIGHT_BAR, SCROLL_SENS, VIRTUAL_CONTROLLER, + RUMBLE, + TOUCHPAD_DUAL_STAGE_MODE, }; // constexpr are like #define but with respect to typeness @@ -443,12 +445,12 @@ struct FloatXY : public pair { } - inline float x() + inline float x() const { return first; } - inline float y() + inline float y() const { return second; } @@ -460,13 +462,6 @@ struct GyroSettings bool always_off = false; ButtonID button = ButtonID::NONE; GyroIgnoreMode ignore_mode = GyroIgnoreMode::BUTTON; - - GyroSettings() = default; - // This constructor is required to make use of the default value of JSMVariable's constructor - GyroSettings(int dummy) - : GyroSettings() - { - } }; class DigitalButton; diff --git a/JoyShockMapper/src/CmdRegistry.cpp b/JoyShockMapper/src/CmdRegistry.cpp index 5ab7e0b..ed39f60 100644 --- a/JoyShockMapper/src/CmdRegistry.cpp +++ b/JoyShockMapper/src/CmdRegistry.cpp @@ -61,7 +61,11 @@ CmdRegistry::CmdRegistry() bool CmdRegistry::loadConfigFile(string fileName) { // https://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring - + auto comment = fileName.find_first_of('#'); + if (comment != string::npos) + { + fileName = fileName.substr(0, comment - 1); + } // Trim away quotation marks from drag and drop if (*fileName.begin() == '\"' && *(fileName.end() - 1) == '\"') fileName = fileName.substr(1, fileName.size() - 2); @@ -227,7 +231,9 @@ void CmdRegistry::processLine(const string& line) if (!hasProcessed) { - CERR << "Unrecognized command: \"" << trimmedLine << "\"\nEnter HELP to display all commands." << endl; + CERR << "Unrecognized command: \"" << trimmedLine << "\"\nEnter "; + COUT_INFO << "HELP"; + CERR << " to display all commands." << endl; } } // else ignore empty lines @@ -262,15 +268,10 @@ bool JSMMacro::DefaultParser(JSMCommand* cmd, in_string arguments) auto macroCmd = static_cast(cmd); // Developper protection to remind you to set a parser. _ASSERT_EXPR(macroCmd->_macro, L"No Macro was set for this command."); - if (arguments.compare(0, 4, "HELP") == 0 && !macroCmd->_help.empty()) + if (!macroCmd->_macro(macroCmd, arguments) && !macroCmd->_help.empty()) { - // Show help. COUT << macroCmd->_help << endl; - } - else if (!macroCmd->_macro(macroCmd, arguments) && !macroCmd->_help.empty()) - { - COUT << macroCmd->_help << endl - << "The "; // Parsing has failed. Show help. + COUT << "The "; // Parsing has failed. Show help. COUT_INFO << "README"; COUT << " command can lead you to further details on this command." << endl; } diff --git a/JoyShockMapper/src/JoyShockLibrary.cpp b/JoyShockMapper/src/JoyShockLibrary.cpp deleted file mode 100644 index f2b9bc5..0000000 --- a/JoyShockMapper/src/JoyShockLibrary.cpp +++ /dev/null @@ -1,405 +0,0 @@ -#include "JoyShockLibrary.h" -#include "JSMVariable.hpp" -#include "SDL.h" -#include -#include -#define INCLUDE_MATH_DEFINES -#include // M_PI - -struct ControllerDevice; - -static std::map _controllerMap; -bool keep_polling = true; -class Joyshock; -void (*g_callback)(int, JOY_SHOCK_STATE, JOY_SHOCK_STATE, IMU_STATE, IMU_STATE, float); - -std::mutex controller_lock; - -extern JSMVariable tick_time; - -static int pollDevices(void *obj) -{ - while (keep_polling) - { - SDL_Delay(tick_time.get()); - - std::lock_guard guard(controller_lock); - for (auto iter = _controllerMap.begin(); iter != _controllerMap.end(); ++iter) - { - SDL_GameControllerUpdate(); - JOY_SHOCK_STATE dummy1; - IMU_STATE dummy2; - memset(&dummy1, 0, sizeof(dummy1)); - memset(&dummy2, 0, sizeof(dummy2)); - g_callback(iter->first, dummy1, dummy1, dummy2, dummy2, tick_time.get()); - } - } - - return 1; -} - -struct ControllerDevice -{ - ~ControllerDevice() - { - SDL_GameControllerClose(_sdlController); - } - - inline bool isValid() - { - return _sdlController == nullptr; - } - bool has_gyro = false; - bool has_accel = false; - int split_type = JS_SPLIT_TYPE_FULL; - SDL_GameController *_sdlController = nullptr; -}; - -int JslConnectDevices() -{ - return SDL_NumJoysticks(); -} - -int JslGetConnectedDeviceHandles(int *deviceHandleArray, int size) -{ - std::lock_guard guard(controller_lock); - auto iter = _controllerMap.begin(); - while (iter != _controllerMap.end()) - { - delete iter->second; - iter = _controllerMap.erase(iter); - } - for (int i = 0; i < size; i++) - { - ControllerDevice *device = new ControllerDevice(); - if (!SDL_IsGameController(i)) - { - continue; - } - device->_sdlController = SDL_GameControllerOpen(i); - - if (SDL_GameControllerHasSensor(device->_sdlController, SDL_SENSOR_GYRO)) - { - device->has_gyro = true; - SDL_GameControllerSetSensorEnabled(device->_sdlController, SDL_SENSOR_GYRO, SDL_TRUE); - } - - if (SDL_GameControllerHasSensor(device->_sdlController, SDL_SENSOR_ACCEL)) - { - device->has_accel = true; - SDL_GameControllerSetSensorEnabled(device->_sdlController, SDL_SENSOR_ACCEL, SDL_TRUE); - } - - int vid = SDL_GameControllerGetVendor(device->_sdlController); - int pid = SDL_GameControllerGetProduct(device->_sdlController); - if (vid == 0x057e) - { - if (pid == 0x2006) - { - device->split_type = JS_SPLIT_TYPE_LEFT; - } - else if (pid == 0x2007) - { - device->split_type = JS_SPLIT_TYPE_RIGHT; - } - } - int handle = i + 1; - deviceHandleArray[i] = handle; - _controllerMap[handle] = device; - } - return _controllerMap.size(); -} - -void JslDisconnectAndDisposeAll() -{ - keep_polling = false; - controller_lock.lock(); - auto iter = _controllerMap.begin(); - while (iter != _controllerMap.end()) - { - delete iter->second; - iter = _controllerMap.erase(iter); - } - controller_lock.unlock(); - SDL_Delay(200); - - SDL_Quit(); -} - -JOY_SHOCK_STATE JslGetSimpleState(int deviceId) -{ - return JOY_SHOCK_STATE(); -} - -IMU_STATE JslGetIMUState(int deviceId) -{ - IMU_STATE imuState; - memset(&imuState, 0, sizeof(imuState)); - if (_controllerMap[deviceId]->has_gyro) - { - array gyro; - SDL_GameControllerGetSensorData(_controllerMap[deviceId]->_sdlController, SDL_SENSOR_GYRO, &gyro[0], 3); - constexpr float toDegPerSec = 180.f / M_PI; - imuState.gyroX = gyro[0] * toDegPerSec; - imuState.gyroY = gyro[1] * toDegPerSec; - imuState.gyroZ = gyro[2] * toDegPerSec; - } - if (_controllerMap[deviceId]->has_accel) - { - array accel; - SDL_GameControllerGetSensorData(_controllerMap[deviceId]->_sdlController, SDL_SENSOR_ACCEL, &accel[0], 3); - constexpr float toGs = 1.f / 9.8f; - imuState.accelX = accel[0] * toGs; - imuState.accelY = accel[1] * toGs; - imuState.accelZ = accel[2] * toGs; - } - return imuState; -} - -MOTION_STATE JslGetMotionState(int deviceId) -{ - return MOTION_STATE(); -} - -TOUCH_STATE JslGetTouchState(int deviceId) -{ - return TOUCH_STATE(); -} - -int JslGetButtons(int deviceId) -{ - static const std::map sdl2jsl = { - { SDL_CONTROLLER_BUTTON_A, JSOFFSET_S }, - { SDL_CONTROLLER_BUTTON_B, JSOFFSET_E }, - { SDL_CONTROLLER_BUTTON_X, JSOFFSET_W }, - { SDL_CONTROLLER_BUTTON_Y, JSOFFSET_N }, - { SDL_CONTROLLER_BUTTON_BACK, JSOFFSET_MINUS }, - { SDL_CONTROLLER_BUTTON_GUIDE, JSOFFSET_HOME }, - { SDL_CONTROLLER_BUTTON_START, JSOFFSET_PLUS }, - { SDL_CONTROLLER_BUTTON_LEFTSTICK, JSOFFSET_LCLICK }, - { SDL_CONTROLLER_BUTTON_RIGHTSTICK, JSOFFSET_RCLICK }, - { SDL_CONTROLLER_BUTTON_LEFTSHOULDER, JSOFFSET_L }, - { SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, JSOFFSET_R }, - { SDL_CONTROLLER_BUTTON_DPAD_UP, JSOFFSET_UP }, - { SDL_CONTROLLER_BUTTON_DPAD_DOWN, JSOFFSET_DOWN }, - { SDL_CONTROLLER_BUTTON_DPAD_LEFT, JSOFFSET_LEFT }, - { SDL_CONTROLLER_BUTTON_DPAD_RIGHT, JSOFFSET_RIGHT }, - { SDL_CONTROLLER_BUTTON_PADDLE2, JSOFFSET_SL }, // LSL - { SDL_CONTROLLER_BUTTON_PADDLE4, JSOFFSET_SR }, // LSR - { SDL_CONTROLLER_BUTTON_PADDLE3, JSOFFSET_SL }, // RSL - { SDL_CONTROLLER_BUTTON_PADDLE1, JSOFFSET_SR }, // RSR - }; - int buttons = 0; - for (auto pair : sdl2jsl) - { - buttons |= SDL_GameControllerGetButton(_controllerMap[deviceId]->_sdlController, SDL_GameControllerButton(pair.first)) > 0 ? 1 << pair.second : 0; - } - switch (_controllerMap[deviceId]->split_type) - { - case SDL_CONTROLLER_TYPE_PS4: - case SDL_CONTROLLER_TYPE_PS5: - buttons |= SDL_GameControllerGetButton(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_BUTTON_TOUCHPAD) > 0 ? 1 << JSOFFSET_CAPTURE : 0; - break; - default: - buttons |= SDL_GameControllerGetButton(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_BUTTON_MISC1) > 0 ? 1 << JSOFFSET_CAPTURE : 0; - break; - } - return buttons; -} - -float JslGetLeftX(int deviceId) -{ - return SDL_GameControllerGetAxis(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_LEFTX) / (float)SDL_JOYSTICK_AXIS_MAX; -} - -float JslGetLeftY(int deviceId) -{ - return SDL_GameControllerGetAxis(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_LEFTY) / (float)SDL_JOYSTICK_AXIS_MAX; - ; -} - -float JslGetRightX(int deviceId) -{ - return SDL_GameControllerGetAxis(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_RIGHTX) / (float)SDL_JOYSTICK_AXIS_MAX; -} - -float JslGetRightY(int deviceId) -{ - return SDL_GameControllerGetAxis(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_RIGHTY) / (float)SDL_JOYSTICK_AXIS_MAX; -} - -float JslGetLeftTrigger(int deviceId) -{ - return SDL_GameControllerGetAxis(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_TRIGGERLEFT) / (float)SDL_JOYSTICK_AXIS_MAX; -} - -float JslGetRightTrigger(int deviceId) -{ - return SDL_GameControllerGetAxis(_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) / (float)SDL_JOYSTICK_AXIS_MAX; -} - -float JslGetGyroX(int deviceId) -{ - if (_controllerMap[deviceId]->has_gyro) - { - float rawGyro[3]; - SDL_GameControllerGetSensorData(_controllerMap[deviceId]->_sdlController, SDL_SENSOR_GYRO, rawGyro, 3); - } - return float(); -} - -float JslGetGyroY(int deviceId) -{ - return float(); -} - -float JslGetGyroZ(int deviceId) -{ - return float(); -} - -float JslGetAccelX(int deviceId) -{ - return float(); -} - -float JslGetAccelY(int deviceId) -{ - return float(); -} - -float JslGetAccelZ(int deviceId) -{ - return float(); -} - -int JslGetTouchId(int deviceId, bool secondTouch) -{ - return int(); -} - -bool JslGetTouchDown(int deviceId, bool secondTouch) -{ - uint8_t touchState = 0; - if (SDL_GameControllerGetTouchpadFinger(_controllerMap[deviceId]->_sdlController, 0, secondTouch ? 1 : 0, &touchState, nullptr, nullptr, nullptr) == 0) - { - return touchState != 0; - } - return false; -} - -float JslGetTouchX(int deviceId, bool secondTouch) -{ - float x = 0; - if (SDL_GameControllerGetTouchpadFinger(_controllerMap[deviceId]->_sdlController, 0, secondTouch ? 1 : 0, nullptr, nullptr, &x, nullptr) == 0) - { - return x; - } - return x; -} - -float JslGetTouchY(int deviceId, bool secondTouch) -{ - float y = 0; - if (SDL_GameControllerGetTouchpadFinger(_controllerMap[deviceId]->_sdlController, 0, secondTouch ? 1 : 0, nullptr, nullptr, &y, nullptr) == 0) - { - return y; - } - return y; -} - -float JslGetStickStep(int deviceId) -{ - return float(); -} - -float JslGetTriggerStep(int deviceId) -{ - return float(); -} - -float JslGetPollRate(int deviceId) -{ - return float(); -} - -void JslResetContinuousCalibration(int deviceId) -{ - return void(); -} - -void JslStartContinuousCalibration(int deviceId) -{ - return void(); -} - -void JslPauseContinuousCalibration(int deviceId) -{ - return void(); -} - -void JslGetCalibrationOffset(int deviceId, float &xOffset, float &yOffset, float &zOffset) -{ - return void(); -} - -void JslSetCalibrationOffset(int deviceId, float xOffset, float yOffset, float zOffset) -{ - return void(); -} - -void JslSetCallback(void (*callback)(int, JOY_SHOCK_STATE, JOY_SHOCK_STATE, IMU_STATE, IMU_STATE, float)) -{ - SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1"); - SDL_Init(SDL_INIT_GAMECONTROLLER); - g_callback = callback; - SDL_Thread *controllerThread = SDL_CreateThread(pollDevices, "Poll Devices", nullptr); - SDL_DetachThread(controllerThread); -} - -void JslSetTouchCallback(void (*callback)(int, TOUCH_STATE, TOUCH_STATE, float)) -{ - return void(); -} - -int JslGetControllerType(int deviceId) -{ - return SDL_GameControllerGetType(_controllerMap[deviceId]->_sdlController); -} - -int JslGetControllerSplitType(int deviceId) -{ - return _controllerMap[deviceId]->split_type; -} - -int JslGetControllerColour(int deviceId) -{ - return int(); -} - -void JslSetLightColour(int deviceId, int colour) -{ - union - { - uint32_t raw; - uint8_t argb[4]; - } uColour; - uColour.raw = colour; - SDL_GameControllerSetLED(_controllerMap[deviceId]->_sdlController, uColour.argb[2], uColour.argb[1], uColour.argb[0]); -} - -void JslSetRumble(int deviceId, int smallRumble, int bigRumble) -{ - SDL_GameControllerRumble(_controllerMap[deviceId]->_sdlController, smallRumble << 8, bigRumble << 8, tick_time.get() + 1); -} - -void JslSetPlayerNumber(int deviceId, int number) -{ - SDL_GameControllerSetPlayerIndex(_controllerMap[deviceId]->_sdlController, number); -} diff --git a/JoyShockMapper/src/SDL2Wrapper.cpp b/JoyShockMapper/src/SDL2Wrapper.cpp new file mode 100644 index 0000000..4ed5397 --- /dev/null +++ b/JoyShockMapper/src/SDL2Wrapper.cpp @@ -0,0 +1,498 @@ +#include "JoyShockLibrary.h" +#include "JSMVariable.hpp" +#include "SDL.h" +#include +#include +#include +#define INCLUDE_MATH_DEFINES +#include // M_PI +#include + +extern JSMVariable tick_time; // defined in main.cc + +struct ControllerDevice +{ + ControllerDevice(int id) + { + if (SDL_IsGameController(id)) + { + _sdlController = SDL_GameControllerOpen(id); + + if (SDL_GameControllerHasSensor(_sdlController, SDL_SENSOR_GYRO)) + { + _has_gyro = true; + SDL_GameControllerSetSensorEnabled(_sdlController, SDL_SENSOR_GYRO, SDL_TRUE); + } + + if (SDL_GameControllerHasSensor(_sdlController, SDL_SENSOR_ACCEL)) + { + _has_accel = true; + SDL_GameControllerSetSensorEnabled(_sdlController, SDL_SENSOR_ACCEL, SDL_TRUE); + } + + int vid = SDL_GameControllerGetVendor(_sdlController); + int pid = SDL_GameControllerGetProduct(_sdlController); + if (vid == 0x057e) + { + if (pid == 0x2006) + { + _split_type = JS_SPLIT_TYPE_LEFT; + } + else if (pid == 0x2007) + { + _split_type = JS_SPLIT_TYPE_RIGHT; + } + } + } + } + + virtual ~ControllerDevice() + { + SDL_GameControllerClose(_sdlController); + } + + inline bool isValid() + { + return _sdlController != nullptr; + } + + bool _has_gyro = true; + bool _has_accel = true; + int _split_type = JS_SPLIT_TYPE_FULL; + uint16_t _small_rumble = 0; + uint16_t _big_rumble = 0; + SDL_GameController *_sdlController = nullptr; +}; + +struct SdlInstance +{ +private: + SdlInstance() + { + SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1"); + SDL_Init(SDL_INIT_GAMECONTROLLER); + } + +public: + static const unique_ptr _inst; + + ~SdlInstance() + { + SDL_Quit(); + } + + static int pollDevices(void *obj) + { + while (SdlInstance::_inst->keep_polling) + { + SDL_Delay(tick_time.get()); + + std::lock_guard guard(SdlInstance::_inst->controller_lock); + for (auto iter = SdlInstance::_inst->_controllerMap.begin(); iter != SdlInstance::_inst->_controllerMap.end(); ++iter) + { + SDL_GameControllerUpdate(); + if (SdlInstance::_inst->g_callback) + { + JOY_SHOCK_STATE dummy1; + IMU_STATE dummy2; + memset(&dummy1, 0, sizeof(dummy1)); + memset(&dummy2, 0, sizeof(dummy2)); + SdlInstance::_inst->g_callback(iter->first, dummy1, dummy1, dummy2, dummy2, tick_time.get()); + } + if (SdlInstance::_inst->g_touch_callback) + { + TOUCH_STATE touch = JslGetTouchState(iter->first), dummy3; + memset(&dummy3, 0, sizeof(dummy3)); + SdlInstance::_inst->g_touch_callback(iter->first, touch, dummy3, tick_time.get()); + } + // Perform rumble + SDL_GameControllerRumble(iter->second->_sdlController, iter->second->_small_rumble, iter->second->_big_rumble, tick_time.get() + 1); + } + } + + return 1; + } + + map _controllerMap; + void (*g_callback)(int, JOY_SHOCK_STATE, JOY_SHOCK_STATE, IMU_STATE, IMU_STATE, float) = nullptr; + void (*g_touch_callback)(int, TOUCH_STATE, TOUCH_STATE, float) = nullptr; + atomic_bool keep_polling = false; + std::mutex controller_lock; +}; + +const unique_ptr SdlInstance::_inst(new SdlInstance); + +int JslConnectDevices() +{ + bool isFalse = false; + if (SdlInstance::_inst->keep_polling.compare_exchange_strong(isFalse, true)) + { + // keep polling was false! It is set to true now. + SDL_Thread *controller_polling_thread = SDL_CreateThread(&SdlInstance::pollDevices, "Poll Devices", nullptr); + SDL_DetachThread(controller_polling_thread); + } + SDL_GameControllerUpdate(); // Refresh driver listing + return SDL_NumJoysticks(); +} + +int JslGetConnectedDeviceHandles(int *deviceHandleArray, int size) +{ + std::lock_guard guard(SdlInstance::_inst->controller_lock); + auto iter = SdlInstance::_inst->_controllerMap.begin(); + while (iter != SdlInstance::_inst->_controllerMap.end()) + { + delete iter->second; + iter = SdlInstance::_inst->_controllerMap.erase(iter); + } + for (int i = 0; i < size; i++) + { + ControllerDevice *device = new ControllerDevice(i); + if (device->isValid()) + { + deviceHandleArray[i] = i + 1; + SdlInstance::_inst->_controllerMap[deviceHandleArray[i]] = device; + } + } + return SdlInstance::_inst->_controllerMap.size(); +} + +void JslDisconnectAndDisposeAll() +{ + lock_guard guard(SdlInstance::_inst->controller_lock); + SdlInstance::_inst->keep_polling = false; + SdlInstance::_inst->g_callback = nullptr; + SdlInstance::_inst->g_touch_callback = nullptr; + auto iter = SdlInstance::_inst->_controllerMap.begin(); + while (iter != SdlInstance::_inst->_controllerMap.end()) + { + delete iter->second; + iter = SdlInstance::_inst->_controllerMap.erase(iter); + } + SDL_Delay(200); +} + +JOY_SHOCK_STATE JslGetSimpleState(int deviceId) +{ + return JOY_SHOCK_STATE(); +} + +IMU_STATE JslGetIMUState(int deviceId) +{ + IMU_STATE imuState; + memset(&imuState, 0, sizeof(imuState)); + if (SdlInstance::_inst->_controllerMap[deviceId]->_has_gyro) + { + array gyro; + if (SDL_GameControllerGetSensorData(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_SENSOR_GYRO, &gyro[0], 3) == 0) + { + constexpr float toDegPerSec = 180.f / M_PI; + imuState.gyroX = gyro[0] * toDegPerSec; + imuState.gyroY = gyro[1] * toDegPerSec; + imuState.gyroZ = gyro[2] * toDegPerSec; + } + } + if (SdlInstance::_inst->_controllerMap[deviceId]->_has_accel) + { + array accel; + if (SDL_GameControllerGetSensorData(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_SENSOR_ACCEL, &accel[0], 3) == 0) + { + constexpr float toGs = 1.f / 9.8f; + imuState.accelX = accel[0] * toGs; + imuState.accelY = accel[1] * toGs; + imuState.accelZ = accel[2] * toGs; + } + } + return imuState; +} + +MOTION_STATE JslGetMotionState(int deviceId) +{ + return MOTION_STATE(); +} + +TOUCH_STATE JslGetTouchState(int deviceId, bool previous) +{ + uint8_t state0 = 0, state1 = 0; + TOUCH_STATE state; + memset(&state, 0, sizeof(TOUCH_STATE)); + if (SDL_GameControllerGetTouchpadFinger(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, 0, 0, &state0, &state.t0X, &state.t0Y, nullptr) == 0 && SDL_GameControllerGetTouchpadFinger(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, 0, 1, &state1, &state.t1X, &state.t1Y, nullptr) == 0) + { + state.t0Down = state0 == SDL_PRESSED; + state.t1Down = state1 == SDL_PRESSED; + } + return state; +} + +bool JslGetTouchpadDimension(int deviceId, int &sizeX, int &sizeY) +{ + // I am assuming a single touchpad (or all touchpads are the same dimension)? + auto *jc = SdlInstance::_inst->_controllerMap[deviceId]; + if (jc != nullptr) + { + switch (JslGetControllerType(deviceId)) + { + case JS_TYPE_DS4: + case JS_TYPE_DS: + // Matching SDL2 resolution + sizeX = 1920; + sizeY = 920; + break; + default: + sizeX = 0; + sizeY = 0; + break; + } + return true; + } + return false; +} + +int JslGetButtons(int deviceId) +{ + static const std::map sdl2jsl = { + { SDL_CONTROLLER_BUTTON_A, JSOFFSET_S }, + { SDL_CONTROLLER_BUTTON_B, JSOFFSET_E }, + { SDL_CONTROLLER_BUTTON_X, JSOFFSET_W }, + { SDL_CONTROLLER_BUTTON_Y, JSOFFSET_N }, + { SDL_CONTROLLER_BUTTON_BACK, JSOFFSET_MINUS }, + { SDL_CONTROLLER_BUTTON_GUIDE, JSOFFSET_HOME }, + { SDL_CONTROLLER_BUTTON_START, JSOFFSET_PLUS }, + { SDL_CONTROLLER_BUTTON_LEFTSTICK, JSOFFSET_LCLICK }, + { SDL_CONTROLLER_BUTTON_RIGHTSTICK, JSOFFSET_RCLICK }, + { SDL_CONTROLLER_BUTTON_LEFTSHOULDER, JSOFFSET_L }, + { SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, JSOFFSET_R }, + { SDL_CONTROLLER_BUTTON_DPAD_UP, JSOFFSET_UP }, + { SDL_CONTROLLER_BUTTON_DPAD_DOWN, JSOFFSET_DOWN }, + { SDL_CONTROLLER_BUTTON_DPAD_LEFT, JSOFFSET_LEFT }, + { SDL_CONTROLLER_BUTTON_DPAD_RIGHT, JSOFFSET_RIGHT }, + { SDL_CONTROLLER_BUTTON_PADDLE2, JSOFFSET_SL }, // LSL + { SDL_CONTROLLER_BUTTON_PADDLE4, JSOFFSET_SR }, // LSR + { SDL_CONTROLLER_BUTTON_PADDLE3, JSOFFSET_SL }, // RSL + { SDL_CONTROLLER_BUTTON_PADDLE1, JSOFFSET_SR }, // RSR + }; + int buttons = 0; + for (auto pair : sdl2jsl) + { + buttons |= SDL_GameControllerGetButton(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_GameControllerButton(pair.first)) > 0 ? 1 << pair.second : 0; + } + switch (JslGetControllerType(deviceId)) + { + case SDL_CONTROLLER_TYPE_PS4: + case SDL_CONTROLLER_TYPE_PS5: + buttons |= SDL_GameControllerGetButton(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_BUTTON_TOUCHPAD) > 0 ? 1 << JSOFFSET_CAPTURE : 0; + break; + default: + buttons |= SDL_GameControllerGetButton(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_BUTTON_MISC1) > 0 ? 1 << JSOFFSET_CAPTURE : 0; + break; + } + return buttons; +} + +float JslGetLeftX(int deviceId) +{ + return SDL_GameControllerGetAxis(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_LEFTX) / (float)SDL_JOYSTICK_AXIS_MAX; +} + +float JslGetLeftY(int deviceId) +{ + return SDL_GameControllerGetAxis(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_LEFTY) / (float)SDL_JOYSTICK_AXIS_MAX; + ; +} + +float JslGetRightX(int deviceId) +{ + return SDL_GameControllerGetAxis(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_RIGHTX) / (float)SDL_JOYSTICK_AXIS_MAX; +} + +float JslGetRightY(int deviceId) +{ + return SDL_GameControllerGetAxis(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_RIGHTY) / (float)SDL_JOYSTICK_AXIS_MAX; +} + +float JslGetLeftTrigger(int deviceId) +{ + return SDL_GameControllerGetAxis(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_TRIGGERLEFT) / (float)SDL_JOYSTICK_AXIS_MAX; +} + +float JslGetRightTrigger(int deviceId) +{ + return SDL_GameControllerGetAxis(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) / (float)SDL_JOYSTICK_AXIS_MAX; +} + +float JslGetGyroX(int deviceId) +{ + if (SdlInstance::_inst->_controllerMap[deviceId]->_has_gyro) + { + float rawGyro[3]; + SDL_GameControllerGetSensorData(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, SDL_SENSOR_GYRO, rawGyro, 3); + } + return float(); +} + +float JslGetGyroY(int deviceId) +{ + return float(); +} + +float JslGetGyroZ(int deviceId) +{ + return float(); +} + +float JslGetAccelX(int deviceId) +{ + return float(); +} + +float JslGetAccelY(int deviceId) +{ + return float(); +} + +float JslGetAccelZ(int deviceId) +{ + return float(); +} + +int JslGetTouchId(int deviceId, bool secondTouch) +{ + return int(); +} + +bool JslGetTouchDown(int deviceId, bool secondTouch) +{ + uint8_t touchState = 0; + if (SDL_GameControllerGetTouchpadFinger(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, 0, secondTouch ? 1 : 0, &touchState, nullptr, nullptr, nullptr) == 0) + { + return touchState == SDL_PRESSED; + } + return false; +} + +float JslGetTouchX(int deviceId, bool secondTouch) +{ + float x = 0; + if (SDL_GameControllerGetTouchpadFinger(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, 0, secondTouch ? 1 : 0, nullptr, nullptr, &x, nullptr) == 0) + { + return x; + } + return x; +} + +float JslGetTouchY(int deviceId, bool secondTouch) +{ + float y = 0; + if (SDL_GameControllerGetTouchpadFinger(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, 0, secondTouch ? 1 : 0, nullptr, nullptr, &y, nullptr) == 0) + { + return y; + } + return y; +} + +float JslGetStickStep(int deviceId) +{ + return float(); +} + +float JslGetTriggerStep(int deviceId) +{ + return float(); +} + +float JslGetPollRate(int deviceId) +{ + return float(); +} + +void JslResetContinuousCalibration(int deviceId) +{ + return void(); +} + +void JslStartContinuousCalibration(int deviceId) +{ + return void(); +} + +void JslPauseContinuousCalibration(int deviceId) +{ + return void(); +} + +void JslGetCalibrationOffset(int deviceId, float &xOffset, float &yOffset, float &zOffset) +{ + return void(); +} + +void JslSetCalibrationOffset(int deviceId, float xOffset, float yOffset, float zOffset) +{ + return void(); +} + +void JslSetCallback(void (*callback)(int, JOY_SHOCK_STATE, JOY_SHOCK_STATE, IMU_STATE, IMU_STATE, float)) +{ + std::lock_guard guard(SdlInstance::_inst->controller_lock); + SdlInstance::_inst->g_callback = callback; +} + +void JslSetTouchCallback(void (*callback)(int, TOUCH_STATE, TOUCH_STATE, float)) +{ + std::lock_guard guard(SdlInstance::_inst->controller_lock); + SdlInstance::_inst->g_touch_callback = callback; +} + +int JslGetControllerType(int deviceId) +{ + int type = SDL_GameControllerGetType(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController); + if (type == JS_TYPE_PRO_CONTROLLER) + { + if (JslGetControllerSplitType(deviceId) == JS_SPLIT_TYPE_LEFT) + return JS_TYPE_JOYCON_LEFT; + else if (JslGetControllerSplitType(deviceId) == JS_SPLIT_TYPE_RIGHT) + return JS_TYPE_JOYCON_RIGHT; + } + return type; +} + +int JslGetControllerSplitType(int deviceId) +{ + return SdlInstance::_inst->_controllerMap[deviceId]->_split_type; +} + +int JslGetControllerColour(int deviceId) +{ + return int(); +} + +void JslSetLightColour(int deviceId, int colour) +{ + if (SDL_GameControllerHasLED(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController)) + { + union + { + uint32_t raw; + uint8_t argb[4]; + } uColour; + uColour.raw = colour; + SDL_GameControllerSetLED(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, uColour.argb[2], uColour.argb[1], uColour.argb[0]); + } +} + +void JslSetRumble(int deviceId, int smallRumble, int bigRumble) +{ + // Rumble command needs to be sent at every poll in SDL, so the next value is set here and the actual call + // is done after the callback return + SdlInstance::_inst->_controllerMap[deviceId]->_small_rumble = clamp(smallRumble, 0, 255) << 8; + SdlInstance::_inst->_controllerMap[deviceId]->_big_rumble = clamp(bigRumble, 0, 255) << 8; +} + +void JslSetPlayerNumber(int deviceId, int number) +{ + SDL_GameControllerSetPlayerIndex(SdlInstance::_inst->_controllerMap[deviceId]->_sdlController, number); +} diff --git a/JoyShockMapper/src/linux/Gamepad.cpp b/JoyShockMapper/src/linux/Gamepad.cpp new file mode 100644 index 0000000..77d2d50 --- /dev/null +++ b/JoyShockMapper/src/linux/Gamepad.cpp @@ -0,0 +1,95 @@ +#include "Gamepad.h" + +struct _XUSB_REPORT +{ +}; + +struct _DS4_REPORT +{ +}; + +void Gamepad::x360Notification( + PVIGEM_CLIENT client, + PVIGEM_TARGET target, + uint8_t largeMotor, + uint8_t smallMotor, + uint8_t ledNumber, + void *userData) +{ +} + +void Gamepad::ds4Notification( + PVIGEM_CLIENT client, + PVIGEM_TARGET target, + uint8_t largeMotor, + uint8_t smallMotor, + Indicator lightbarColor, + void *userData) +{ +} + +Gamepad::Gamepad(ControllerScheme scheme) + : _stateX360() + , _stateDS4() + , _notification() +{ +} + +Gamepad::Gamepad(ControllerScheme scheme, Callback notification) + : Gamepad(scheme) +{ +} + +Gamepad::~Gamepad() +{ +} + +void Gamepad::init_x360() +{ +} + +void Gamepad::init_ds4() +{ +} + +bool Gamepad::isInitialized(std::string *errorMsg) +{ + return false; +} + +void Gamepad::setButton(KeyCode btn, bool pressed) +{ +} + +ControllerScheme Gamepad::getType() const +{ + return ControllerScheme::INVALID; +} + +void Gamepad::setButtonX360(KeyCode btn, bool pressed) +{ +} + +void Gamepad::setButtonDS4(KeyCode btn, bool pressed) +{ +} + +void Gamepad::setLeftStick(float x, float y) +{ +} + +void Gamepad::setRightStick(float x, float y) +{ +} + +void Gamepad::setLeftTrigger(float val) +{ +} + +void Gamepad::setRightTrigger(float val) +{ +} + +void Gamepad::update() +{ +} diff --git a/JoyShockMapper/src/main.cpp b/JoyShockMapper/src/main.cpp index b6ef754..74442b9 100644 --- a/JoyShockMapper/src/main.cpp +++ b/JoyShockMapper/src/main.cpp @@ -6,7 +6,7 @@ #include "TrayIcon.h" #include "JSMAssignment.hpp" #include "quatMaths.cpp" -#include "win32/Gamepad.h" +#include "Gamepad.h" #include #include @@ -21,6 +21,7 @@ const Mapping Mapping::NO_MAPPING = Mapping("NONE"); function Mapping::_isCommandValid = function(); class JoyShock; +void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE lastState, IMU_STATE imuState, IMU_STATE lastImuState, float deltaTime); // Contains all settings that can be modeshifted. They should be accessed only via Joyshock::getSetting JSMSetting left_stick_mode = JSMSetting(SettingID::LEFT_STICK_MODE, StickMode::NO_MOUSE); @@ -36,7 +37,6 @@ JSMSetting joycon_gyro_mask = JSMSetting(SettingID::JOYC JSMSetting joycon_motion_mask = JSMSetting(SettingID::JOYCON_MOTION_MASK, JoyconMask::IGNORE_RIGHT); JSMSetting zlMode = JSMSetting(SettingID::ZL_MODE, TriggerMode::NO_FULL); JSMSetting zrMode = JSMSetting(SettingID::ZR_MODE, TriggerMode::NO_FULL); -; JSMSetting flick_snap_mode = JSMSetting(SettingID::FLICK_SNAP_MODE, FlickSnapMode::NONE); JSMSetting min_gyro_sens = JSMSetting(SettingID::MIN_GYRO_SENS, { 0.0f, 0.0f }); JSMSetting max_gyro_sens = JSMSetting(SettingID::MAX_GYRO_SENS, { 0.0f, 0.0f }); @@ -86,6 +86,8 @@ JSMSetting scroll_sens = JSMSetting(SettingID::SCROLL_SENS, { JSMVariable autoloadSwitch = JSMVariable(Switch::ON); JSMVariable hide_minimized = JSMVariable(Switch::OFF); JSMVariable virtual_controller = JSMVariable(ControllerScheme::NONE); +JSMSetting touch_ds_mode = JSMSetting(SettingID::TOUCHPAD_DUAL_STAGE_MODE, TriggerMode::NO_SKIP); +JSMSetting rumble_enable = JSMSetting(SettingID::RUMBLE, Switch::ON); JSMVariable currentWorkingDir = JSMVariable(GetCWD()); vector mappings; // array enables use of for each loop and other i/f @@ -762,6 +764,24 @@ bool Mapping::AddMapping(KeyCode key, EventModifier evtMod, ActionModifier actMo } BtnEvent applyEvt, releaseEvt; + switch (actMod) + { + case ActionModifier::Toggle: + apply = bind(&DigitalButton::ApplyButtonToggle, placeholders::_1, key, apply, release); + release = OnEventAction(); + break; + case ActionModifier::Instant: + { + OnEventAction action2 = bind(&DigitalButton::RegisterInstant, placeholders::_1, applyEvt); + apply = bind(&Mapping::RunBothActions, placeholders::_1, apply, action2); + releaseEvt = BtnEvent::OnInstantRelease; + } + break; + case ActionModifier::INVALID: + return false; + // None applies no modification... Hey! + } + switch (evtMod) { case EventModifier::StartPress: @@ -784,30 +804,13 @@ bool Mapping::AddMapping(KeyCode key, EventModifier evtMod, ActionModifier actMo case EventModifier::TurboPress: applyEvt = BtnEvent::OnTurbo; releaseEvt = BtnEvent::OnTurbo; + InsertEventMapping(BtnEvent::OnRelease, release); // On turbo you also need to clear the turbo on release break; default: // EventModifier::INVALID or None return false; } - switch (actMod) - { - case ActionModifier::Toggle: - apply = bind(&DigitalButton::ApplyButtonToggle, placeholders::_1, key, apply, release); - release = OnEventAction(); - break; - case ActionModifier::Instant: - { - OnEventAction action2 = bind(&DigitalButton::RegisterInstant, placeholders::_1, applyEvt); - apply = bind(&Mapping::RunBothActions, placeholders::_1, apply, action2); - releaseEvt = BtnEvent::OnInstantRelease; - } - break; - case ActionModifier::INVALID: - return false; - // None applies no modification... Hey! - } - - // Insert release first because in turbo's case apply and release are the same but we want release to apply first + // Insert release first because in turbo's case apply and release are the same but we want release to happen first InsertEventMapping(releaseEvt, release); InsertEventMapping(applyEvt, apply); return true; @@ -994,7 +997,6 @@ class JoyShock int lastGyroIndexY = 0; Color _light_bar; - pair last_rumble = { 0, 0 }; JoyShock(int uniqueHandle, int controllerSplitType, shared_ptr sharedButtonCommon = nullptr) : handle(uniqueHandle) @@ -1024,7 +1026,10 @@ class JoyShock buttons.push_back(DigitalButton(btnCommon, ButtonID(i), uniqueHandle, &motion)); } ResetSmoothSample(); - CheckVigemState(); + if (!CheckVigemState()) + { + virtual_controller = ControllerScheme::NONE; + } JslSetLightColour(handle, _light_bar.raw); } @@ -1034,10 +1039,11 @@ class JoyShock void Rumble(int smallRumble, int bigRumble) { - COUT << "Rumbling at " << smallRumble << " and " << bigRumble << endl; - JslSetRumble(handle, smallRumble, bigRumble); - last_rumble.first = smallRumble; - last_rumble.second = bigRumble; + if (getSetting(SettingID::RUMBLE) == Switch::ON) + { + //COUT << "Rumbling at " << smallRumble << " and " << bigRumble << endl; + JslSetRumble(handle, smallRumble, bigRumble); + } } bool CheckVigemState() @@ -1061,16 +1067,16 @@ class JoyShock void handleViGEmNotification(UCHAR largeMotor, UCHAR smallMotor, Indicator indicator) { - static chrono::steady_clock::time_point last_call; - auto now = chrono::steady_clock::now(); - auto diff = ((float)chrono::duration_cast(now - last_call).count()) / 1000000.0f; - last_call = now; - COUT_INFO << "Time since last vigem rumble is " << diff << " us" << endl; + //static chrono::steady_clock::time_point last_call; + //auto now = chrono::steady_clock::now(); + //auto diff = ((float)chrono::duration_cast(now - last_call).count()) / 1000000.0f; + //last_call = now; + //COUT_INFO << "Time since last vigem rumble is " << diff << " us" << endl; lock_guard guard(this->btnCommon->callback_lock); switch (platform_controller_type) { - case 4: // SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS4 - case 7: // SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS5 + case JS_TYPE_DS4: + case JS_TYPE_DS: JslSetLightColour(handle, _light_bar.raw); break; default: @@ -1159,6 +1165,11 @@ class JoyShock case SettingID::FLICK_SNAP_MODE: opt = GetOptionalSetting(flick_snap_mode, *activeChord); break; + case SettingID::TOUCHPAD_DUAL_STAGE_MODE: + opt = GetOptionalSetting(touch_ds_mode, *activeChord); + break; + case SettingID::RUMBLE: + opt = GetOptionalSetting(rumble_enable, *activeChord); } if (opt) return *opt; @@ -1482,8 +1493,7 @@ class JoyShock void handleTriggerChange(ButtonID softIndex, ButtonID fullIndex, TriggerMode mode, float position) { - constexpr int SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO = 5; // SDL_GameControllerType:: - if (platform_controller_type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO && mode != TriggerMode::X_LT && mode != TriggerMode::X_RT) + if (mode != TriggerMode::X_LT && mode != TriggerMode::X_RT && (platform_controller_type == JS_TYPE_PRO_CONTROLLER || platform_controller_type == JS_TYPE_JOYCON_LEFT || platform_controller_type == JS_TYPE_JOYCON_RIGHT)) { // Override local variable because the controller has digital triggers. Effectively ignore Full Pull binding. mode = TriggerMode::NO_FULL; @@ -1795,6 +1805,8 @@ static void resetAllMappings() autoloadSwitch.Reset(); hide_minimized.Reset(); virtual_controller.Reset(); + rumble_enable.Reset(); + touch_ds_mode.Reset(); os_mouse_speed = 1.0f; last_flick_and_rotation = 0.0f; @@ -1880,10 +1892,10 @@ bool do_RESET_MAPPINGS(CmdRegistry *registry) resetAllMappings(); if (registry) { - if (!registry->loadConfigFile("onreset.txt")) + if (!registry->loadConfigFile("OnReset.txt")) { COUT << "There is no "; - COUT_INFO << "onreset.txt"; + COUT_INFO << "OnReset.txt"; COUT << " file to load." << endl; } } @@ -1896,7 +1908,9 @@ bool do_RECONNECT_CONTROLLERS(in_string arguments) if (mergeJoycons || arguments.rfind("SPLIT", 0) == 0) { COUT << "Reconnecting controllers: " << arguments << endl; + JslDisconnectAndDisposeAll(); connectDevices(mergeJoycons); + JslSetCallback(&joyShockPollCallback); return true; } return false; @@ -2144,8 +2158,8 @@ static float handleFlickStick(float calX, float calY, float lastCalX, float last float flickSpeedConstant = jc->getSetting(SettingID::REAL_WORLD_CALIBRATION) * mouseCalibrationFactor / jc->getSetting(SettingID::IN_GAME_SENS); float flickSpeed = -(angleChange * flickSpeedConstant); int maxSmoothingSamples = min(jc->NumSamples, (int)ceil(64.0f / tick_time.get())); // target a max smoothing window size of 64ms - float stepSize = 0.01f; // and we only want full on smoothing when the stick change each time we poll it is approximately the minimum stick resolution - // the fact that we're using radians makes this really easy + float stepSize = 0.01f; // and we only want full on smoothing when the stick change each time we poll it is approximately the minimum stick resolution + // the fact that we're using radians makes this really easy auto rotate_smooth_override = jc->getSetting(SettingID::ROTATE_SMOOTH_OVERRIDE); if (rotate_smooth_override < 0.0f) { @@ -2393,6 +2407,7 @@ void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE l shared_ptr jc = handle_to_joyshock[jcHandle]; if (jc == nullptr) return; + jc->btnCommon->callback_lock.lock(); auto timeNow = chrono::steady_clock::now(); deltaTime = ((float)chrono::duration_cast(timeNow - jc->time_now).count()) / 1000000.0f; @@ -2429,7 +2444,6 @@ void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE l bool rightAny = false; bool motionAny = false; - jc->btnCommon->callback_lock.lock(); if (jc->set_neutral_quat) { jc->neutralQuatW = inQuatW; @@ -2612,7 +2626,6 @@ void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE l jc->handleButtonChange(ButtonID::L, buttons & (1 << JSOFFSET_L)); jc->handleButtonChange(ButtonID::MINUS, buttons & (1 << JSOFFSET_MINUS)); // for backwards compatibility, we need need to account for the fact that SDL2 maps the touchpad button differently to SDL - jc->handleButtonChange(ButtonID::CAPTURE, buttons & (1 << JSOFFSET_CAPTURE)); jc->handleButtonChange(ButtonID::L3, buttons & (1 << JSOFFSET_LCLICK)); // SL and SR are mapped to back paddle positions: jc->handleButtonChange(ButtonID::LSL, buttons & (1 << JSOFFSET_SL)); @@ -2620,6 +2633,26 @@ void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE l float lTrigger = JslGetLeftTrigger(jc->handle); jc->handleTriggerChange(ButtonID::ZL, ButtonID::ZLF, jc->getSetting(SettingID::ZL_MODE), lTrigger); + + bool touch = JslGetTouchDown(jc->handle, false) || JslGetTouchDown(jc->handle, true); + switch (jc->platform_controller_type) + { + case JS_TYPE_DS4: + case JS_TYPE_DS: + { + float triggerpos = buttons & (1 << JSOFFSET_CAPTURE) ? 1.f : + touch ? 0.99f : + 0.f; + jc->handleTriggerChange(ButtonID::TOUCH, ButtonID::CAPTURE, jc->getSetting(SettingID::TOUCHPAD_DUAL_STAGE_MODE), triggerpos); + } + break; + default: + { + jc->handleButtonChange(ButtonID::TOUCH, touch); + jc->handleButtonChange(ButtonID::CAPTURE, buttons & (1 << JSOFFSET_CAPTURE)); + } + break; + } } if (jc->controller_split_type != JS_SPLIT_TYPE_LEFT) { @@ -2638,8 +2671,6 @@ void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE l float rTrigger = JslGetRightTrigger(jc->handle); jc->handleTriggerChange(ButtonID::ZR, ButtonID::ZRF, jc->getSetting(SettingID::ZR_MODE), rTrigger); } - bool touch = JslGetTouchDown(jc->handle, false) || JslGetTouchDown(jc->handle, true); - jc->handleButtonChange(ButtonID::TOUCH, touch); // Handle buttons before GYRO because some of them may affect the value of blockGyro auto gyro = jc->getSetting(SettingID::GYRO_ON); // same result as getting GYRO_OFF @@ -2760,8 +2791,6 @@ void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE l if (jc->btnCommon->_vigemController) { jc->btnCommon->_vigemController->update(); // Check for initialized built-in - if (jc->last_rumble.first == 0 && jc->last_rumble.second == 0) - jc->Rumble(jc->last_rumble.first, jc->last_rumble.second); } auto newColor = jc->getSetting(SettingID::LIGHT_BAR); if (jc->_light_bar != newColor) @@ -2822,7 +2851,9 @@ bool AutoLoadPoll(void *param) } if (!success) { - COUT_INFO << "create \"AutoLoad\\" << noextmodule << ".txt\" to autoload for this application." << endl; + COUT_INFO << "create "; + COUT << "AutoLoad\\" << noextmodule << ".txt"; + COUT_INFO << " to autoload for this application." << endl; } } return true; @@ -2911,7 +2942,6 @@ void CleanUp() { tray->Hide(); HideConsole(); - handle_to_joyshock.clear(); JslDisconnectAndDisposeAll(); handle_to_joyshock.clear(); // Destroy Vigem Gamepads ReleaseConsole(); @@ -3016,6 +3046,16 @@ TriggerMode filterTriggerMode(TriggerMode current, TriggerMode next) return filterInvalidValue(current, next); } +TriggerMode filterTouchpadDualStageMode(TriggerMode current, TriggerMode next) +{ + if (next == TriggerMode::X_LT || next == TriggerMode::X_RT || next == TriggerMode::INVALID) + { + COUT_WARN << SettingID::TOUCHPAD_DUAL_STAGE_MODE << " doesn't support vigem analog modes." << endl; + return current; + } + return next; +} + StickMode filterStickMode(StickMode current, StickMode next) { if (next == StickMode::LEFT_STICK || next == StickMode::RIGHT_STICK) @@ -3048,17 +3088,24 @@ void UpdateRingModeFromStickMode(JSMVariable *stickRingMode, StickMode ControllerScheme UpdateVirtualController(ControllerScheme prevScheme, ControllerScheme nextScheme) { + bool success = true; for (auto &js : handle_to_joyshock) { if (!js.second->btnCommon->_vigemController || js.second->btnCommon->_vigemController->getType() != nextScheme) { - js.second->btnCommon->_vigemController.reset( - nextScheme == ControllerScheme::NONE ? nullptr : - new Gamepad(nextScheme, bind(&JoyShock::handleViGEmNotification, js.second.get(), placeholders::_1, placeholders::_2, placeholders::_3))); + if (nextScheme == ControllerScheme::NONE) + { + js.second->btnCommon->_vigemController.reset(nullptr); + } + else + { + js.second->btnCommon->_vigemController.reset(new Gamepad(nextScheme, bind(&JoyShock::handleViGEmNotification, js.second.get(), placeholders::_1, placeholders::_2, placeholders::_3))); + success &= js.second->btnCommon->_vigemController->isInitialized(); + } } } - return nextScheme; + return success ? nextScheme : prevScheme; } void OnVirtualControllerChange(ControllerScheme newScheme) @@ -3073,7 +3120,7 @@ void OnVirtualControllerChange(ControllerScheme newScheme) } } -void RefreshAutoloadHelp(JSMAssignment *autoloadCmd) +void RefreshAutoLoadHelp(JSMAssignment *autoloadCmd) { stringstream ss; ss << "AUTOLOAD will attempt load a file from the following folder when a window with a matching executable name enters focus:" << endl @@ -3105,63 +3152,53 @@ class StickDeadzoneAssignment : public JSMAssignment class GyroButtonAssignment : public JSMAssignment { -private: +protected: const bool _always_off; + const ButtonID _chordButton; - static bool GyroParser(JSMCommand *cmd, in_string data) + virtual void DisplayCurrentValue() override { - auto inst = dynamic_cast(cmd); - if (data.empty()) + GyroSettings value(_var); + if (_chordButton > ButtonID::NONE) { - GyroSettings value(inst->_var); - //No assignment? Display current assignment - COUT << (value.always_off ? string("GYRO_ON") : string("GYRO_OFF")) << " = " << value << endl; - ; + COUT << _chordButton << ','; } - else - { - stringstream ss(data); - // Read the value - GyroSettings value; - value.always_off = inst->_always_off; // Added line from DefaultParser - ss >> value; - if (!ss.fail()) - { - GyroSettings oldVal = inst->_var; - inst->_var = value; - // Command succeeded if the value requested was the current one - // or if the new value is different from the old. - return value == oldVal || inst->_var != oldVal; // Command processed successfully - } - // Couldn't read the value - } - // Not an equal sign? The command is entered wrong! - return false; + COUT << (value.always_off ? string("GYRO_ON") : string("GYRO_OFF")) << " = " << value << endl; } - void DisplayGyroSettingValue(GyroSettings value) + virtual GyroSettings ReadValue(stringstream &in) override { - COUT << (value.always_off ? string("GYRO_ON") : string("GYRO_OFF")) << " is set to " << value << endl; - ; + GyroSettings value; + value.always_off = _always_off; // Added line from DefaultParser + in >> value; + return value; + } + + virtual void DisplayNewValue(GyroSettings value) override + { + if (_chordButton > ButtonID::NONE) + { + COUT << _chordButton << ','; + } + COUT << (value.always_off ? string("GYRO_ON") : string("GYRO_OFF")) << " has been set to " << value << endl; } public: - GyroButtonAssignment(in_string name, JSMVariable &setting, bool always_off) - : JSMAssignment(name, setting) + GyroButtonAssignment(in_string name, in_string displayName, JSMVariable &setting, bool always_off, ButtonID chord = ButtonID::NONE) + : JSMAssignment(name, name, setting, true) , _always_off(always_off) + , _chordButton(chord) { - SetParser(&GyroButtonAssignment::GyroParser); - _var.RemoveOnChangeListener(_listenerId); } GyroButtonAssignment(SettingID id, bool always_off) - : GyroButtonAssignment(magic_enum::enum_name(id).data(), gyro_settings, always_off) + : GyroButtonAssignment(magic_enum::enum_name(id).data(), magic_enum::enum_name(id).data(), gyro_settings, always_off) { } GyroButtonAssignment *SetListener() { - _listenerId = _var.AddOnChangeListener(bind(&GyroButtonAssignment::DisplayGyroSettingValue, this, placeholders::_1)); + _listenerId = _var.AddOnChangeListener(bind(&GyroButtonAssignment::DisplayNewValue, this, placeholders::_1)); return this; } @@ -3173,14 +3210,16 @@ class GyroButtonAssignment : public JSMAssignment { //Create Modeshift string name = chord + op + _displayName; - unique_ptr chordAssignment(new GyroButtonAssignment(name, *settingVar->AtChord(*optBtn), _always_off)); - chordAssignment->SetHelp(_help)->SetParser(bind(&GyroButtonAssignment::ModeshiftParser, *optBtn, settingVar, _parse, placeholders::_1, placeholders::_2))->SetTaskOnDestruction(bind(&JSMSetting::ProcessModeshiftRemoval, settingVar, *optBtn)); + unique_ptr chordAssignment((new GyroButtonAssignment(_name, name, *settingVar->AtChord(*optBtn), _always_off, *optBtn))->SetListener()); + chordAssignment->SetHelp(_help)->SetParser(bind(&GyroButtonAssignment::ModeshiftParser, *optBtn, settingVar, &_parse, placeholders::_1, placeholders::_2))->SetTaskOnDestruction(bind(&JSMSetting::ProcessModeshiftRemoval, settingVar, *optBtn)); return chordAssignment; } return JSMCommand::GetModifiedCmd(op, chord); } - virtual ~GyroButtonAssignment() = default; + virtual ~GyroButtonAssignment() + { + } }; class HelpCmd : public JSMMacro @@ -3286,7 +3325,7 @@ int main(int argc, char *argv[]) // Threads need to be created before listeners CmdRegistry commandRegistry; minimizeThread.reset(new PollingThread("Minimize thread", &MinimizePoll, nullptr, 1000, hide_minimized.get() == Switch::ON)); // Start by default - autoLoadThread.reset(new PollingThread("Autoload thread", &AutoLoadPoll, &commandRegistry, 1000, autoloadSwitch.get() == Switch::ON)); // Start by default + autoLoadThread.reset(new PollingThread("AutoLoad thread", &AutoLoadPoll, &commandRegistry, 1000, autoloadSwitch.get() == Switch::ON)); // Start by default if (autoLoadThread && autoLoadThread->isRunning()) { @@ -3356,14 +3395,15 @@ int main(int argc, char *argv[]) dbl_press_window.SetFilter(&filterPositive); hold_press_time.SetFilter(&filterHoldPressDelay); tick_time.SetFilter(&filterTickTime); - currentWorkingDir.SetFilter([](PathString current, PathString next) - { - return SetCWD(string(next)) ? next : current; - }); + currentWorkingDir.SetFilter([](PathString current, PathString next) { + return SetCWD(string(next)) ? next : current; + }); autoloadSwitch.SetFilter(&filterInvalidValue)->AddOnChangeListener(bind(&UpdateThread, autoLoadThread.get(), placeholders::_1)); hide_minimized.SetFilter(&filterInvalidValue)->AddOnChangeListener(bind(&UpdateThread, minimizeThread.get(), placeholders::_1)); virtual_controller.SetFilter(&UpdateVirtualController)->AddOnChangeListener(&OnVirtualControllerChange); + rumble_enable.SetFilter(&filterInvalidValue); scroll_sens.SetFilter(&filterFloatPair); + touch_ds_mode.SetFilter(&filterTouchpadDualStageMode); // light_bar needs no filter or listener. The callback polls and updates the color. #if _WIN32 currentWorkingDir = string(&cmdLine[0], &cmdLine[wcslen(cmdLine)]); @@ -3483,7 +3523,7 @@ int main(int argc, char *argv[]) ->SetHelp("Controllers with a left analog trigger can use one of the following dual stage trigger modes:\nNO_FULL, NO_SKIP, MAY_SKIP, MUST_SKIP, MAY_SKIP_R, MUST_SKIP_R, NO_SKIP_EXCLUSIVE, X_LT, X_RT, PS_L2, PS_R2")); auto *autoloadCmd = new JSMAssignment("AUTOLOAD", autoloadSwitch); commandRegistry.Add(autoloadCmd); - currentWorkingDir.AddOnChangeListener(bind(&RefreshAutoloadHelp, autoloadCmd), true); + currentWorkingDir.AddOnChangeListener(bind(&RefreshAutoLoadHelp, autoloadCmd), true); commandRegistry.Add((new JSMMacro("README"))->SetMacro(bind(&do_README))->SetHelp("Open the latest JoyShockMapper README in your browser.")); commandRegistry.Add((new JSMMacro("WHITELIST_SHOW"))->SetMacro(bind(&do_WHITELIST_SHOW))->SetHelp("Open HIDCerberus configuration page in your browser.")); commandRegistry.Add((new JSMMacro("WHITELIST_ADD"))->SetMacro(bind(&do_WHITELIST_ADD))->SetHelp("Add JoyShockMapper to HIDGuardian whitelisted applications.")); @@ -3522,6 +3562,8 @@ int main(int argc, char *argv[]) ->SetHelp("Sets the time in milliseconds that JoyShockMaper waits before reading from each controller again.")); commandRegistry.Add((new JSMAssignment("JSM_DIRECTORY", currentWorkingDir)) ->SetHelp("If AUTOLOAD doesn't work properly, set this value to the path to the directory holding the JoyShockMapper.exe file. Make sure a folder named \"AutoLoad\" exists there.")); + commandRegistry.Add((new JSMAssignment("HIDE_MINIMIZED", hide_minimized)) + ->SetHelp("When enabled, JoyShockMapper disappears from the taskbar when minimized, leaving only the try icon to access it.")); commandRegistry.Add((new JSMAssignment(light_bar)) ->SetHelp("Changes the color bar of the DS4. Either enter as a hex code (xRRGGBB), as three decimal values between 0 and 255 (RRR GGG BBB), or as a common color name in all caps and underscores.")); commandRegistry.Add(new HelpCmd(commandRegistry)); @@ -3529,6 +3571,10 @@ int main(int argc, char *argv[]) ->SetHelp("Sets the vigem virtual controller type. Can be NONE (default), XBOX (360) or DS4 (PS4).")); commandRegistry.Add((new JSMAssignment(scroll_sens)) ->SetHelp("Scrolling sensitivity for sticks.")); + commandRegistry.Add((new JSMAssignment(rumble_enable)) + ->SetHelp("Disable the rumbling feature from vigem. Valid values are ON and OFF.")); + commandRegistry.Add((new JSMAssignment(touch_ds_mode)) + ->SetHelp("Dual stage mode for the touchpad TOUCH and CAPTURE (i.e. click) bindings.")); bool quit = false; commandRegistry.Add((new JSMMacro("QUIT")) @@ -3540,20 +3586,20 @@ int main(int argc, char *argv[]) Mapping::_isCommandValid = bind(&CmdRegistry::isCommandValid, &commandRegistry, placeholders::_1); - JslSetCallback(&joyShockPollCallback); connectDevices(); + JslSetCallback(&joyShockPollCallback); tray.reset(new TrayIcon(trayIconData, &beforeShowTrayMenu)); tray->Show(); - do_RESET_MAPPINGS(&commandRegistry); // onreset.txt - if (commandRegistry.loadConfigFile("onstartup.txt")) + do_RESET_MAPPINGS(&commandRegistry); // OnReset.txt + if (commandRegistry.loadConfigFile("OnStartup.txt")) { COUT << "Finished executing startup file." << endl; } else { COUT << "There is no "; - COUT_INFO << "onstartup.txt"; + COUT_INFO << "OnStartup.txt"; COUT << " file to load." << endl; } diff --git a/JoyShockMapper/src/operators.cpp b/JoyShockMapper/src/operators.cpp index c2fe9b9..e8b33bf 100644 --- a/JoyShockMapper/src/operators.cpp +++ b/JoyShockMapper/src/operators.cpp @@ -1,11 +1,11 @@ #include "JoyShockMapper.h" -#include "InputHelpers.h" -#include +#include "ColorCodes.h" #include #include #include #include +#include static optional getFloat(const string &str, size_t *newpos = nullptr) { @@ -107,21 +107,26 @@ istream &operator>>(istream &in, GyroSettings &gyro_settings) string valueName; in >> valueName; stringstream ss(valueName); - ButtonID rhsMappingIndex; - ss >> rhsMappingIndex; - if (rhsMappingIndex >= ButtonID::NONE) + auto rhsMappingIndex = magic_enum::enum_cast(valueName); + if (valueName.compare("NONE\\") == 0) // Special handling to assign none to a modeshift { - gyro_settings.button = rhsMappingIndex; + gyro_settings.button = ButtonID::NONE; + gyro_settings.ignore_mode = GyroIgnoreMode::BUTTON; + } + else if (rhsMappingIndex && *rhsMappingIndex >= ButtonID::NONE) + { + gyro_settings.button = *rhsMappingIndex; gyro_settings.ignore_mode = GyroIgnoreMode::BUTTON; } else { - if (valueName.compare("LEFT_STICK") == 0) + auto ignoreMode = magic_enum::enum_cast(valueName); + if (ignoreMode == GyroIgnoreMode::LEFT_STICK) { gyro_settings.button = ButtonID::NONE; gyro_settings.ignore_mode = GyroIgnoreMode::LEFT_STICK; } - else if (valueName.compare("RIGHT_STICK") == 0) + else if (ignoreMode == GyroIgnoreMode::RIGHT_STICK) { gyro_settings.button = ButtonID::NONE; gyro_settings.ignore_mode = GyroIgnoreMode::RIGHT_STICK; @@ -137,23 +142,13 @@ istream &operator>>(istream &in, GyroSettings &gyro_settings) ostream &operator<<(ostream &out, GyroSettings gyro_settings) { - switch (gyro_settings.ignore_mode) + if (gyro_settings.ignore_mode == GyroIgnoreMode::BUTTON) + { + out << gyro_settings.button; + } + else { - case GyroIgnoreMode::LEFT_STICK: - out << "LEFT_STICK"; - break; - case GyroIgnoreMode::RIGHT_STICK: - out << "RIGHT_STICK"; - break; - case GyroIgnoreMode::BUTTON: - if (gyro_settings.button == ButtonID::NONE) - out << "No button disables or enables gyro"; - else if (gyro_settings.button != ButtonID::INVALID) - out << gyro_settings.button; - break; - default: - out << "INVALID"; - break; + out << gyro_settings.ignore_mode; } return out; } @@ -267,7 +262,9 @@ istream &operator>>(istream &in, Color &color) ostream &operator<<(ostream &out, Color color) { - out << 'x' << std::hex << int(color.rgb.r) << int(color.rgb.g) << int(color.rgb.b); + out << 'x' << hex << setw(2) << setfill('0') << int(color.rgb.r) + << hex << setw(2) << setfill('0') << int(color.rgb.g) + << hex << setw(2) << setfill('0') << int(color.rgb.b); return out; } diff --git a/JoyShockMapper/src/win32/Gamepad.cpp b/JoyShockMapper/src/win32/Gamepad.cpp index 92b9cc3..f22a3bb 100644 --- a/JoyShockMapper/src/win32/Gamepad.cpp +++ b/JoyShockMapper/src/win32/Gamepad.cpp @@ -1,5 +1,5 @@ #include -#include "win32/Gamepad.h" +#include "Gamepad.h" #include "ViGEm/Client.h" #include @@ -12,16 +12,7 @@ class VigemClient { - class Deleter - { - public: - void operator()(VigemClient *vc) - { - delete vc; - } - }; - // singleton - static unique_ptr _inst; + static unique_ptr _inst; PVIGEM_CLIENT _client = nullptr; VIGEM_ERROR _error = VIGEM_ERROR::VIGEM_ERROR_NONE; @@ -53,7 +44,7 @@ class VigemClient } }; -unique_ptr VigemClient::_inst; +unique_ptr VigemClient::_inst; template<> ostream &operator<<(ostream &out, VIGEM_ERROR errCode) @@ -117,10 +108,10 @@ ostream &operator<<(ostream &out, VIGEM_ERROR errCode) void Gamepad::x360Notification( PVIGEM_CLIENT client, PVIGEM_TARGET target, - UCHAR largeMotor, - UCHAR smallMotor, - UCHAR ledNumber, - LPVOID userData) + uint8_t largeMotor, + uint8_t smallMotor, + uint8_t ledNumber, + void *userData) { auto originator = static_cast(userData); if (client == VigemClient::get() && originator && originator->_gamepad == target && originator->_notification) @@ -134,10 +125,10 @@ void Gamepad::x360Notification( void Gamepad::ds4Notification( PVIGEM_CLIENT client, PVIGEM_TARGET target, - UCHAR largeMotor, - UCHAR smallMotor, + uint8_t largeMotor, + uint8_t smallMotor, Indicator lightbarColor, - LPVOID userData) + void *userData) { auto originator = static_cast(userData); if (client == VigemClient::get() && originator && originator->_gamepad == target && originator->_notification) diff --git a/JoyShockMapper/src/win32/PlatformDefinitions.cpp b/JoyShockMapper/src/win32/PlatformDefinitions.cpp index d7766eb..bda64f2 100644 --- a/JoyShockMapper/src/win32/PlatformDefinitions.cpp +++ b/JoyShockMapper/src/win32/PlatformDefinitions.cpp @@ -6,7 +6,7 @@ const char *AUTOLOAD_FOLDER() { - return _strdup((GetCWD() + "\\Autoload\\").c_str()); + return _strdup((GetCWD() + "\\AutoLoad\\").c_str()); }; const char *GYRO_CONFIGS_FOLDER() diff --git a/README.md b/README.md index 74749f9..1a6990c 100644 --- a/README.md +++ b/README.md @@ -189,8 +189,8 @@ L3: L3 or Left-stick click R3: R3 or Right-stick click N: The North face button, △, Y (Xbox) or X (Nintendo) E: The East face button, ○, B (Xbox) or A (Nintendo) -S: The South face button, ⨉, X (Xbox) or B (Nintendo) -W: The West face button, □, A (Xbox) or Y (Nintendo) +S: The South face button, ⨉, A (Xbox) or B (Nintendo) +W: The West face button, □, X (Xbox) or Y (Nintendo) LUP, LDOWN, LLEFT, LRIGHT: Left stick tilted up, down left or right LRING: Left ring binding, either inner or outer. RUP, RDOWN, RLEFT, RRIGHT: Right stick tilted up, down, left or right @@ -222,7 +222,7 @@ SCROLLUP, SCROLLDOWN: scroll the mouse wheel up, down, respectively VOLUME_UP, VOLUME_DOWN, MUTE: Volume controls NEXT_TRACK, PREV_TRACK, STOP_TRACK, PLAY_PAUSE: media control SCREENSHOT: print screen button -NONE: No input +NONE, DEFAULT: No input CALIBRATE: recalibrate gyro when pressing this input GYRO_ON, GYRO_OFF: Enable or disable gyro GYRO_INVERT, GYRO_INV_X, GYRO_INV_Y: Invert gyro, or in just the x or y axes, respectively @@ -787,7 +787,7 @@ GYRO_OFF = R3 # Use gyro, disable with stick click R = Q # Last weapon / Bring up weapon wheel -R,GYRO_ON = NONE # Disable gyro when R is down +R,GYRO_ON = NONE\ # Disable gyro when R is down R,RIGHT_STICK_MODE = MOUSE_AREA # Select wheel item with stick ``` @@ -805,10 +805,12 @@ ZLF,GYRO_SENS = 0.5 0.4 # Half sensitivity on full pull These commands function exactly like chorded press bindings, whereas if multiple chords are active the latest has priority. Also the chord is active whenever the button is down regardless of whether a binding is active or not. It is also worth noting that a special case is handled on stick mode changes where upon returning to the normal mode by releasing the chord button, the stick input will be ignored until it returns to the center. In the DOOM example above, this prevents an undesirable flick upon releasing the chord. -To remove an existing modeshift you have to assign ```NONE``` to the chord. +To remove an existing modeshift you have to assign ```NONE``` to the chord. There is special handling for the gyro button because NONE is a valid assignment. Add a backslash to indicate it is the button assignment rather than clearing the modeshift ``` -ZLF,GYRO_SENS = NONE +GYRO_OFF = RIGHT_STICK # Gyro off when using right stick +ZLF,GYRO_OFF = NONE\ # RS does not turn gyro off when ZLF is pressed +ZLF,GYRO_OFF = NONE # oops undo ``` ### 8. Miscellaneous Commands diff --git a/cmake/WindowsConfig.cmake b/cmake/WindowsConfig.cmake index 0b292b5..bc9307e 100644 --- a/cmake/WindowsConfig.cmake +++ b/cmake/WindowsConfig.cmake @@ -1,4 +1,4 @@ -if (WIN32) +if (WIN32) set (WINDOWS ON) set_property (GLOBAL PROPERTY USE_FOLDERS ON) enable_language (RC) @@ -42,28 +42,51 @@ if (WIN32) add_library (Platform::Dependencies ALIAS platform_dependencies) + set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/${CONFIG}/install") + + if(SDL) + set(PACKAGE_DIR "${PROJECT_NAME}_${CMAKE_GENERATOR_PLATFORM}_SDL2") + else() + set(PACKAGE_DIR "${PROJECT_NAME}_${CMAKE_GENERATOR_PLATFORM}") + endif() + install ( DIRECTORY ${PROJECT_SOURCE_DIR}/dist/GyroConfigs - DESTINATION bin + DESTINATION ${PACKAGE_DIR} + ) + + install ( + DIRECTORY ${PROJECT_SOURCE_DIR}/dist/AutoLoad + DESTINATION ${PACKAGE_DIR} + ) + + install ( + FILES ${PROJECT_SOURCE_DIR}/dist/OnReset.txt + DESTINATION ${PACKAGE_DIR} + ) + + install ( + FILES ${PROJECT_SOURCE_DIR}/dist/OnStartup.txt + DESTINATION ${PACKAGE_DIR} ) install ( FILES ${PROJECT_SOURCE_DIR}/CHANGELOG.md - DESTINATION bin + DESTINATION ${PACKAGE_DIR} ) install ( FILES ${PROJECT_SOURCE_DIR}/LICENSE.md - DESTINATION bin + DESTINATION ${PACKAGE_DIR} ) install ( FILES ${PROJECT_SOURCE_DIR}/README.md - DESTINATION bin + DESTINATION ${PACKAGE_DIR} ) install ( - FILES ${PROJECT_SOURCE_DIR}/README_CN.md - DESTINATION bin + FILES ${PROJECT_SOURCE_DIR}/README_中文.md + DESTINATION ${PACKAGE_DIR} ) endif () diff --git a/dist/AutoLoad/README.txt b/dist/AutoLoad/README.txt new file mode 100644 index 0000000..f2ad7e5 --- /dev/null +++ b/dist/AutoLoad/README.txt @@ -0,0 +1,9 @@ +Config files in this directory will be loaded automatically by JoyShockMapper when +the window in focus has a process name identical to the configuration file. + +For example: A file named chrome.txt in this folder will load when the window of chrome.exe gets in focus. + +Watch closely the blue logs in the JoyShockMapper console : it will tell you how to name the +file for the windows that got in focus when no file is present to load. + +To disable the Autoload feature, enter AUTOLOAD=OFF diff --git a/dist/GyroConfigs/Desktop.txt b/dist/GyroConfigs/Desktop.txt index c825f7a..774ec08 100644 --- a/dist/GyroConfigs/Desktop.txt +++ b/dist/GyroConfigs/Desktop.txt @@ -7,18 +7,33 @@ REAL_WORLD_CALIBRATION = 5.3333 IN_GAME_SENS = 1 COUNTER_OS_MOUSE_SPEED -# Button mappings -GYRO_OFF = E -LLEFT = LEFT -LRIGHT = RIGHT -LUP = UP -LDOWN = DOWN -R = LMOUSE -L = RMOUSE -+ = ESC -ZL = LSHIFT -S = SPACE -N = ESC +# DPAD is arrows +LEFT = LEFT +RIGHT = RIGHT +UP = UP +DOWN = DOWN -# Include mouse settings +# Mouse Buttons and wheel +R = FMOUSE +L = BMOUSE +ZR = LMOUSE LMOUSE +ZL = RMOUSE RMOUSE +L3 = MMOUSE +LEFT_STICK_MODE = SCROLL_WHEEL +LLEFT = SCROLLUP +LRIGHT = SCROLLDOWN +SCROLL_SENS = 60 + +# Button pad is common buttons +S = ENTER +W = SPACE +N = BACKSPACE +E = ESC GYRO_OFF + ++ = LALT\ !TAB\ # Task view +- = CONTROL\ LWINDOWS\ O\ # On Screen Keyboard +HOME = LWINDOWS # Start Menu +HOME,HOME = LWINDOWS\ D\ # Minimize All + +# Right stick drives the cursor GyroConfigs/_2Dmouse.txt \ No newline at end of file diff --git a/dist/OnReset.txt b/dist/OnReset.txt new file mode 100644 index 0000000..fb14456 --- /dev/null +++ b/dist/OnReset.txt @@ -0,0 +1,4 @@ +# Always have a calibrate button. Toggle on tap or hold to calibrate +# Each joycon should have a calibrate button. That's why there's two bindings here. +HOME = CALIBRATE CALIBRATE +CAPTURE = CALIBRATE CALIBRATE \ No newline at end of file diff --git a/dist/OnStartup.txt b/dist/OnStartup.txt new file mode 100644 index 0000000..90edaa3 --- /dev/null +++ b/dist/OnStartup.txt @@ -0,0 +1,12 @@ +# Edit this file to your prefered settings on startup + +#GyroConfigs/Desktop.txt # Load a configuration to navigate the OS + +# Uncomment the following three lines to calibrate all your devices at startup +#RESTART_GYRO_CALIBRATION +#SLEEP 2 # wait two seconds +#FINISH_GYRO_CALIBRATION + +#AUTOLOAD = OFF # Uncomment to disable AUTOLOAD on startup + +#HIDE_MINIMIZED = ON # Uncomment to remove JSM from the taskbar when minimized. diff --git a/script/generate_win32_vs_solution.bat b/script/generate_win32_vs_solution_JSL.bat similarity index 60% rename from script/generate_win32_vs_solution.bat rename to script/generate_win32_vs_solution_JSL.bat index fc5886a..d07644b 100644 --- a/script/generate_win32_vs_solution.bat +++ b/script/generate_win32_vs_solution_JSL.bat @@ -5,9 +5,9 @@ REM Get project directory name for %%a in (.) do set projectDir=%%~na cd .. -mkdir build-jsm-win32 -cd build-jsm-win32 -cmake ../%projectDir% -G "Visual Studio 16 2019" -A Win32 -DBUILD_SHARED_LIBS=1 +mkdir build-jsm-win32-jsl +cd build-jsm-win32-jsl +cmake ../%projectDir% -A Win32 -DBUILD_SHARED_LIBS=1 echo Open the generated Visual Studio solution located at: %cd%\JoyShockMapper.sln diff --git a/script/generate_win32_vs_solution_SDL.bat b/script/generate_win32_vs_solution_SDL.bat new file mode 100644 index 0000000..c35b131 --- /dev/null +++ b/script/generate_win32_vs_solution_SDL.bat @@ -0,0 +1,14 @@ +@echo off + +cd .. +REM Get project directory name +for %%a in (.) do set projectDir=%%~na + +cd .. +mkdir build-jsm-win32-sdl +cd build-jsm-win32-sdl +cmake ../%projectDir% -A Win32 -DBUILD_SHARED_LIBS=1 -DSDL=1 + +echo Open the generated Visual Studio solution located at: %cd%\JoyShockMapper.sln + +pause diff --git a/script/generate_win64_vs_solution_JSL.bat b/script/generate_win64_vs_solution_JSL.bat index ee6d04e..6d54ad9 100644 --- a/script/generate_win64_vs_solution_JSL.bat +++ b/script/generate_win64_vs_solution_JSL.bat @@ -7,7 +7,7 @@ for %%a in (.) do set projectDir=%%~na cd .. mkdir build-jsm-win64-jsl cd build-jsm-win64-jsl -cmake ../%projectDir% -G "Visual Studio 16 2019" -A x64 -DBUILD_SHARED_LIBS=1 +cmake ../%projectDir% -A x64 -DBUILD_SHARED_LIBS=1 echo Open the generated Visual Studio solution located at: %cd%\JoyShockMapper.sln diff --git a/script/generate_win64_vs_solution_SDL.bat b/script/generate_win64_vs_solution_SDL.bat index e9e437b..7dc30a5 100644 --- a/script/generate_win64_vs_solution_SDL.bat +++ b/script/generate_win64_vs_solution_SDL.bat @@ -7,7 +7,7 @@ for %%a in (.) do set projectDir=%%~na cd .. mkdir build-jsm-win64-sdl cd build-jsm-win64-sdl -cmake ../%projectDir% -G "Visual Studio 16 2019" -A x64 -DBUILD_SHARED_LIBS=1 -DSDL=1 +cmake ../%projectDir% -A x64 -DBUILD_SHARED_LIBS=1 -DSDL=1 echo Open the generated Visual Studio solution located at: %cd%\JoyShockMapper.sln