Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for mapping multiple controllers to a single input device #12534

Merged
merged 1 commit into from
Jun 21, 2021

Conversation

jdgleaver
Copy link
Contributor

Description

This PR adds support for the remapping of individual controllers to arbitrary core input devices. This allows multiple controllers to be assigned to one input. To configure this, a new Mapped Port option has been added to the Quick Menu > Controls > Port N Controls menu:

Screenshot_2021-06-17_17-51-55

Here, the Port 2 Controls refers to the physical controller; Mapped Port refers to the core port that will receive input from this controller.

For example, to map physical controllers 1 and 2 to the player 1 input of a core, set the following:

  • Port 1 Controls > Mapped Port > 1
  • Port 2 Controls > Mapped Port > 1

There are various use cases for this remapping:

  • It allows multiple controllers to be used when playing hotseat multiplayer games that normally support only a single controller
  • It allows hybrid-type controllers (e.g. two joysticks mounted in a cabinet) to be used as a single device
  • It allows easy swapping of player 1/player 2 (etc.) inputs - for example, when the PSX version of Metal Gear Solid asks the player to plug their controller into port 2

Note that these configuration settings are independent from the main RetroArch config file. To make any changes persistent, a Core, Content Directory or Game remap file must be saved.

This PR also cleans up a long standing issue when configuring remaps (#10622): at present, changes to Device Type and Analog to Digital Type via the Quick Menu while a core is running will 'bleed through' to the main config file. This is harmful and unintuitive behaviour. With this PR, the global settings for these values are cached when initialising a core, and restored when the core is unloaded. (This essentially became a necessity for using the port remapping functionality, since it is rather easy to create complex configs that should in no way touch the main config file...)

Related Issues

Addresses half of #7830
Closes #10622

@bslenul
Copy link
Contributor

bslenul commented Jun 17, 2021

Oh that's f***ing awesome! Thank you so much for this! And for the "device type" and "analog to digital type" fix too! ❤️

@Ryunam
Copy link
Contributor

Ryunam commented Jun 17, 2021

This is, simply put, yet another huge improvement that I was looking forward to seeing in RA! Amazing work as always.

@Tatsuya79
Copy link
Contributor

Perhaps this could be made more explicit: "retroarch side/core side" with sub-labels or something like that.

@jdgleaver
Copy link
Contributor Author

Thanks guys!

@Tatsuya79 That's a good point. These input menu entries are non-standard, and tricky to deal with, but let me see about adding a sublabel....

@jdgleaver jdgleaver force-pushed the input-port-remapping branch from 0e95e5c to 4467fbe Compare June 18, 2021 10:33
@jdgleaver
Copy link
Contributor Author

@Tatsuya79 There we go - I have added a sublabel:

Screenshot_2021-06-18_11-11-45

@jdgleaver jdgleaver force-pushed the input-port-remapping branch from 0d11daa to ad4e491 Compare June 19, 2021 11:03
@bslenul
Copy link
Contributor

bslenul commented Jun 20, 2021

Not sure if intended with this PR or not: Settings > Input > Port X Controls > Device Type without a core loaded isn't saved anymore on exit, so if it's set to "None" or "RetroPad with Analog" (not sure why anyone would use these anyway?), it will be set to "RetroPad" again on the next RA boot.

edit: OK probably a bug, because if you have input_libretro_device_p1 = "0" ("None") in the .cfg you'll be stuck with "None", which will be an issue if people update RA with this value in their .cfg :p

@jdgleaver
Copy link
Contributor Author

@bslenul Thanks for reporting this! It is indeed a bug.

But oh boy - what a nightmare it will be to fix it... (turns out that RA does some very stupid things at a fundamental level; I can't really change how it functions, so will need a creative workaround...)

Will push a fix as soon as I can find one :)

(BTW - I agree that setting Device Type globally is odd and undesirable behaviour. It should indeed never be set to anything other than RetroPad. But I think removing this setting is beyond the scope of this particular PR...)

@jdgleaver jdgleaver force-pushed the input-port-remapping branch from ad4e491 to 973f5cd Compare June 21, 2021 11:43
@jdgleaver
Copy link
Contributor Author

@bslenul I just updated the PR with a fix: now it should only restore cached settings if content has actually been launched, and the cache will be cleared on core deinit to prevent spurious overwrites when quitting RA (i.e. changes made while a core is not running should be preserved)

@inactive123 inactive123 merged commit 0a75b51 into libretro:master Jun 21, 2021
@bslenul
Copy link
Contributor

bslenul commented Jun 21, 2021

Nice, thank you! :)

@RobinTD
Copy link

RobinTD commented Jul 26, 2021

@jdgleaver Great feature! I would really have loved to be able to change this setting in the global Input menu (I have a device with multiple devices that I always need to use together). Any particular reason that it is only provided as a remap?

@jdgleaver
Copy link
Contributor Author

@RobinTD The input code is very bad, and difficult to work with. I implemented the feature like this because it was the easiest/sanest method. I will try to think about a global setting, but I have some other work I need to do first.

@RobinTD
Copy link

RobinTD commented Jul 27, 2021

