DuroSoft / XJoy

Use Nintendo Switch JoyCons as a virtual Xbox 360 controller in Windows
MIT License
390 stars 56 forks source link

Cannot connect 2 pairs of Joycons #57

Closed AcideA7X closed 3 years ago

AcideA7X commented 3 years ago

Hello! I tried to use 4 joycons to play with a friends and it turns out Xjoy only recognize one pair of Joycons Do have any solution for us? Or is connecting 4 joycons juts impossible for now?

Thank you for your help!

sam0x17 commented 3 years ago

You might be able to jank this into working by running multiple instances of XJoy, but I have never tested this.

The code isn't too complicated and is all contained within one file, so I definitely recommend trying to modify the project to suit your needs if you have any C++ experience or are willing to teach yourself a bit.

If you do get it working the way you want, feel free to submit a pull request!

Good luck!!

DavidEMorales commented 3 years ago

Not sure if you still need this, but for anyone else who stumbles upon this in the future, I just replaced a few lines to get this working.

In the file XJoy.cpp, replace this:

void initialize_left_joycon() {
  struct hid_device_info *left_joycon_info = hid_enumerate(NINTENDO, JOYCON_L);
  if(left_joycon_info != NULL) std::cout << " => found left Joy-Con" << std::endl;
  else {
    std::cout << " => could not find left Joy-Con" << std::endl;
    hid_exit();
    vigem_free(client);
    std::cout << "press [ENTER] to exit" << std::endl;
    getchar();
    exit(1);
  }
  left_joycon = hid_open(NINTENDO, JOYCON_L, left_joycon_info->serial_number);
  if(left_joycon != NULL) std::cout << " => successfully connected to left Joy-Con" << std::endl;
  else {
    std::cout << " => could not connect to left Joy-Con" << std::endl;
    hid_exit();
    vigem_free(client);
    std::cout << "press [ENTER] to exit" << std::endl;
    getchar();
    exit(1);
  }

  setup_joycon(left_joycon, 0x1, 1);
}

void initialize_right_joycon() {
  struct hid_device_info *right_joycon_info = hid_enumerate(NINTENDO, JOYCON_R);
  if(right_joycon_info != NULL) std::cout << " => found right Joy-Con" << std::endl;
  else {
    std::cout << " => could not find right Joy-Con" << std::endl;
    hid_exit();
    vigem_free(client);
    std::cout << "press [ENTER] to exit" << std::endl;
    getchar();
    exit(1);
  }
  right_joycon = hid_open(NINTENDO, JOYCON_R, right_joycon_info->serial_number);
  if(right_joycon != NULL) std::cout << " => successfully connected to right Joy-Con" << std::endl;
  else {
    std::cout << " => could not connect to right Joy-Con" << std::endl;
    hid_exit();
    vigem_free(client);
    std::cout << "press [ENTER] to exit" << std::endl;
    getchar();
    exit(1);
  }
  setup_joycon(right_joycon, 0x1, 0);

}

with this:

void initialize_left_joycon() {
  struct hid_device_info *left_joycon_info = hid_enumerate(NINTENDO, JOYCON_L);

  if (left_joycon_info == NULL) {
      std::cout << " => could not find left Joy-Con" << std::endl;
      hid_exit();
      vigem_free(client);
      std::cout << "press [ENTER] to exit" << std::endl;
      getchar();
      exit(1);
  }

  while (left_joycon_info->next != NULL) {
      left_joycon_info = left_joycon_info->next;
  }

  std::cout << " => found left Joy-Con" << std::endl;
  left_joycon = hid_open(NINTENDO, JOYCON_L, left_joycon_info->serial_number);
  if(left_joycon != NULL) std::cout << " => successfully connected to left Joy-Con" << std::endl;
  else {
    std::cout << " => could not connect to left Joy-Con" << std::endl;
    hid_exit();
    vigem_free(client);
    std::cout << "press [ENTER] to exit" << std::endl;
    getchar();
    exit(1);
  }

  setup_joycon(left_joycon, 0x1, 1);
}

