Much has been said about the innovative detachable Joy-Con controllers that come with the Nintendo Switch. Well now with my XJoy utility you can use these delightful Bluetooth devices as an input for your favorite PC games that support Xbox controller input.
The accessibility of Joy-Cons
This project arose originally out of my husband’s nerve-damaged wrists — PC gaming mice and even ergonomic mice put too much strain on his wrists. He had an equally bad time with Xbox and PS4 controllers, as most of his strain came from having his hands too close together. Thus to his great dismay my husband was unable to play any PC or console games when his condition worsened — that is, until his Nintendo Switch arrived.
The Switch changed everything for my husband. Because the two Joy-Cons could be separated and held in his left and right hands apart in a resting position, he was able to play games once again, and for hours on end at that!
Still, he wanted to be able to play his old PC games, as the number of titles (and genres) available for the Switch (as with any Nintendo-based platform) can be quite limiting, and he has hundreds of games in his Steam library. Thus I began my quest to map the input from my husband’s Joy-Cons to something he could use to play PC games.
Going it alone — XJoy origins
At first I attempted using the joycon-driver project in conjunction with VJoy. This worked well in some games like Stardew Valley, but the configuration was extremely flaky and only worked in certain games. In particular, it didn’t work at all in BeamNG, which is my husband’s must-play go-to game when he is bored, so this solution was out. This phase was also dominated by a number of Steam-specific bugs in the edit controller window in Steam.
Next I became stubborn and decided to bypass this whole controller-emulation nonsense entirely by writing my own cross-platform Node.js program that used an HID library in conjunction with robotjs to read the Joy-Con input and translate it to mouse and keyboard. I was experimentally able to figure out what parts of the HID data did what by observing the numbers as they came in, and eventually I was able to map out all the controls for both Joy-Cons (this is much more fun than simply looking up what the values are for each button).
This worked extremely well at first, however games like No Man’s Sky, which has different controller mappings for practically every menu and every interaction in the game, I had to start coding in custom “modes” the program would enter into where the controller-to-keyboard-plus-mouse mapping would change based on any number of factors. This all fell apart when there were modes that were impossible to activate (because they depended on the presence of something in-game that I couldn’t detect without analyzing screenshots) without using a hotkey. I estimated that to fully cover all of the functionality in No Man’s Sky, I would need over 45 modes, so I quickly abandoned this approach. All of that said, it was extremely cool controlling a mouse on Linux, Windows, and OSX using a Joy-Con analog stick, and I definitely plan to do something with that project in the future, just not in a gaming-focused sort of way.
A workable solution
After doing some more research, I stumbled upon ViGEm, which is a very well put together, well-maintained virtual Xbox 360 (and DS4) controller library for Windows written in C++. Upon making this discovery, I ported over all of my Joy-Con parsing code to use HIDApi and ViGEm in windows C++. That did the trick. I was able to translate every Joy-Con button over to something on the virtual Xbox controller.
One difficulty I ran into was I realized rather late that I needed to multithread the program, as the left and right Joy-Cons need separate threads to properly maintain their HID-read loops. Once I retrofitted everything to run on two threads (and after adding a mutex), everything worked perfectly — I was able to control arbitrary PC games using my Joy-Cons!