├── .gitignore ├── 71-nintendo-switch-procon-usb.rules ├── CMakeLists.txt ├── License.txt ├── README.md ├── install.sh ├── main.cpp └── src └── procon.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | -------------------------------------------------------------------------------- /71-nintendo-switch-procon-usb.rules: -------------------------------------------------------------------------------- 1 | # Nintendo Switch Pro Controller ;USB 2 | KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0660", TAG+="uaccess" 3 | 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | #SET(CMAKE_CXX_COMPILER /usr/bin/clang) 3 | SET(CMAKE_CXX_COMPILER /usr/bin/g++) 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | project(procon_driver) 7 | 8 | find_library(HIDAPI_LIBRARY 9 | NAMES hidapi hidapi-libusb REQUIRED) 10 | 11 | #find_library(EVDEV_LIBRARY 12 | # NAMES evdev REQUIRED) 13 | 14 | find_library(TESTLIB_LIBRARY2 evdev) 15 | 16 | find_path(HIDAPI_INCLUDE_DIR 17 | NAMES hidapi.h 18 | PATH_SUFFIXES 19 | hidapi REQUIRED) 20 | 21 | include(FindPackageHandleStandardArgs) 22 | find_package_handle_standard_args(HIDAPI 23 | DEFAULT_MSG 24 | HIDAPI_LIBRARY 25 | HIDAPI_INCLUDE_DIR) 26 | 27 | if(HIDAPI_FOUND) 28 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") 29 | 30 | 31 | 32 | set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") 33 | endif() 34 | 35 | #add_library (libshit libevdev/libevdev/libevdev.c) 36 | 37 | 38 | #if(EVDEV_FOUND) 39 | # set(EVDEV_LIBRARIES "${EVDEV_LIBRARY}") 40 | #endif() 41 | 42 | mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) 43 | 44 | include_directories( 45 | ${HIDAPI_INCLUDE_DIRS} 46 | "src/" 47 | "libevdev/" 48 | ) 49 | set(MAIN main.cpp) 50 | 51 | 52 | MESSAGE( STATUS "CMAKE_BINARY_DIR: " ${HIDAPI_INCLUDE_DIR} ) 53 | 54 | add_executable(${PROJECT_NAME} ${MAIN}) 55 | target_link_libraries(${PROJECT_NAME} ${HIDAPI_LIBRARIES}) #${TESTLIB_LIBRARY2})#libshit)# ${TESTLIB_LIBRARY2})#libshit) -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Keegan Kohl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwitchProConLinuxUSB 2 | This repository aims to provide a uinput driver for the Nintendo Switch Pro Controller when connected via USB. 3 | Currently only one controller is supported! 4 | 5 | # Dependencies 6 | 7 | This repo needs 8 | 9 | - libudev 10 | - autotools, autoconf and libtool 11 | - cmake 12 | - hidapi 13 | 14 | On Ubuntu you can install these in a terminal with: 15 | ``` 16 | sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev 17 | sudo apt-get install autotools-dev autoconf automake libtool 18 | sudo apt-get install cmake 19 | sudo apt-get install libhidapi-dev 20 | ``` 21 | # Installation 22 | 23 | Create install folder for Pro Controller driver and enter it, e.g. 24 | ``` 25 | mkdir ~/procon_driver 26 | cd ~/procon_driver 27 | ``` 28 | You can download the ZIP file through your browser and extract it, or you can use git. If you don't already have it: 29 | ``` 30 | sudo apt install git 31 | ``` 32 | Clone the repository here: 33 | ``` 34 | git clone https://github.com/FrotBot/SwitchProConLinuxUSB.git . 35 | ``` 36 | install and build the driver: 37 | ``` 38 | bash install.sh 39 | ``` 40 | 41 | Reboot your PC once to make the udev rules work. 42 | 43 | Open the terminal once more and navigate to the build directory in the install folder: 44 | ``` 45 | cd ~/procon_driver/build 46 | ``` 47 | 48 | Start the driver! 49 | ``` 50 | ./procon_driver 51 | ``` 52 | 53 | Follow instructions on screen and enjoy your games. 54 | 55 | (You'll need to reopen the executable from the last step everytime you use the driver.) 56 | 57 | On newer kernel versions, uinput devices need root privileges, so if you get error messages try to run 58 | ``` 59 | sudo ./procon_driver 60 | ``` 61 | 62 | # Invert axes and swap buttons 63 | 64 | If you're having trouble with inverted axes, try ./procon_driver --help and see options to run with inverted axes there. 65 | There's also an option to run with A and B as well as X and Y buttons switched, if you prefer the button output as they're written on the pad as opposed to XBox layout. 66 | 67 | 68 | # Thanks 69 | 70 | This project took heavy inspiration and some constants from this project: 71 | https://github.com/MTCKC/ProconXInput/tree/v0.1.0-alpha2 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Build SwitchProCon 4 | mkdir build 5 | cd build 6 | cmake .. 7 | make 8 | cd .. 9 | 10 | #copy udev rules 11 | sudo cp 71-nintendo-switch-procon-usb.rules /etc/udev/rules.d/71-nintendo-switch-procon-usb.rules 12 | 13 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "procon.hpp" 2 | 3 | //#define DEBUG 4 | #define TEST_BAD_DATA_CYCLES 10 5 | 6 | int main(int argc, char *argv[]) { 7 | bool help = false; 8 | bool force_calibration = false; 9 | bool show_version = false; 10 | bool invert_lx = false; 11 | bool invert_ly = false; 12 | bool invert_rx = false; 13 | bool invert_ry = false; 14 | bool invert_dx = false; 15 | bool invert_dy = false; 16 | bool swap_buttons = false; 17 | 18 | #ifdef DRIBBLE_MODE 19 | int dribble_cam_value; 20 | bool found_dribble_cam_value = false; 21 | #endif 22 | 23 | for (size_t i = 1; i < argc; ++i) { 24 | // printf("argv: %d\n",argv[i]); 25 | // std::cout << argv[i] << std::endl; 26 | 27 | bool option_found = false; 28 | if (std::string(argv[i]) == "-h" || std::string(argv[i]) == "--help") { 29 | help = true; 30 | option_found = true; 31 | } 32 | if (std::string(argv[i]) == "-c" || 33 | std::string(argv[i]) == "--calibration") { 34 | force_calibration = true; 35 | option_found = true; 36 | } 37 | if (std::string(argv[i]) == "--version") { 38 | show_version = true; 39 | option_found = true; 40 | } 41 | if (std::string(argv[i]) == "--invert-axis" || 42 | std::string(argv[i]) == "-i") { 43 | if (i + 1 >= argc) { 44 | ProController::red(); 45 | printf("Expected axis parameter. use --help for options!\n"); 46 | ProController::normal(); 47 | return -1; 48 | } 49 | option_found = true; 50 | bool valid_axis_name; 51 | do { 52 | valid_axis_name = false; 53 | if (std::string(argv[i + 1]) == "lx") { 54 | invert_lx = true; 55 | valid_axis_name = true; 56 | } else if (std::string(argv[i + 1]) == "ly") { 57 | invert_ly = true; 58 | valid_axis_name = true; 59 | } else if (std::string(argv[i + 1]) == "rx") { 60 | invert_rx = true; 61 | valid_axis_name = true; 62 | } else if (std::string(argv[i + 1]) == "ry") { 63 | invert_ry = true; 64 | valid_axis_name = true; 65 | } else if (std::string(argv[i + 1]) == "dx") { 66 | invert_dx = true; 67 | valid_axis_name = true; 68 | } else if (std::string(argv[i + 1]) == "dy") { 69 | invert_dy = true; 70 | valid_axis_name = true; 71 | } 72 | 73 | if (valid_axis_name) { 74 | ++i; 75 | } 76 | 77 | } while (valid_axis_name && i + 1 < argc); 78 | } 79 | if (std::string(argv[i]) == "--swap_buttons" || 80 | std::string(argv[i]) == "-s") { 81 | option_found = true; 82 | swap_buttons = true; 83 | } 84 | #ifdef DRIBBLE_MODE 85 | if (std::string(argv[i]) == "-d") { 86 | option_found = true; 87 | i++; 88 | dribble_cam_value = std::stoi(argv[i]); 89 | found_dribble_cam_value = true; 90 | } 91 | #endif 92 | if (!option_found) { 93 | std::cout << "Unknown option " << argv[i] 94 | << ". For usage, type './procon_driver --help'" << std::endl; 95 | return -1; 96 | } 97 | } 98 | 99 | if (help) { 100 | printf("Usage: procon_driver [OPTIONS]\nOptions are:\n"); 101 | printf(" -h --help get help on usage at start\n"); 102 | printf(" -c --calibration force calibration at start\n"); 103 | printf(" -s --swap_buttons Swap A and B buttons and X and Y " 104 | "buttons\n"); 105 | printf(" -i --invert-axis [AXIS] invert axis, possible axis: lx, ly, " 106 | "rx, ry, dx, dy\n"); 107 | printf("\nIf you are experiencing an error, try running the program as root."); 108 | 109 | #ifdef DRIBBLE_MODE 110 | printf(" -d [VALUE] pass parameter for dribble cam. Range " 111 | "0 to 255\n"); 112 | #endif 113 | printf("\n"); 114 | return 0; 115 | } 116 | 117 | if (show_version) { 118 | std::cout << "Version is " << PROCON_DRIVER_VERSION << std::endl; 119 | return 0; 120 | } 121 | 122 | printf("\n--------------------------------------------------------------------" 123 | "------\n"); 124 | printf("| "); 125 | printf("%c[%d;%dmNintendo Switch Pro-Controller USB uinput driver" 126 | ".%c[%dm ", 127 | 27, 1, 32, 27, 0); 128 | printf("%c[%d;%dmVersion: ", 27, 1, 36); 129 | printf(PROCON_DRIVER_VERSION); 130 | printf("%c[%dm ", 27, 0); 131 | 132 | printf("%s " 133 | "|\n-------------------------------------------------------------------" 134 | "-------", 135 | KNRM); 136 | printf("\n\n%s", KNRM); 137 | #ifdef DRIBBLE_MODE 138 | printf("%c[%d;%dmDribble mode enabled!%c[%dm \n\n", 27, 1, 36, 27, 0); 139 | // if(found_dribble_cam_value) { 140 | // printf("VALUE: %i", dribble_cam_value); 141 | // } 142 | #endif 143 | fflush(stdout); 144 | ProController controller; 145 | hid_init(); 146 | hid_device *controller_ptr; 147 | hid_device_info *devs = 148 | hid_enumerate(NINTENDO_ID, PROCON_ID); // Don't trust hidapi, returns 149 | // non-matching devices sometimes 150 | // (*const to prevent compiler from 151 | // optimizing away) 152 | hid_device_info *iter = devs; 153 | unsigned short n_controller = 0; 154 | bool controller_found = false; 155 | 156 | bool opened = false; 157 | bool bad_data = false; 158 | 159 | 160 | // pass arguments to controller 161 | if (force_calibration) { 162 | controller.read_calibration_from_file = false; 163 | } 164 | controller.invert_LX = invert_lx; 165 | controller.invert_LY = invert_ly; 166 | controller.invert_RX = invert_rx; 167 | controller.invert_RY = invert_ry; 168 | controller.invert_DX = invert_dx; 169 | controller.invert_DY = invert_dy; 170 | controller.swap_buttons = swap_buttons; 171 | #ifdef DRIBBLE_MODE 172 | if (found_dribble_cam_value) { 173 | controller.dribble_mode_value = dribble_cam_value; 174 | } 175 | #endif 176 | 177 | // OPEN PHASE 178 | do { 179 | opened = false; 180 | bad_data = false; 181 | if (iter != nullptr) { 182 | if (iter->product_id == PROCON_ID && iter->vendor_id == NINTENDO_ID) { 183 | // open & test for timeout in read! 184 | int ret = controller.open_device(iter->vendor_id, iter->product_id, 185 | iter->serial_number, n_controller + 1); 186 | opened = ret == 0; 187 | if (!opened) { // read timed out 188 | 189 | if (ret == -1) { 190 | ProController::red(); 191 | printf("Invalid device pointer. Aborting!\n"); 192 | ProController::normal(); 193 | return -1; 194 | } 195 | ProController::magenta(); 196 | printf("Failed to open controller, error code %d, trying again...\n", 197 | ret); 198 | ProController::normal(); 199 | controller.close_device(); 200 | usleep(1000 * 10); 201 | continue; 202 | } else { 203 | // TEST FOR BAD DATA 204 | for (size_t i = 0; i < TEST_BAD_DATA_CYCLES; ++i) { 205 | if (controller.try_read_bad_data() != 0) { 206 | ProController::magenta(); 207 | printf("Detected bad data stream. Trying again...\n"); 208 | ProController::normal(); 209 | controller.close_device(); 210 | bad_data = true; 211 | usleep(1000 * 10); 212 | break; 213 | } 214 | } 215 | } 216 | } 217 | } else { 218 | ProController::red(); 219 | printf("No controller found...\n"); 220 | ProController::normal(); 221 | return -1; 222 | } 223 | 224 | } while (!opened || bad_data); 225 | 226 | if (controller.is_opened) { 227 | ProController::green(); 228 | printf("Opened controller!\n"); 229 | 230 | if (controller.uinput_create() < 0) { 231 | ProController::red(); 232 | printf("Failed to open uinput device!\n"); 233 | ProController::normal(); 234 | } 235 | 236 | if (!controller.read_calibration_from_file || 237 | !controller.calibration_file_exists()) { 238 | ProController::blue(); 239 | printf("Now entering calibration mode. \n"); 240 | ProController::cyan(); 241 | printf("%c[%d;%dmMove both control sticks to their maximum positions (i.e. turn them in a circle once slowly.), then press the " 242 | "square 'share' button!\n%c[%dm", 243 | 27, 1, 36, 27, 0); 244 | ProController::normal(); 245 | } 246 | } 247 | 248 | // controller.u_setup(); 249 | 250 | while (true) { 251 | if (!controller.calibrated) { 252 | while (!controller.calibrated) { 253 | controller.calibrate(); 254 | } 255 | ProController::green(); 256 | printf("Calibrated Controller! Now entering input mode!\n"); 257 | ProController::normal(); 258 | } 259 | 260 | if (controller.is_opened) { 261 | if (controller.poll_input() < 0) 262 | return -1; 263 | } 264 | } 265 | 266 | // hid_exit; 267 | for (short unsigned i = 0; i < MAX_N_CONTROLLERS; ++i) { 268 | controller.close_device(); 269 | controller.uinput_destroy(); 270 | } 271 | printf("\n"); 272 | return 0; 273 | } 274 | -------------------------------------------------------------------------------- /src/procon.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCON_DRIVER_H 2 | #define PROCON_DRIVER_H 3 | 4 | // #define DRIBBLE_MODE // game-specific hack. does not belong here! 5 | 6 | #include "hidapi.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define PROCON_DRIVER_VERSION "1.0 alpha2" 24 | 25 | #define KNRM "\x1B[0m" 26 | #define KRED "\x1B[31m" 27 | #define KGRN "\x1B[32m" 28 | #define KYEL "\x1B[33m" 29 | #define KBLU "\x1B[34m" 30 | #define KMAG "\x1B[35m" 31 | #define KCYN "\x1B[36m" 32 | #define KWHT "\x1B[37m" 33 | 34 | #define PROCON_ID 0x2009 35 | #define NINTENDO_ID 0x057E 36 | 37 | #define MAX_N_CONTROLLERS 4 38 | 39 | class ProController { 40 | 41 | enum BUTTONS { 42 | d_left, 43 | d_right, 44 | d_up, 45 | d_down, 46 | A, 47 | B, 48 | X, 49 | Y, 50 | plus, 51 | minus, 52 | home, 53 | share, 54 | L1, 55 | L2, 56 | L3, 57 | R1, 58 | R2, 59 | R3, 60 | None 61 | }; 62 | 63 | static constexpr uint8_t led_command{0x30}; 64 | static constexpr uint8_t get_input{0x1f}; 65 | static constexpr uint8_t center{0x7e}; 66 | static constexpr size_t exchange_length{0x400}; 67 | using exchange_array = std::array; 68 | 69 | public: 70 | static const uint8_t bit_position(ProController::BUTTONS button) { 71 | switch (button) { 72 | case d_left: 73 | return 0x04; 74 | break; 75 | case d_right: 76 | return 0x03; 77 | break; 78 | case d_up: 79 | return 0x02; 80 | break; 81 | case d_down: 82 | return 0x01; 83 | break; 84 | case A: 85 | return 0x04; 86 | break; 87 | case B: 88 | return 0x03; 89 | break; 90 | case X: 91 | return 0x02; 92 | break; 93 | case Y: 94 | return 0x01; 95 | break; 96 | case plus: 97 | return 0x02; 98 | break; 99 | case minus: 100 | return 0x01; 101 | break; 102 | case home: 103 | return 0x05; 104 | break; 105 | case share: 106 | return 0x06; 107 | break; 108 | case L1: 109 | return 0x07; 110 | break; 111 | case L2: 112 | return 0x08; 113 | break; 114 | case L3: 115 | return 0x04; 116 | break; 117 | case R1: 118 | return 0x07; 119 | break; 120 | case R2: 121 | return 0x08; 122 | break; 123 | case R3: 124 | return 0x03; 125 | break; 126 | case None: 127 | return 0x00; 128 | break; 129 | default: 130 | red(); 131 | printf("ERROR: Tried to find bitpos of unknown button!\n"); 132 | normal(); 133 | return 0x00; 134 | break; 135 | } 136 | } 137 | 138 | static const uint8_t byte_button_value(ProController::BUTTONS button) { 139 | switch (button) { 140 | case d_left: 141 | return 0x08; 142 | break; 143 | case d_right: 144 | return 0x04; 145 | break; 146 | case d_up: 147 | return 0x02; 148 | break; 149 | case d_down: 150 | return 0x01; 151 | break; 152 | case A: 153 | return 0x08; 154 | break; 155 | case B: 156 | return 0x04; 157 | break; 158 | case X: 159 | return 0x02; 160 | break; 161 | case Y: 162 | return 0x01; 163 | break; 164 | case plus: 165 | return 0x02; 166 | break; 167 | case minus: 168 | return 0x01; 169 | break; 170 | case home: 171 | return 0x10; 172 | break; 173 | case share: 174 | return 0x20; 175 | break; 176 | case L1: 177 | return 0x40; 178 | break; 179 | case L2: 180 | return 0x80; 181 | break; 182 | case L3: 183 | return 0x08; 184 | break; 185 | case R1: 186 | return 0x40; 187 | break; 188 | case R2: 189 | return 0x80; 190 | break; 191 | case R3: 192 | return 0x04; 193 | break; 194 | case None: 195 | return 0x00; 196 | break; 197 | default: 198 | red(); 199 | printf("ERROR: Tried to find bitpos of unknown button!\n"); 200 | normal(); 201 | return 0x00; 202 | break; 203 | } 204 | } 205 | 206 | static const uint8_t data_address(ProController::BUTTONS button) { 207 | switch (button) { 208 | case d_left: 209 | return 0x0f; 210 | break; 211 | case d_right: 212 | return 0x0f; 213 | break; 214 | case d_up: 215 | return 0x0f; 216 | break; 217 | case d_down: 218 | return 0x0f; 219 | break; 220 | case A: 221 | return 0x0d; 222 | break; 223 | case B: 224 | return 0x0d; 225 | break; 226 | case X: 227 | return 0x0d; 228 | break; 229 | case Y: 230 | return 0x0d; 231 | break; 232 | case plus: 233 | return 0x0e; 234 | break; 235 | case minus: 236 | return 0x0e; 237 | break; 238 | case home: 239 | return 0x0e; 240 | break; 241 | case share: 242 | return 0x0e; 243 | break; 244 | case L1: 245 | return 0x0f; 246 | break; 247 | case L2: 248 | return 0x0f; 249 | break; 250 | case L3: 251 | return 0x0e; 252 | break; 253 | case R1: 254 | return 0x0d; 255 | break; 256 | case R2: 257 | return 0x0d; 258 | break; 259 | case R3: 260 | return 0x0e; 261 | break; 262 | case None: 263 | return 0x00; 264 | break; 265 | default: 266 | red(); 267 | printf("ERROR: Tried to find data adress of unknown button!\n"); 268 | normal(); 269 | return 0x00; 270 | break; 271 | } 272 | } 273 | 274 | // void timer() { 275 | 276 | // using namespace std; 277 | // clock_t now = clock(); 278 | 279 | // double elapsed_secs = double(now - last_time) / CLOCKS_PER_SEC; 280 | 281 | // last_time = now; 282 | 283 | // printf("Time for last %u polls: %f seconds\n", n_print_cycle, 284 | // elapsed_secs); 285 | // printf("Bad 0x00: %u\nBad 0x30: %u\n\n", n_bad_data_thirty, 286 | // n_bad_data_zero); 287 | 288 | // print_cycle_counter = 0; 289 | // n_bad_data_thirty = 0; 290 | // n_bad_data_zero = 0; 291 | // } 292 | 293 | template 294 | exchange_array send_command(uint8_t command, 295 | std::array const &data) { 296 | std::array buffer; 297 | buffer.fill(0); 298 | buffer[0x0] = 0x80; 299 | buffer[0x1] = 0x92; 300 | buffer[0x3] = 0x31; 301 | buffer[0x8] = command; 302 | if (length > 0) { 303 | memcpy(buffer.data() + 0x9, data.data(), length); 304 | } 305 | return exchange(buffer); 306 | } 307 | 308 | template 309 | exchange_array exchange(std::array const &data, 310 | bool timed = false, int *status = nullptr) { 311 | 312 | if (!controller_ptr) { 313 | red(); 314 | printf("ERROR: controller_ptr is nullptr!\n"); 315 | normal(); 316 | return {}; 317 | } 318 | 319 | if (hid_write(controller_ptr, data.data(), length) < 0) { 320 | red(); 321 | printf( 322 | "ERROR: read() returned -1!\nDid you disconnect the controller?\n"); 323 | normal(); 324 | throw - 1; 325 | return {}; 326 | } 327 | 328 | std::array ret; 329 | ret.fill(0); 330 | if (!timed) 331 | hid_read(controller_ptr, ret.data(), exchange_length); 332 | else { 333 | 334 | if (hid_read_timeout(controller_ptr, ret.data(), exchange_length, 100) == 335 | 0) { 336 | // failed to read! 337 | if (status) { 338 | *status = -1; 339 | return {}; 340 | } 341 | } 342 | } 343 | if (status) { 344 | *status = 0; 345 | } 346 | return ret; 347 | } 348 | 349 | template 350 | exchange_array send_subcommand(uint8_t command, uint8_t subcommand, 351 | std::array const &data) { 352 | std::array buffer{ 353 | static_cast(rumble_counter++ & 0xF), 354 | 0x00, 355 | 0x01, 356 | 0x40, 357 | 0x40, 358 | 0x00, 359 | 0x01, 360 | 0x40, 361 | 0x40, 362 | subcommand}; 363 | if (length > 0) { 364 | memcpy(buffer.data() + 10, data.data(), length); 365 | } 366 | return send_command(command, buffer); 367 | } 368 | 369 | // void print_sticks(const uint8_t &data0, const uint8_t &data1, const uint8_t 370 | // &data2, 371 | // const uint8_t &data3, const uint8_t &data4, 372 | // const uint8_t &data5) { 373 | // uint8_t left_x = ((data1 & 0x0F) << 4) | ((data0 & 0xF0) >> 4); 374 | // uint8_t left_y = data2; 375 | // uint8_t right_x = ((data4 & 0x0F) << 4) | ((data3 & 0xF0) >> 4); 376 | // uint8_t right_y = data5; 377 | 378 | // map_sticks(left_x, left_y, right_x, right_y); 379 | 380 | // clear_console(); 381 | // yellow(); 382 | // printf("left_x %d\n", left_x); 383 | // printf("left_y %d\n", left_y); 384 | // printf("right_x %d\n", right_x); 385 | // printf("right_y %d\n\n", right_y); 386 | // normal(); 387 | 388 | // // if(left_x == 0x00 || left_y == 0x00 || right_x == 0x00 || right_y == 389 | // 0x00 390 | // // ) { 391 | // // return -1; 392 | // // } 393 | // // return 0; 394 | // } 395 | 396 | // void print_buttons(const uint8_t &left, const uint8_t &mid, const uint8_t 397 | // &right) { 398 | // // uint8_t left = buttons[0]; 399 | // // uint8_t mid = buttons[1]; 400 | // // uint8_t right = buttons[2]; 401 | 402 | // if (left & byte_button_value(d_left)) 403 | // printf("d_left\n"); 404 | // if (left & byte_button_value(d_right)) 405 | // printf("d_right\n"); 406 | // if (left & byte_button_value(d_up)) 407 | // printf("d_up\n"); 408 | // if (left & byte_button_value(d_down)) 409 | // printf("d_down\n"); 410 | // if (left & byte_button_value(L1)) 411 | // printf("L1\n"); 412 | // if (left & byte_button_value(L2)) 413 | // printf("L2\n"); 414 | // if (mid & byte_button_value(L3)) 415 | // printf("L3\n"); 416 | // if (mid & byte_button_value(R3)) 417 | // printf("R3\n"); 418 | // if (mid & byte_button_value(share)) 419 | // printf("share\n"); 420 | // if (mid & byte_button_value(home)) { 421 | // printf("home\n"); 422 | // } 423 | // if (mid & byte_button_value(plus)) 424 | // printf("plus\n"); 425 | // if (mid & byte_button_value(minus)) 426 | // printf("minus\n"); 427 | // if (right & byte_button_value(A)) 428 | // printf("A\n"); 429 | // if (right & byte_button_value(B)) 430 | // printf("B\n"); 431 | // if (right & byte_button_value(X)) 432 | // printf("X\n"); 433 | // if (right & byte_button_value(Y)) 434 | // printf("Y\n"); 435 | // if (right & byte_button_value(R1)) 436 | // printf("R1\n"); 437 | // if (right & byte_button_value(R2)) 438 | // printf("R2\n"); 439 | // } 440 | 441 | void clear_console() { system("clear"); } 442 | 443 | exchange_array send_rumble(uint8_t large_motor, uint8_t small_motor) { 444 | std::array buf{static_cast(rumble_counter++ & 0xF), 445 | 0x80, 446 | 0x00, 447 | 0x40, 448 | 0x40, 449 | 0x80, 450 | 0x00, 451 | 0x40, 452 | 0x40}; 453 | if (large_motor != 0) { 454 | buf[1] = buf[5] = 0x08; 455 | buf[2] = buf[6] = large_motor; 456 | } else if (small_motor != 0) { 457 | buf[1] = buf[5] = 0x10; 458 | buf[2] = buf[6] = small_motor; 459 | } 460 | exchange_array ret = send_command(0x10, buf); 461 | print_exchange_array(ret); 462 | return ret; 463 | } 464 | 465 | int poll_input() { 466 | // print_cycle_counter++; 467 | // if(print_cycle_counter++ > n_print_cycle) { 468 | // timer(); 469 | // } 470 | if (!controller_ptr) { 471 | printf("%sERROR: Controller pointer is nullptr%s\n", KRED, KNRM); 472 | return -1; 473 | } 474 | 475 | auto dat = send_command(get_input, empty); 476 | 477 | if (detect_useless_data(dat[0])) { 478 | // printf("detected useless data!\n"); 479 | return 0; 480 | } 481 | 482 | send_subcommand(0x1, led_command, led_calibrated); // XXX way too often 483 | 484 | if (dat[0x0e] & byte_button_value(home) && 485 | dat[0x0e] & byte_button_value(share)) { 486 | decalibrate(); 487 | } 488 | 489 | uinput_manage_buttons(dat[0x0f], dat[0x0e], dat[0x0d]); 490 | uinput_manage_joysticks(dat[0x10], dat[0x11], dat[0x12], dat[0x13], 491 | dat[0x14], dat[0x15]); 492 | uinput_manage_dpad(dat[0x0f]); 493 | 494 | // print_buttons(dat[0x0f], dat[0x0e], dat[0x0d]); 495 | // print_sticks(dat[0x10], dat[0x11], dat[0x12], dat[0x13], dat[0x14], 496 | // dat[0x15]); 497 | // print_exchange_array(dat); 498 | return 0; 499 | } 500 | 501 | #ifdef DRIBBLE_MODE 502 | void toggle_dribble_mode() { dribble_mode = !dribble_mode; } 503 | #endif 504 | 505 | void calibrate() { 506 | if (read_calibration_from_file) { 507 | std::ifstream myReadFile; 508 | uint8_t output[8]; 509 | myReadFile.open("procon_calibration_data", 510 | std::ios::in | std::ios::binary); 511 | if (myReadFile) { 512 | 513 | // while (!myReadFile.eof()) 514 | 515 | myReadFile.read((char *)&left_x_min, sizeof(uint8_t)); 516 | myReadFile.read((char *)&left_x_max, sizeof(uint8_t)); 517 | myReadFile.read((char *)&left_y_min, sizeof(uint8_t)); 518 | myReadFile.read((char *)&left_y_max, sizeof(uint8_t)); 519 | myReadFile.read((char *)&right_x_min, sizeof(uint8_t)); 520 | myReadFile.read((char *)&right_x_max, sizeof(uint8_t)); 521 | myReadFile.read((char *)&right_y_min, sizeof(uint8_t)); 522 | myReadFile.read((char *)&right_y_max, sizeof(uint8_t)); 523 | 524 | green(); 525 | printf("Read calibration data from file! "); 526 | cyan(); 527 | printf("Press 'share' and 'home' to calibrate again or start with " 528 | "--calibrate or -c.\n"); 529 | normal(); 530 | 531 | calibrated = true; 532 | // send_rumble(0,255); 533 | // send_subcommand(0x1, led_command, led_calibrated); 534 | 535 | return; 536 | } 537 | 538 | myReadFile.close(); 539 | } 540 | 541 | if (!controller_ptr) { 542 | printf("%sERROR: Controller pointer is nullptr%s\n", KRED, KNRM); 543 | return; 544 | } 545 | 546 | auto dat = send_command(get_input, empty); 547 | 548 | if (detect_useless_data(dat[0])) { 549 | // printf("detected useless data!\n"); 550 | return; 551 | } 552 | 553 | // print_buttons(dat[0x0f], dat[0x0e], dat[0x0d]); 554 | // print_sticks(dat[0x10], dat[0x11], dat[0x12], dat[0x13], dat[0x14], 555 | // dat[0x15]); 556 | // print_exchange_array(dat); 557 | 558 | send_subcommand(0x1, led_command, led_calibration); // XXX way too often 559 | if (!share_button_free) { 560 | if (!(dat[0x0e] & byte_button_value(share))) { 561 | share_button_free = true; 562 | } 563 | } else { 564 | bool cal = do_calibrate(dat[0x10], dat[0x11], dat[0x12], dat[0x13], 565 | dat[0x14], dat[0x15], dat[0x0e]); 566 | if (cal) { 567 | // send_rumble(0,255); 568 | calibrated = true; 569 | send_subcommand(0x1, led_command, led_calibrated); 570 | // printf("finished calibration\n"); 571 | // usleep(1000000); 572 | 573 | // write calibration data to file 574 | std::ofstream calibration_file; 575 | calibration_file.open("procon_calibration_data", 576 | std::ios::out | std::ios::binary); 577 | calibration_file.write((char *)&left_x_min, sizeof(uint8_t)); 578 | calibration_file.write((char *)&left_x_max, sizeof(uint8_t)); 579 | calibration_file.write((char *)&left_y_min, sizeof(uint8_t)); 580 | calibration_file.write((char *)&left_y_max, sizeof(uint8_t)); 581 | calibration_file.write((char *)&right_x_min, sizeof(uint8_t)); 582 | calibration_file.write((char *)&right_x_max, sizeof(uint8_t)); 583 | calibration_file.write((char *)&right_y_min, sizeof(uint8_t)); 584 | calibration_file.write((char *)&right_y_max, sizeof(uint8_t)); 585 | calibration_file.close(); 586 | green(); 587 | printf("Wrote calibration data to file!\n"); 588 | normal(); 589 | } 590 | } 591 | 592 | // std::ofstream out("calibration_data"); 593 | // if (!out) 594 | // { 595 | // return; 596 | // } 597 | 598 | // printf("wrote text\n"); 599 | 600 | // out.close(); 601 | } 602 | 603 | bool do_calibrate(const uint8_t &stick0, const uint8_t &stick1, 604 | const uint8_t &stick2, const uint8_t &stick3, 605 | const uint8_t &stick4, const uint8_t &stick5, 606 | const uint8_t &mid_buttons) { 607 | uint8_t left_x = ((stick1 & 0x0F) << 4) | ((stick0 & 0xF0) >> 4); 608 | uint8_t left_y = stick2; 609 | uint8_t right_x = ((stick4 & 0x0F) << 4) | ((stick3 & 0xF0) >> 4); 610 | uint8_t right_y = stick5; 611 | 612 | // invert 613 | if (invert_LX) { 614 | left_x = 255 - left_x; 615 | } 616 | if (invert_LY) { 617 | left_y = 255 - left_y; 618 | } 619 | if (invert_RX) { 620 | right_x = 255 - right_x; 621 | } 622 | if (invert_RY) { 623 | right_y = 255 - right_y; 624 | } 625 | 626 | left_x_min = (left_x < left_x_min) ? left_x : left_x_min; 627 | left_y_min = (left_y < left_y_min) ? left_y : left_y_min; 628 | right_x_min = (right_x < right_x_min) ? right_x : right_x_min; 629 | right_y_min = (right_y < right_y_min) ? right_y : right_y_min; 630 | left_x_max = (left_x > left_x_max) ? left_x : left_x_max; 631 | left_y_max = (left_y > left_y_max) ? left_y : left_y_max; 632 | right_x_max = (right_x > right_x_max) ? right_x : right_x_max; 633 | right_y_max = (right_y > right_y_max) ? right_y : right_y_max; 634 | 635 | // clear_console(); 636 | // printf("left_x_min: %u\n", left_x_min); 637 | // printf("left_y_min: %u\n", left_y_min); 638 | // printf("right_x_min: %u\n", right_x_min); 639 | // printf("right_y_min: %u\n", right_y_min); 640 | // printf("left_x_max: %u\n", left_x_max); 641 | // printf("left_y_max: %u\n", left_y_max); 642 | // printf("right_x_max: %u\n", right_x_max); 643 | // printf("right_y_max: %u\n\n", right_y_max); 644 | // print_calibration_values(); 645 | 646 | return (mid_buttons & byte_button_value(share)); 647 | } 648 | 649 | void print_calibration_values() { 650 | std::cout << "LX: " << (unsigned int)left_x_min << "," 651 | << (unsigned int)left_x_max 652 | << " LY: " << (unsigned int)left_y_min << "," 653 | << (unsigned int)left_y_max 654 | << " RX: " << (unsigned int)right_x_min << "," 655 | << (unsigned int)right_x_max 656 | << " RY: " << (unsigned int)right_y_min << "," 657 | << (unsigned int)right_y_max << " \r"; 658 | } 659 | 660 | void decalibrate() { 661 | left_x_min = center; 662 | left_x_max = center; 663 | left_y_min = center; 664 | left_x_max = center; 665 | right_x_min = center; 666 | right_x_max = center; 667 | right_y_min = center; 668 | right_x_max = center; 669 | calibrated = false; 670 | send_subcommand(0x1, led_command, led_calibration); 671 | magenta(); 672 | printf("Controller decalibrated!\n"); 673 | cyan(); 674 | printf("%c[%d;%dmPerform calibration again and press the square 'share' " 675 | "button!\n%c[%dm", 676 | 27, 1, 36, 27, 0); 677 | normal(); 678 | read_calibration_from_file = false; 679 | share_button_free = false; 680 | // usleep(1000*1000); 681 | } 682 | 683 | const void map_sticks(uint8_t &left_x, uint8_t &left_y, uint8_t &right_x, 684 | uint8_t &right_y) { 685 | left_x = (uint8_t)(clamp((float)(left_x - left_x_min) / 686 | (float)(left_x_max - left_x_min) * 255.f)); 687 | left_y = (uint8_t)(clamp((float)(left_y - left_y_min) / 688 | (float)(left_y_max - left_y_min) * 255.f)); 689 | right_x = (uint8_t)(clamp((float)(right_x - right_x_min) / 690 | (float)(right_x_max - right_x_min) * 255.f)); 691 | right_y = (uint8_t)(clamp((float)(right_y - right_y_min) / 692 | (float)(right_y_max - right_y_min) * 255.f)); 693 | } 694 | 695 | static const float clamp(float inp) { 696 | if (inp < 0.5f) 697 | return 0.5f; 698 | if (inp > 254.5f) { 699 | return 254.5f; 700 | } 701 | return inp; 702 | } 703 | static const int clamp_int(int inp) { 704 | if (inp < 0) 705 | return 0; 706 | if (inp > 255) { 707 | return 255; 708 | } 709 | return inp; 710 | } 711 | 712 | int try_read_bad_data() { 713 | 714 | if (!controller_ptr) { 715 | printf("%sERROR: Controller pointer is nullptr%s\n", KRED, KNRM); 716 | return -1; 717 | } 718 | 719 | auto dat = send_command(get_input, empty); 720 | 721 | if (detect_useless_data(dat[0])) { 722 | return 0; 723 | } 724 | 725 | if (detect_bad_data(dat[0], dat[1])) { 726 | // print_exchange_array(dat); 727 | return -1; 728 | } 729 | 730 | return 0; 731 | } 732 | 733 | /* Hackishly detects when the controller is trapped in a bad loop. 734 | Nothing to do here, need to reopen device :(*/ 735 | bool detect_bad_data(const uint8_t &dat1, const uint8_t &dat2) { 736 | return (dat2 == 0x01 && dat1 == 0x81) ? true : bad_data_detected; 737 | } 738 | 739 | /* If this returns true, there is no controller information in this package, 740 | * we can skip it*/ 741 | bool detect_useless_data(const uint8_t &dat) { 742 | if (dat == 0x30) 743 | n_bad_data_thirty++; 744 | if (dat == 0x00) 745 | n_bad_data_zero++; 746 | return (dat == 0x30 || dat == 0x00); 747 | } 748 | 749 | void print_exchange_array(exchange_array arr) { 750 | bool redcol = false; 751 | if (arr[0] != 0x30) 752 | yellow(); 753 | else { 754 | red(); 755 | redcol = true; 756 | } 757 | for (size_t i = 0; i < 20; ++i) { 758 | if (arr[i] == 0x00) { 759 | blue(); 760 | } else { 761 | if (redcol) { 762 | red(); 763 | } else { 764 | yellow(); 765 | } 766 | } 767 | printf("%02X ", arr[i]); 768 | } 769 | normal(); 770 | printf("\n"); 771 | fflush(stdout); 772 | } 773 | 774 | int read(hid_device *device, uint8_t *data, size_t size) { 775 | int ret = hid_read(device, data, size); 776 | if (ret < 0) { 777 | printf("%sERROR: Couldn't read from device nr. %u%s\n", KRED, 778 | n_controller, KNRM); 779 | } 780 | return ret; 781 | } 782 | 783 | int open_device(unsigned short vendor_id, unsigned short product_id, 784 | const wchar_t *serial_number, unsigned short n_controll) { 785 | controller_ptr = hid_open(vendor_id, product_id, serial_number); 786 | // controller_ptr = hid_open_path("/dev/input/hidraw0"); 787 | is_opened = true; 788 | 789 | 790 | //printf("SERIAL NUMBER: %u\n", serial_number); 791 | if (!controller_ptr) { 792 | return -1; 793 | } 794 | // hid_device_info *info = hid_open(vendor_id, product_id, serial_number); 795 | // std::cout<< "PATH: " << info->path << std::endl;; 796 | 797 | n_controller = n_controll; 798 | ven_id = vendor_id; 799 | prod_id = product_id; 800 | 801 | // if (false) 802 | // { //!exchange(handshake)) { //need std::optional 803 | // red(); 804 | // printf("ERROR: exchange handshake failed!\n"); 805 | // normal(); 806 | // } 807 | 808 | // set_non_blocking(); 809 | 810 | exchange(switch_baudrate); 811 | exchange(handshake); 812 | 813 | // the next part will sometimes fail, then need to reopen device via hidapi 814 | int read_failed; 815 | exchange(hid_only_mode, true, &read_failed); 816 | if (read_failed < 0) { 817 | return -2; 818 | } 819 | 820 | send_subcommand(0x1, led_command, led_calibration); 821 | 822 | usleep(100 * 1000); 823 | 824 | return 0; 825 | } 826 | 827 | void set_non_blocking() { 828 | if (hid_set_nonblocking(controller_ptr, 1) < 0) { 829 | printf("%sERROR: Couldn't set controller %u to non-blocking%s\n", KRED, 830 | n_controller, KNRM); 831 | } 832 | } 833 | 834 | void set_blocking() { 835 | if (hid_set_nonblocking(controller_ptr, 0) < 0) { 836 | printf("%sERROR: Couldn't set controller %u to blocking%s\n", KRED, 837 | n_controller, KNRM); 838 | } 839 | } 840 | 841 | void close_device() { 842 | if (!is_opened) 843 | return; 844 | is_opened = false; 845 | if (controller_ptr) { 846 | hid_close(controller_ptr); 847 | blue(); 848 | // printf("Closed controller nr. %u\n", n_controller); 849 | normal(); 850 | } 851 | } 852 | 853 | // void blink() { 854 | // if (++blink_counter > blink_length) { 855 | // blink_counter = 0; 856 | // if (++blink_position >= blink_array.size()) { 857 | // blink_position = 0; 858 | // } 859 | // } 860 | // std::array blink_command{{blink_array[blink_position]}}; 861 | // send_subcommand(0x1, led_command, blink_command); 862 | // } 863 | 864 | //------------------------- 865 | // UINPUT 866 | //------------------------- 867 | 868 | void uinput_manage_dpad(const char &left) { 869 | bool b_d_left; 870 | bool b_d_right; 871 | bool b_d_up; 872 | bool b_d_down; 873 | 874 | // invert 875 | if (!invert_DX) { 876 | b_d_left = left & byte_button_value(d_left); 877 | b_d_right = left & byte_button_value(d_right); 878 | } else { 879 | b_d_left = left & byte_button_value(d_right); 880 | b_d_right = left & byte_button_value(d_left); 881 | } 882 | 883 | if (!invert_DY) { 884 | b_d_up = left & byte_button_value(d_up); 885 | b_d_down = left & byte_button_value(d_down); 886 | } else { 887 | b_d_up = left & byte_button_value(d_down); 888 | b_d_down = left & byte_button_value(d_up); 889 | } 890 | 891 | memset(&uinput_event, 0, sizeof(uinput_event)); 892 | gettimeofday(&uinput_event.time, NULL); 893 | 894 | if (b_d_left) { 895 | uinput_write_single_joystick(-1, ABS_HAT0X); 896 | } else if (b_d_right) { 897 | uinput_write_single_joystick(1, ABS_HAT0X); 898 | } else if (!b_d_left && !b_d_right) { 899 | uinput_write_single_joystick(0, ABS_HAT0X); 900 | } 901 | if (b_d_down) { 902 | uinput_write_single_joystick(-1, ABS_HAT0Y); 903 | } else if (b_d_up) { 904 | uinput_write_single_joystick(1, ABS_HAT0Y); 905 | } else if (!b_d_down && !b_d_up) { 906 | uinput_write_single_joystick(0, ABS_HAT0Y); 907 | } 908 | 909 | // send report 910 | uinput_event.type = EV_SYN; 911 | uinput_event.code = SYN_REPORT; 912 | uinput_event.value = 0; 913 | write(uinput_fd, &uinput_event, sizeof(uinput_event)); 914 | } 915 | 916 | void uinput_manage_buttons(const char &left, const char &mid, 917 | const char &right) { 918 | 919 | // bool b_d_left = left & byte_button_value(d_left); 920 | // bool b_d_right = left & byte_button_value(d_right); 921 | // bool b_d_up = left & byte_button_value(d_up); 922 | // bool b_d_down = left & byte_button_value(d_down); 923 | bool b_L1 = left & byte_button_value(L1); 924 | bool b_L2 = left & byte_button_value(L2); 925 | bool b_L3 = mid & byte_button_value(L3); 926 | bool b_R1 = right & byte_button_value(R1); 927 | bool b_R2 = right & byte_button_value(R2); 928 | bool b_R3 = mid & byte_button_value(R3); 929 | bool b_share = mid & byte_button_value(share); 930 | bool b_home = mid & byte_button_value(home); 931 | bool b_plus = mid & byte_button_value(plus); 932 | bool b_minus = mid & byte_button_value(minus); 933 | 934 | bool b_a, b_b, b_x, b_y; 935 | if (!swap_buttons) { 936 | b_a = right & byte_button_value(A); 937 | b_b = right & byte_button_value(B); 938 | b_x = right & byte_button_value(X); 939 | b_y = right & byte_button_value(Y); 940 | } else { 941 | b_a = right & byte_button_value(B); 942 | b_b = right & byte_button_value(A); 943 | b_x = right & byte_button_value(Y); 944 | b_y = right & byte_button_value(X); 945 | } 946 | 947 | // press 948 | if (b_a && !last_a) 949 | uinput_button_down(BTN_EAST); 950 | if (b_b && !last_b) 951 | uinput_button_down(BTN_SOUTH); 952 | if (b_x && !last_x) { 953 | uinput_button_down(BTN_WEST); 954 | #ifdef DRIBBLE_MODE // toggle off dribble mode 955 | if (dribble_mode) 956 | toggle_dribble_mode(); 957 | #endif 958 | } 959 | #ifdef DRIBBLE_MODE // toggle dribble mode 960 | if (b_y && !last_y) 961 | toggle_dribble_mode(); 962 | if (b_share && !last_share) // replace button by share 963 | uinput_button_down(BTN_NORTH); 964 | #else 965 | if (b_y && !last_y) 966 | uinput_button_down(BTN_NORTH); 967 | #endif 968 | 969 | // if (b_d_down && !last_d_down) 970 | // uinput_button_down(BTN_DPAD_DOWN); 971 | // if (b_d_up && !last_d_up) 972 | // uinput_button_down(BTN_DPAD_UP); 973 | // if (b_d_left && !last_d_left) 974 | // uinput_button_down(BTN_DPAD_LEFT); 975 | // if (b_d_right && !last_d_right) 976 | // uinput_button_down(BTN_DPAD_RIGHT); 977 | if (b_plus && !last_plus) 978 | uinput_button_down(BTN_START); 979 | if (b_minus && !last_minus) 980 | uinput_button_down(BTN_SELECT); 981 | if (b_L1 && !last_L1) 982 | uinput_button_down(BTN_TL); 983 | // if (b_L2 && !last_L2) 984 | // uinput_button_down(BTN_TL2); 985 | if (b_R1 && !last_R1) 986 | uinput_button_down(BTN_TR); 987 | // if (b_R2 && !last_R2) 988 | // uinput_button_down(BTN_TR2); 989 | if (b_L3 && !last_L3) 990 | uinput_button_down(BTN_THUMBL); 991 | if (b_R3 && !last_R3) 992 | uinput_button_down(BTN_THUMBR); 993 | // if (b_L1 && !last_L1) 994 | // uinput_button_down(BTN_TL); 995 | if (b_home && !last_home) 996 | uinput_button_down(BTN_MODE); 997 | 998 | // release 999 | if (!b_a && last_a) 1000 | uinput_button_release(BTN_EAST); 1001 | if (!b_b && last_b) 1002 | uinput_button_release(BTN_SOUTH); 1003 | if (!b_x && last_x) 1004 | uinput_button_release(BTN_WEST); 1005 | 1006 | #ifdef DRIBBLE_MODE 1007 | if (!b_y && last_y) 1008 | uinput_button_release(BTN_WEST); 1009 | if (!b_share && last_share) 1010 | uinput_button_release(BTN_NORTH); 1011 | #else 1012 | if (!b_y && last_y) 1013 | uinput_button_release(BTN_NORTH); 1014 | #endif 1015 | 1016 | // if (!b_d_down && last_d_down) 1017 | // uinput_button_release(BTN_DPAD_DOWN); 1018 | // if (!b_d_up && last_d_up) 1019 | // uinput_button_release(BTN_DPAD_UP); 1020 | // if (!b_d_left && last_d_left) 1021 | // uinput_button_release(BTN_DPAD_LEFT); 1022 | // if (!b_d_right && last_d_right) 1023 | // uinput_button_release(BTN_DPAD_RIGHT); 1024 | if (!b_plus && last_plus) 1025 | uinput_button_release(BTN_START); 1026 | if (!b_minus && last_minus) 1027 | uinput_button_release(BTN_SELECT); 1028 | if (!b_L1 && last_L1) 1029 | uinput_button_release(BTN_TL); 1030 | // if (!b_L2 && last_L2) 1031 | // uinput_button_release(BTN_TL2); 1032 | if (!b_R1 && last_R1) 1033 | uinput_button_release(BTN_TR); 1034 | // if (!b_R2 && last_R2) 1035 | // uinput_button_release(BTN_TR2); 1036 | if (!b_L3 && last_L3) 1037 | uinput_button_release(BTN_THUMBL); 1038 | if (!b_R3 && last_R3) 1039 | uinput_button_release(BTN_THUMBR); 1040 | // if (!b_L1 && last_L1) 1041 | // uinput_button_release(BTN_TL); 1042 | if (!b_home && last_home) 1043 | uinput_button_release(BTN_MODE); 1044 | 1045 | // last_d_left = b_d_left; 1046 | // last_d_right = b_d_right; 1047 | // last_d_up = b_d_up; 1048 | // last_d_down = b_d_down; 1049 | last_L1 = b_L1; 1050 | // last_L2 = b_L2; 1051 | last_L3 = b_L3; 1052 | last_R1 = b_R1; 1053 | // last_R2 = b_R2; 1054 | last_R3 = b_R3; 1055 | last_share = b_share; 1056 | last_home = b_home; 1057 | last_plus = b_plus; 1058 | last_minus = b_minus; 1059 | last_a = b_a; 1060 | last_b = b_b; 1061 | last_x = b_x; 1062 | last_y = b_y; 1063 | 1064 | // do triggers here as well 1065 | int val = b_L2 ? 255 : 0; 1066 | uinput_write_single_joystick(val, ABS_Z); 1067 | val = b_R2 ? 255 : 0; 1068 | uinput_write_single_joystick(val, ABS_RZ); 1069 | 1070 | // send report 1071 | uinput_event.type = EV_SYN; 1072 | uinput_event.code = SYN_REPORT; 1073 | uinput_event.value = 0; 1074 | write(uinput_fd, &uinput_event, sizeof(uinput_event)); 1075 | } 1076 | 1077 | bool calibration_file_exists() { 1078 | std::ifstream conf("procon_calibration_data"); 1079 | return conf.good(); 1080 | } 1081 | 1082 | void uinput_manage_joysticks(const char &dat0, const char &dat1, 1083 | const char &dat2, const char &dat3, 1084 | const char &dat4, const char &dat5) { 1085 | // extract data 1086 | uint8_t left_x = ((dat1 & 0x0F) << 4) | ((dat0 & 0xF0) >> 4); 1087 | uint8_t left_y = dat2; 1088 | uint8_t right_x = ((dat4 & 0x0F) << 4) | ((dat3 & 0xF0) >> 4); 1089 | uint8_t right_y = dat5; 1090 | 1091 | // invert 1092 | if (invert_LX) { 1093 | left_x = 255 - left_x; 1094 | } 1095 | if (invert_LY) { 1096 | left_y = 255 - left_y; 1097 | } 1098 | if (invert_RX) { 1099 | right_x = 255 - right_x; 1100 | } 1101 | if (invert_RY) { 1102 | right_y = 255 - right_y; 1103 | } 1104 | 1105 | // map data 1106 | map_sticks(left_x, left_y, right_x, right_y); 1107 | 1108 | // write uinput 1109 | memset(&uinput_event, 0, sizeof(uinput_event)); 1110 | 1111 | // left_x = 0; 1112 | // left_y = 127; 1113 | // right_x = 255; 1114 | // right_y = 200; 1115 | gettimeofday(&uinput_event.time, NULL); 1116 | 1117 | uinput_write_single_joystick(left_x, ABS_X); 1118 | uinput_write_single_joystick(left_y, ABS_Y); 1119 | uinput_write_single_joystick(right_x, ABS_RX); 1120 | #ifndef DRIBBLE_MODE 1121 | uinput_write_single_joystick(right_y, ABS_RY); 1122 | #else 1123 | if (dribble_mode) { 1124 | right_y = clamp_int(right_y + dribble_mode_value - 127); 1125 | } 1126 | #endif 1127 | uinput_write_single_joystick(right_y, ABS_RY); 1128 | // send report 1129 | uinput_event.type = EV_SYN; 1130 | uinput_event.code = SYN_REPORT; 1131 | uinput_event.value = 0; 1132 | write(uinput_fd, &uinput_event, sizeof(uinput_event)); 1133 | 1134 | // clear_console(); 1135 | // printf("left_x: %i\n", (int)left_x); 1136 | // printf("left_y: %i\n", (int)left_y); 1137 | // printf("right_x: %i\n", (int)right_x); 1138 | // printf("right_y: %i\n", (int)right_y); 1139 | } 1140 | 1141 | void uinput_write_single_joystick(const int &val, const int &cod) { 1142 | 1143 | uinput_event.type = EV_ABS; 1144 | uinput_event.code = cod; // BTN_EAST; 1145 | uinput_event.value = (int)val; 1146 | 1147 | int ret = write(uinput_fd, &uinput_event, sizeof(uinput_event)); 1148 | if (ret < 0) { 1149 | red(); 1150 | printf("ERROR: write in write_single_joystic() returned %i\nMaybe try running with sudo\n", ret); 1151 | normal(); 1152 | } 1153 | } 1154 | 1155 | void uinput_button_down(const int &cod) { 1156 | 1157 | // press button 1158 | memset(&uinput_event, 0, sizeof(uinput_event)); 1159 | gettimeofday(&uinput_event.time, NULL); 1160 | uinput_event.type = EV_KEY; 1161 | uinput_event.code = cod; // BTN_EAST; 1162 | uinput_event.value = 1; 1163 | int ret = write(uinput_fd, &uinput_event, sizeof(uinput_event)); 1164 | if (ret < 0) { 1165 | red(); 1166 | printf("ERROR: write in button_down() returned %i\nMaybe try running with sudo\n", ret); 1167 | normal(); 1168 | } 1169 | 1170 | // if (ret < 0) 1171 | // { 1172 | // red(); 1173 | // printf("ERROR: write in button_down() send report returned %i\n", ret); 1174 | // normal(); 1175 | // } 1176 | 1177 | // printf("PRessed button %u\n", cod); 1178 | } 1179 | 1180 | void uinput_button_release(const int &cod) { 1181 | // release button 1182 | memset(&uinput_event, 0, sizeof(uinput_event)); 1183 | gettimeofday(&uinput_event.time, NULL); 1184 | uinput_event.type = EV_KEY; 1185 | uinput_event.code = cod; 1186 | uinput_event.value = 0; 1187 | write(uinput_fd, &uinput_event, sizeof(uinput_event)); 1188 | 1189 | // send report 1190 | uinput_event.type = EV_SYN; 1191 | uinput_event.code = SYN_REPORT; 1192 | uinput_event.value = 0; 1193 | write(uinput_fd, &uinput_event, sizeof(uinput_event)); 1194 | } 1195 | 1196 | int uinput_create() { 1197 | uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); 1198 | uinput_rc = ioctl(uinput_fd, UI_GET_VERSION, &uinput_version); 1199 | 1200 | memset(&uinput_device, 0, sizeof(uinput_device)); 1201 | 1202 | 1203 | uinput_device.id.bustype = BUS_USB; 1204 | uinput_device.id.vendor = 0x045e; // Microsoft 1205 | uinput_device.id.product = 0x028e; // XBOX 360 1206 | uinput_device.id.version = 0x110; // dunno but xboxdrv uses this 1207 | strncpy(uinput_device.name, "Switch ProController disguised as XBox360", 1208 | UINPUT_MAX_NAME_SIZE); 1209 | 1210 | 1211 | // buttons 1212 | ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY); 1213 | 1214 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_EAST); 1215 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_SOUTH); 1216 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_NORTH); 1217 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_WEST); 1218 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_MODE); 1219 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_TL); 1220 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_TR); 1221 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_THUMBL); 1222 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_THUMBR); 1223 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_START); 1224 | ioctl(uinput_fd, UI_SET_KEYBIT, BTN_SELECT); 1225 | 1226 | // joysticks 1227 | ioctl(uinput_fd, UI_SET_EVBIT, EV_ABS); 1228 | 1229 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_X); 1230 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_Y); 1231 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_RX); 1232 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_RY); 1233 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_Z); // L2 1234 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_RZ); // R2 1235 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_HAT0X); 1236 | ioctl(uinput_fd, UI_SET_ABSBIT, ABS_HAT0Y); 1237 | 1238 | uinput_device.absmin[ABS_X] = 0; 1239 | uinput_device.absmax[ABS_X] = 255; 1240 | uinput_device.absmin[ABS_Y] = 0; 1241 | uinput_device.absmax[ABS_Y] = 255; 1242 | uinput_device.absmin[ABS_RX] = 0; 1243 | uinput_device.absmax[ABS_RX] = 255; 1244 | uinput_device.absmin[ABS_RY] = 0; 1245 | uinput_device.absmax[ABS_RY] = 255; 1246 | uinput_device.absmin[ABS_Z] = 0; 1247 | uinput_device.absmax[ABS_Z] = 255; 1248 | uinput_device.absmin[ABS_RZ] = 0; 1249 | uinput_device.absmax[ABS_RZ] = 255; 1250 | uinput_device.absmin[ABS_HAT0X] = -1; 1251 | uinput_device.absmax[ABS_HAT0X] = 1; 1252 | uinput_device.absmin[ABS_HAT0Y] = -1; 1253 | uinput_device.absmax[ABS_HAT0Y] = 1; 1254 | 1255 | write(uinput_fd, &uinput_device, sizeof(uinput_device)); 1256 | 1257 | if (ioctl(uinput_fd, UI_DEV_CREATE)) { 1258 | //return -1; 1259 | } 1260 | 1261 | green(); 1262 | printf("Created uinput device!\n"); 1263 | normal(); 1264 | 1265 | return 0; 1266 | } 1267 | 1268 | 1269 | 1270 | void uinput_destroy() { 1271 | ioctl(uinput_fd, UI_DEV_DESTROY); 1272 | 1273 | close(uinput_fd); 1274 | 1275 | yellow(); 1276 | printf("Destroyed uinput device!\n"); 1277 | normal(); 1278 | 1279 | return; 1280 | } 1281 | 1282 | static const void red() { 1283 | printf("%s", KRED); 1284 | fflush(stdout); 1285 | } 1286 | static const void normal() { 1287 | printf("%s", KNRM); 1288 | fflush(stdout); 1289 | } 1290 | static const void blue() { 1291 | printf("%s", KBLU); 1292 | fflush(stdout); 1293 | } 1294 | static const void yellow() { 1295 | printf("%s", KYEL); 1296 | fflush(stdout); 1297 | } 1298 | static const void green() { 1299 | printf("%s", KGRN); 1300 | fflush(stdout); 1301 | } 1302 | static const void magenta() { 1303 | printf("%s", KMAG); 1304 | fflush(stdout); 1305 | } 1306 | static const void cyan() { 1307 | printf("%s", KCYN); 1308 | fflush(stdout); 1309 | } 1310 | std::clock_t last_time; 1311 | 1312 | std::array first{{0x0}}; 1313 | std::array second{{0x0}}; 1314 | std::array third{{0x0}}; 1315 | std::array fourth{{0x0}}; 1316 | std::array fifth{{0x0}}; 1317 | std::array sixth{{0x0}}; 1318 | 1319 | uint8_t rumble_counter{0}; 1320 | const std::array led_calibration{{0xff}}; 1321 | const std::array led_calibrated{{0x01}}; 1322 | const std::array empty{{}}; 1323 | const std::array handshake{{0x80, 0x02}}; 1324 | const std::array switch_baudrate{{0x80, 0x03}}; 1325 | const std::array hid_only_mode{{0x80, 0x04}}; 1326 | // const std::array blink_array{{0x05, 0x10}};//, 0x04, 0x08}}; 1327 | 1328 | // uint blink_position = 0; 1329 | // size_t blink_counter = 0; 1330 | // const size_t blink_length = 50; 1331 | 1332 | bool bad_data_detected = false; 1333 | 1334 | hid_device *controller_ptr; 1335 | 1336 | unsigned short ven_id; 1337 | unsigned short prod_id; 1338 | unsigned short n_controller; 1339 | 1340 | uint n_print_cycle = 1000; 1341 | uint print_cycle_counter = 0; 1342 | uint n_bad_data_zero = 0; 1343 | uint n_bad_data_thirty = 0; 1344 | 1345 | bool is_opened = false; 1346 | bool calibrated = false; 1347 | bool read_calibration_from_file = 1348 | true; // will be set to false in decalibrate or with flags 1349 | bool share_button_free = false; // used for recalibration (press share & home) 1350 | uint8_t left_x_min = 0x7e; 1351 | uint8_t left_y_min = 0x7e; 1352 | uint8_t right_x_min = 0x7e; 1353 | uint8_t right_y_min = 0x7e; 1354 | uint8_t left_x_max = 0x7e; 1355 | uint8_t left_y_max = 0x7e; 1356 | uint8_t right_x_max = 0x7e; 1357 | uint8_t right_y_max = 0x7e; 1358 | 1359 | // bool last_d_left = false; 1360 | // bool last_d_right = false; 1361 | // bool last_d_up = false; 1362 | // bool last_d_down = false; 1363 | bool last_L1 = false; 1364 | // bool last_L2 = false; 1365 | bool last_L3 = false; 1366 | bool last_R1 = false; 1367 | // bool last_R2 = false; 1368 | bool last_R3 = false; 1369 | bool last_share = false; 1370 | bool last_home = false; 1371 | bool last_plus = false; 1372 | bool last_minus = false; 1373 | bool last_a = false; 1374 | bool last_b = false; 1375 | bool last_x = false; 1376 | bool last_y = false; 1377 | 1378 | bool dribble_mode = false; 1379 | int dribble_mode_value = 205; 1380 | 1381 | bool invert_LX = false; 1382 | bool invert_LY = false; 1383 | bool invert_RX = false; 1384 | bool invert_RY = false; 1385 | bool invert_DX = false; 1386 | bool invert_DY = false; 1387 | 1388 | bool swap_buttons = false; 1389 | 1390 | // uinput 1391 | struct uinput_user_dev uinput_device; 1392 | struct input_event uinput_event; 1393 | int uinput_version, uinput_rc, uinput_fd; 1394 | }; 1395 | 1396 | #endif 1397 | --------------------------------------------------------------------------------