void initialize_right_joycon() {
  struct hid_device_info *right_joycon_info = hid_enumerate(NINTENDO, JOYCON_R);

  if (right_joycon_info == NULL) {
      std::cout << " => could not find right Joy-Con" << std::endl;
      hid_exit();
      vigem_free(client);
      std::cout << "press [ENTER] to exit" << std::endl;
      getchar();
      exit(1);
  }

  while (right_joycon_info->next != NULL) {
      right_joycon_info = right_joycon_info->next;
  }

  std::cout << " => found right Joy-Con" << std::endl;
  right_joycon = hid_open(NINTENDO, JOYCON_R, right_joycon_info->serial_number);
  if(right_joycon != NULL) std::cout << " => successfully connected to right Joy-Con" << std::endl;
  else {
    std::cout << " => could not connect to right Joy-Con" << std::endl;
    hid_exit();
    vigem_free(client);
    std::cout << "press [ENTER] to exit" << std::endl;
    getchar();
    exit(1);
  }

  setup_joycon(right_joycon, 0x1, 0);
}

At the beginning of the function, our left_joycon_info variable is actually a linked list of all connected left JoyCons. The original code just takes the first one, so connecting another one and launching another XJoy will just reconnect it to the first pair. This replaced snippet makes it so that it pairs with the last pair of connected JoyCons instead - a rough fix (and I'm not going to make a pull request), but it works. Just connect your first pair, launch XJoy, then connect your second pair and launch XJoy again. Repeat for however many pairs of JoyCons you have.

albfan commented 3 years ago

Yes, hid_enumerate returns a list, so you can iterate on them.

I will do a proper PR for this, and try to relay on serial number so you can put it on config and pair same joycons at any time

reformatting as a patch:

--- Xjoy.cpp
+++ Xjoy.cpp
@@ -1,7 +1,7 @@
 void initialize_left_joycon() {
   struct hid_device_info *left_joycon_info = hid_enumerate(NINTENDO, JOYCON_L);
-  if(left_joycon_info != NULL) std::cout << " => found left Joy-Con" << std::endl;
-  else {
+
+  if(left_joycon_info == NULL) {
     std::cout << " => could not find left Joy-Con" << std::endl;
     hid_exit();
     vigem_free(client);
@@ -9,6 +9,12 @@
     getchar();
     exit(1);
   }
+
+  while (left_joycon_info->next != NULL) {
+      left_joycon_info = left_joycon_info->next;
+  }
+
+  std::cout << " => found left Joy-Con" << std::endl;
   left_joycon = hid_open(NINTENDO, JOYCON_L, left_joycon_info->serial_number);
   if(left_joycon != NULL) std::cout << " => successfully connected to left Joy-Con" << std::endl;
   else {
@@ -25,8 +31,8 @@

 void initialize_right_joycon() {
   struct hid_device_info *right_joycon_info = hid_enumerate(NINTENDO, JOYCON_R);
-  if(right_joycon_info != NULL) std::cout << " => found right Joy-Con" << std::endl;
-  else {
+
+  if(right_joycon_info == NULL) {
     std::cout << " => could not find right Joy-Con" << std::endl;
     hid_exit();
     vigem_free(client);
@@ -34,6 +40,12 @@
     getchar();
     exit(1);
   }
+
+  while (right_joycon_info->next != NULL) {
+      right_joycon_info = right_joycon_info->next;
+  }
+
+  std::cout << " => found right Joy-Con" << std::endl;
   right_joycon = hid_open(NINTENDO, JOYCON_R, right_joycon_info->serial_number);
   if(right_joycon != NULL) std::cout << " => successfully connected to right Joy-Con" << std::endl;
   else {
@@ -45,7 +57,4 @@
     exit(1);
   }
   setup_joycon(right_joycon, 0x1, 0);
 }
sam0x17 commented 3 years ago

Awesome guys will happily merge a PR please do be careful about testing that everything still works

albfan commented 3 years ago

@sam0x17 To me the trick of running two times didn't work. I'm checking if listening to existing joycons, you can detect two pressing L+R to pair them.

The idea is discard input from paired joycons, using serial number.

Not sure if hidapi serial number is unique and the same all the time, so I tried to read the serial number from joycon, but didn't work (not sure why, I'm using same code as JC toolkit, that works indeed https://github.com/CTCaer/jc_toolkit/blob/master/jctool/jctool.cpp#L149)

When that is ready, is turn XJoy as service an option? and add motion parameters?

sam0x17 commented 3 years ago

Gotcha, this is some good insight.

sam0x17 commented 3 years ago

I don't work directly on this project anymore but happy to accept pull requests adding those features @albfan

albfan commented 3 years ago

Ok, solved: The https://github.com/CTCaer/jc_toolkit/blob/master/jctool/jctool.h#L20 the alignment for data read is important

#pragma pack(push, 1)
...
#pragma pack(pop)

That allows to read serial if other serial is not unique.

Allow me some time to put all in order. Trying to pair with "L+R" right now