For context, if I push a button on one device now right after RetroArch is started, the others will of course not be available. Which is unfortunate if they are the ones who provide the directional keys etc. So then I have to just quit and restart.

@jdgleaver
Copy link
Contributor Author

Oof...! Yes, I see the problem. That's actually a completely different ballgame though - the current implementation is just for core inputs, but for RetroArch itself it's an entirely different codepath. Very, very tricky...

@bhamiltoncx
Copy link
Contributor

This is phenomenal! Bravo and well done.

By the way, can I help contribute making this work in global settings? (I'm a software developer, but haven't contributed to libretro yet. Might be a great way to get started.)

My use-case is a home arcade system with two USB arcade joysticks (port 1 and port 2), plus two Bluetooth game pads (port 3 mapped to port 1 and port 4 mapped to port 2).

I want every core to behave the same, but I currently have to set up the port mapping individually for every core.

@jdgleaver
Copy link
Contributor Author

@bhamiltoncx Not to discourage you, but I'd probably recommend attempting almost anything other than this as a first foray into libretro development! There is a great deal of ugly (horrible) legacy code here, and some tricky menu work involved. Starting with this area of the codebase is likely to turn you off libretro development for good...

One of the more significant issues here is on the UX side. These port maps are set relative to the core - but for global settings via the regular Input menu, everything would have to be done in reverse (which is more involved than you might think). Alternatively, we'd a new global 'core port mapping' menu - but then that's awkward because we don't know how many core ports should be set until after a particular core is loaded.

If you're feeling brave, however, then by all means have a dig through the code :)

@bhamiltoncx
Copy link
Contributor

OK, OK. :) It wasn't too hard to copy the behavior for each core I care about, it just took a while.

I'll still try to find another way to contribute — I've had to struggle a lot to get AutoHotKey to provide a consistent way to exit Retroarch by holding a button (the existing Retroarch feature to open the menu by holding a button is too complex for my kiddos, and my home arcade doesn't have a dedicated "exit" button I can map otherwise). I might contribute that to libretro instead..

@jdgleaver
Copy link
Contributor Author

Ah, that sounds like a fine idea :)

Starting with something smallish is always good - the codebase can be quite intimidating at first ;)

@rtomasa
Copy link

rtomasa commented Jan 16, 2022

Unfortunately this feature do not cover different device type scenarios.
For example, I'm trying to configure Lucky & Wild on FBNEO with a gamepad and a lightgun plugged in. Then I perform the following configuration from Settings > Input:

Port 1 > Device Type = Classic
Port 2 > Device Type = Lightgun

Then I go to Quick Menu > Controls:
Port 2 > Mapped Port = 1
This results in Device Type becoming Classic as in Port 1 so the ligthgun does not work and if I change to Ligthgun then it works but not the gamepad.

@barryl93
Copy link

I'm trying to set up the Atari 2600 Raiders of the Lost Ark game with just one controller. Back in the day, you had to play Raiders with both joysticks -- one controlled Indy while the other allowed you to choose from your inventory.

But I have an Xbox controller with two sticks, a D-pad, etc. I thought that using Mapped Port I'd be able to map the inventory selection to, say, the shoulder buttons... Then my left analog stick would control Indy, B would fire/use, L and R would cycle through the inventory, and X would drop.

But I can't seem to get that to work. I'm sure I'm missing something incredibly basic! Right now, I've set Port 2 (which has a standard gamepad plugged in) to Mapped Port -> 1. I then set the shoulder buttons on Port 2 to "left" and "right." But I'm not sure what I need to do under Port 1 in order to get the Xbox's shoulder buttons to "see" the mapping.

Is what I'm attempting possible, or am I tilting at windmills?

@jdgleaver
Copy link
Contributor Author

@barryl93 The Mapped Port feature enables multiple physical gamepads to control a single core input. It does not allow one physical gamepad to control multiple core inputs.

What you need to do is go to Settings > Input, and for both Port 1 Controls and Port 2 Controls set the Device Index to the same physical gamepad. Now that device will control both player 1 and player 2 in the core. You'll then need to get creative with the button mapping (i.e. in the Settings > Input > Port 2 Controls menu, unbind everything you don't need and only bind the analog inputs to whatever buttons you want to use). Remember to save all this as a config override, so you don't mess up your global settings!

@barryl93
Copy link

@barryl93 The Mapped Port feature enables multiple physical gamepads to control a single core input. It does not allow one physical gamepad to control multiple core inputs.

What you need to do is go to Settings > Input, and for both Port 1 Controls and Port 2 Controls set the Device Index to the same physical gamepad. Now that device will control both player 1 and player 2 in the core. You'll then need to get creative with the button mapping (i.e. in the Settings > Input > Port 2 Controls menu, unbind everything you don't need and only bind the analog inputs to whatever buttons you want to use). Remember to save all this as a config override, so you don't mess up your global settings!

Ah, I see now! That seems to work! (The D-pad on Player 1 now acts like it's getting input from the D-pad on Player 2. It also, weirdly, still acts as the D-pad on P1 -- it moves the inventory selection AND moves Indy -- but that's not a big deal.) Thank you so much for you guiding this noob!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"Analog to Digital Type" and "Device Type" changes being written in global config if you make a remap file.
9 participants