jgeumlek / MoltenGamepad

Flexible Linux input device translator, geared for gamepads
MIT License
274 stars 42 forks source link

Merging two devices into one #52

Open TiZ-HugLife opened 6 years ago

TiZ-HugLife commented 6 years ago

Please consider this low priority. Like, so low that I kept thinking about fixing this but never got around to asking you how.

Recent kernels keep switching things up with the dualshocks. DS4s now show up as three separate devices! One for the accelerometers, one for the touchpad (which interacts with X by default; fun!), and one for the regular gamepad inputs. I'd like to be able to merge together the touchpad and regular inputs so that I can get back touchpad-as-select functionality. Is that currently possible? I know we have splitting; what about merging?

It's really no big deal if that's not feasible at the moment.

jgeumlek commented 6 years ago

First of all, sorry for development delays. Life keeps popping up.

In a roundabout way, this whole project came to be because I wanted to merge devices. Namely, the wiimote and its extensions. So this issue strikes a chord with me. But I do agree it appears not too painful -- and having a touchpad that works with X is at least as cool as one that emits the select button.

So one way forward is to finally get around to making a DS4 driver -- they now have enough peculiarities and enough wide spread use to be worth supporting directly. A special DS4 driver could easily copy the model I have for the wiimotes, gathering and claiming all the relevant devices. Such work would also be essentially required if MG is to ever do fancy stuff with that RGB light bar.

However the DS4 also acts as a nice poster child for the generic driver functionality of MG. Is it feasible to improve the generic driver to handle this? The backend would a little work, but actually wouldn't be too bad. The two major problems are (1) syntax in the gendevices spec, especially if two merged devices are emitting the same event codes (2) a nice way to correctly merge when multiple instances are connected at once.

(2) could possibly be done via enforcing merged devices have a shared HID parent -- but might also be limiting? For example, if we imagine a world where generic joycon-esque things are common. (Or an arcade stick with multiple encoders)


As long as I have your attention, you seem to have been following Steam and Linux gaming more closely than I have as of late. Answer any of these as you are best able.

Would you say the DS4 kernel changes are mostly final now, and shipping in major distros? How many DS4 targets do we have? (1) The old awkward hid-sony mapping (2) a new xpad-esque mapping (3) xpad-esque w/ separated touchpad/accels? Or did (2) and (3) happen at the same time?

Is Steam still not playing nicely with MG, or is there at least a way to tell Steam to not do fancy stuff with the DS4s? I think there might be a quick fix adding a new gendev flag like grab_hid_parent to get MG to remove permissions from the raw HID node (might need new udev rules though).

TiZ-HugLife commented 6 years ago

Don't sweat it, dude. Life doesn't stop keep happening. MG is still working great at the end of the day, that's why you haven't heard a peep from me. :+1:

Real quick I'm going to answer the questions you put below that HR, starting with the second. Steam Input is not playing nicely with MG. It was a mystery how it could possibly read from event devices that I knew it didn't have permission to read. But it wasn't reading from those devices. It is reading straight from hidraw devices, as you surmised (and writing to them too, actually, for the light bar), and linux packages ship with a udev rule to enable that by making certain hidraw devices 0666: /lib/udev/rules.d/99-steam-perms.rules. The simplest way to make steam input go away is to just create an empty file at /etc/udev/rules.d/99-steam-perms.rules; it overrides the one in /lib and then hidraws aren't 0666 anymore. It may be better to give the MG user ownership of them and make them 0600. Either way, Steam Input is in direct opposition to our goal since we accomplish the same thing we do but system-wide instead of only inside Steam.

Regarding the DS4 kernel changes, there's no way to know. I'm not following the mailing list, I'm just adapting to whatever happens when I use a new kernel series. I do not believe (2) and (3) happened at the same time. In addition, I haven't paired a sixaxis with my laptop in a while; DS3s might expose accelerometer event devices too! (I'll have to check later.) But it is kind of a pain to have to worry about three different ways that a sony device could present itself event-wise. That might be why Valve opted to read hidraw; what it reads and writes from there isn't going to change.

And that might be a good case for writing a sony driver. Gendevices can't read hidraw (right?) but a specialized driver could, and then we could just say nads to whatever sony event devices do or don't exist because we wouldn't need them. I personally thought the best use for the gendevices would be to properly account for third party controllers and first party controllers that happen to be simple enough. Gendevices already have a poster child in the fact that my laptop has not yet met an arcade stick it couldn't handle.

I'm not opposed to if you wanted to create spec for merging multiple event devices for one gendevice, however I am almost certain that the sony devices are the only modern controllers this complicated that you haven't written a specific driver for yet. (Wiimotes and Joycons being the other two.) The Playstation Move controller might be the only exception but I imagine that games (or rather game singular: J.S. Joust) program specifically for it anyways, and we don't want to interfere with that.

jgeumlek commented 6 years ago

Thanks for that info. That sucks that Steam Input can't be told to back off, but at least there is a way. The workaround should get added to the troubleshooting FAQ or the wiki or something, at least until I can get around to testing having MG grab the hidraw nodes too. (Aside: my recent sessions in Steam have been with the Wii U Pro -- thankfully Steam Input still ignores it!)

I think a DS4 driver would be the correct path forward. Merging generic devices would be cool, but also a lot of complexity. You are right that this feature creeping away from what I originally wrote the generic driver for, namely simple devices that just need a little massaging due to awkward mappings.

Gendev can't read hidraw, and truly making a generic hidraw driver would be a large task! The raw HID spec is nontrivial. A more feasible task is just encouraging code reuse across hidraw-based drivers in MG, so that future hidraw drivers are relatively easy to add.

Some more research might be needed before deciding whether a MG DS4 driver should target hidraw or the kernel hid-sony event nodes. Going hidraw would get around annoying kernel changes, but might open us up to annoying DS4 hardware/firmware changes. I think I recall a period where the kernel was working with older DS4s but not newer ones. If feasible, it might be nice to not duplicate the effort of the kernel devs in catching and fixing things like that. I guess the Steam devs chose the hidraw method, but that might also be to reuse code across platforms. MG is likely going to remain Linux specific.


As much of a pain these kernel changes have been, I am very glad they are happening. Hopefully game pad annoyances will fade as future hardware comes out and their drivers are written well the first time! I think hid-wiimote still emits keyboard arrow keys for the dpad, meaning some games ignore those inputs... what could have been if the classic controller just worked out of the box!

TiZ-HugLife commented 6 years ago

I think I recall a period where the kernel was working with older DS4s but not newer ones.

That was mainly on account of the second DS4 having a different product ID. It didn't get picked up by hid-sony because of that and instead just functioned as a generic HID gamepad instead. All that really meant at that time was no lightbar; all the mappings were the same (this was before they went to xpad mappings). I'm confident the HID stuff won't change because I'm fairly certain it doesn't communicate with computers any differently than it does PS4s, and it'd be a huge pain for PS4 owners if they were to try and change things in that way.

Besides, if we rely on the evdev devices, wouldn't we then have to rely on the kernel keeping up with hypothetical HID changes instead of just us? New kernels take time to roll out to stable distros, too, which would be even more of a pain point.

jgeumlek commented 6 years ago

Oh, was that an issue? I though I remember seeing something deeper happen once, like the report size changed or something, and the old code just aborted. Ah yes, found the commit. I think since MG just skips to making virtual evdev devices, we can skip all this report descriptor patching junk. A uhid instead of uinput solution might need to deal with all this though.

You are right though, any hid changes are likely minor since it would cause console headaches as well. Fixing MG to handle new vendor/product ids would be relatively easy. Although as a special driver, these IDs will likely be hardcoded instead of easy text files... might be a feature worth trying to keep, since new USB product IDs will likely always be an issue. At least recompiling MG is a lot less scary for people than recompiling kernel modules.

As for the old kernel issue, at least motivated users can generally do dkms to backport the newer drivers. But yeah, I agree with you. MG is trying to be a userspace "just works" driver, so MG should be the one handling these issues. Just relying on the kernel updates is the lazy way out, not really the right one.

TiZ-HugLife commented 6 years ago

Oh yeah, you're right. The HID report size did change, my B. Yeah, I think a DS4 driver is definitely the best way forward.

They did this xpad mapping goofiness with all the sony controllers though, including the DS3. I'm pretty sure it also exposes its accelerometers separately (forgot to double-check, oops) as well. So should this be more of a "sony" driver and less of a "ds4" driver, do you think?

jgeumlek commented 6 years ago

If not much work, it would be good to make it a DS3+DS4 driver. The set of events is similar enough, just that DS4 has more bells and whistles. Going through the kernel, it should be simple. Going through raw HID, well, I don't know how different they are. I expect they both act as mostly normal HID devices, just a matter of looking at the reports.

The biggest hurdle would simply be that I don't have any DS3s. So I could likely test with DS4s and code up what should work for DS3s, but I couldn't verify it. DS3s might be best left for gendev, and just ignore the accels entirely. Accels are currently at best a novelty in MG.

Another factor is whether we expect any one to ever want to have different mappings for DS3s vs. DS4s. For example, sacrificing a button to make up for a lack of touchpad events.

Now that I think about it, that use case would probably be met if we just add an match_ds3 option to the driver. When true, the driver grabs both, and uses the same mapping. When false, the driver only grabs DS4s, and DS3s can fallback to a gendev driver if it exists. The latter can have a different mapping. This would also allow it be set false by default, since I can't verify it works.


In other news, I got around to cleaning up some partial changes on the devel branch I've been sitting on for months, which permits the virtual devices to disappear when they lose all their input sources. Still experimental, but seems to be working now. Redirect translators or assign slot ... just start doing nothing when their target devices get closed, but oh well.

As part of the testing, I see that these extra accel devices actually get picked up by the gendev driver, since their axes are a subset of the normal gamepad axes. And so if you move the controller quickly, it registers as notable event and gets allocated one of the virtpads...

As a quick fix since a DS4 driver isn't likely to happen very soon, I've added an extra matching constraint, a subset of exposed events with a minimum requirement. E.g. to match sony-standard, a device should expose a subset of the events, and also expose at least, let's say, 8 of them. This rules out the accel device (only 6 events) but should still match any vaguely DS-y pads. The current name of the option is pretty bad, and I'll likely change it before it hits master. I want something succinct yet clear... min_events_in_common is a little wordy, but might be the best.

In the near future I hope to investigate a clean way to handle the permission grabbing now that we have two things (DS4 via gendev, joycon driver) that also care about hidraw nodes. When grabbing an evdev node, we might want to also grab the hid node. When grabbing an hid node, we might also want to grab any and all evdev/js nodes. I'm thinking I might combine both of these behaviors just under some sort of aggressive modifier.

TiZ-HugLife commented 6 years ago

Since I have a DS3 sitting around at home, what can I do to help you verify whether or not it'll work the same if we use HID? Is there a way I can dump HID reports for each input to a file? Since it still outputs the accelerometers that might be a pain though. hid-sony.c in the kernel might be a good reference to see what differs and how.

One stupid hack I thought of was blacklisting hid-sony to force DS3s and DS4s through hid-generic, which I think would prevent multiple devices from being created at all. I think the gamepad inputs, touchpad, and accelerometers would all get forced through one event device. I do wonder if our gendev ruleset is prepared for that. I'd have to test it. If it works, we would more or less only lose the lightbar, which is really not that important since hardly any games use it, and I imagine could be configured manually by figuring out what to send through the HID device and coming up with a script or program to do that. Steam Input can configure the lightbar this way. I wonder if I could just rip out every part of ds4drv except LED configuration, lol.

... which permits the virtual devices to disappear when they lose all their input sources.

Oh no! I'm actually not about this at all. I hope you just mean they become inert and that the virtual /dev/input/event devices stick around. One of the big upsides about the system-wide MG setup is that you have a number of controllers in a contiguous arrangement at all times. Some Unity games just flip out when you have gaps in event devices. It matters because a controller coming in could steal the place of one of the virtual devices before the virtual device appears.

I imagine that behavior is good for user mode, which I can understand you still want to support, and I imagine all the stuff you're talking about after that--grabbing devices and such--is relevant mainly for user mode. System mode usually just involves udev rules that go "move, this is mine. shhhhhhhhhhhh." I honestly can't speak on user mode at all. I find system mode so much more stable, reliable, and from a configuration perspective, maintainable. With user read/write permission on the socket for mgctl, I can switch profiles and such as a user. I feel like user mode is a constant race to beat the system and other programs at doing what they innately want to do and hoping it continues to still work, and that we're only winning right now by chance. :(

In any case, I'm glad to hear there is work on silencing a virtual device when its backing device disappears. WRT to the matching constraints, I think that's a good idea. min_common_events takes a little bit of verbosity out. I wonder if it'd be better to indicate mandatory inputs. Like, for example, Mayflash PS2 adapters have those janky 290 inputs. The old version of the DS3 mapping uses 704 for home. Stuff like that.

jgeumlek commented 6 years ago

I don't know of a convenient way to dump HID responses.

It might be worth exploring going through hid-generic, although I think these controllers abuse the HID spec, so I'm not sure how well it will work. (As evidenced by all those quirky hid report descriptor patches in hid-sony)


I hope you just mean they become inert

No, I mean destroying the virtual input nodes. This was a feature for issue #31, which is also a necessary step if MG ever wants to allow the user to change what kind of virtual devices they have (e.g. switch from --mimic-xpad to a currently nonexistent --mimic-ds4 option during runtime).

I agree that having constant persistent devices is great, and that many Linux games still don't handle hotplugging well. That's why the default behavior doesn't allow the virt devs to be destroyed, and the option to never destroy will be there.

I also agree that single user mode is hacky and unreliable. It pretty much is a race condition for hotplugging, assuming a game that supports hotplugging is currently running. I would recommend only connecting/disconnecting controllers while games aren't running in this mode.

But just as some games don't handle hotplugging well, I also have some games that don't handle additional silent controllers well. They just wait forever for players 3 and 4 to ready up. Another milder issue can show up when someone wants to play a 3 player game with two controllers + a keyboard player (since they don't own a third pad). Some games will see 4 controllers, and just give them all 4 player spots, not allowing a keyboard player.

The feature has you specify a minimum number of virtpads to keep around. Set it to 0, and they come and go as they please. However, I expect most users to keep it at 4, and sometimes reduce it to 2 (or vice versa). I don't think the 0 setting will be too popular.

The dream here, is for that far off day, where most Linux games actually consider that people might connect controllers, or possibly temporarily lose a connection/have a battery run. A day when they can do the right thing. In this dream, system mode + allowing virt nodes to go away has the smoothest magic. The virt devs connect/disconnect just like the real thing, and the games never get even a glimpse of the original device nodes.


Aside: I maintain the user mode mostly since it makes testing a fair bit easier. Test with root grabbing the permissions, figure out the necessary udev rules later. I would suggest anyone long-term using MG to migrate to the system wide install. And this ability to have virt devs come and go shouldn't be recommended for user mode, as it leads to races.

Perhaps I'm wrong, but until MG's user base grows, I feel like it is easier to get people to try it out if they don't need to add a new user account. I guess now that we have had install scripts for a while, it isn't too scary a process.


Some Unity games just flip out when you have gaps in event devices.

Please tell me this doesn't mean what I think you mean. Are you saying you know of a game that flips out if the user has event6 and event8 as controllers, but event7 is either missing or some other device?


About matching, specifying minimum common events was the low hanging fruit that best fit with the current events-based matching system.

A more general mandatory exposed event / forbidden exposed event might be better, but also more complicated to code and more complicated to write the configs for.

If you have a use case for more fine-grained event matching that would make MG nicer/easier, I'm interested in hearing it.

TiZ-HugLife commented 6 years ago

Well, I tried out my DS4 and my DS3 using hid-generic. It works fine. But in order to do it, I can't just blacklist sony-hid. If I do that, it just doesn't get bound to a driver when I connect it. I'm sure I could make it bind by talking to sysfs but for the purpose of the experiment, the hid module option ignore_special_drivers was good enough. Our existing DS4 rules work totally fine. I get my touchpad-as-select back this way, which I value more than a convenient sysfs LED interface.

For the DS3, all I had to do was duplicate the driver matching line in playstation3.cfg and change sony-hid to hid-generic. That ruleset worked fine too. So it could be more convenient than creating a driver using hidraw to find the cleanest way to force sony devices off of sony-hid and onto hid-generic. The call is ultimately yours but this is probably what I'll end up doing for my own system until you decide what it should be.


Please tell me this doesn't mean what I think you mean.

It sure does. In Assault Android Cactus and Ultimate Chicken Horse at the very least, last time I checked, if you plug in three controllers and yank player two, even before you start the game, player one will be controlling both the first and second slot at the same time. Devices coming and going for any other reason, leaving gaps in event device numbers, cause this to occur. Before MG was a thing, my dumb bash script solution wrapped around xboxdrv, rejoy, would get around this by moving event devices around to fill any and all gaps.

That's why I like having a contiguous block of four controllers. This is likely a fundamental flaw in Unity that programmers probably can't get around. Meanwhile, games that are sitting around waiting for players to ready up just because controllers are plugged in are misbehaving. Controller slots should always be opt-in, not opt-out. Issue reports should be filed with the game to make it behave.

Not that I don't think there is value in dynamically creating and destroying event devices; I do. But using that to fix games that are just programmed inconsiderately or lazily will break others that likely have no way to fix that particular issue. :(


Regarding your question about matching, my best use-case is just fool-proofing the gendev matching so that it doesn't pick up stuff like motion devices which don't expose enough buttons to possibly actually be a gamepad, and to take advantage of mapping quirks on devices like the DS3. Our stuff already works short of motion devices stealing slots.

jgeumlek commented 6 years ago

That hid-generic hack is definitely too hacky for my tastes, but I am glad a solution exists for now. I think I'm happier saying that MG doesn't support the touchpad on modern kernels for now than suggesting users go through that.


player one will be controlling both the first and second slot at the same time

: (

Issue reports should be filed with the game to make it behave.

While true, I have higher hopes of Unity fixing their fundamental issue than I do for the various game devs to switch to an opt-in model.

I am still in awe that having a gap in the devices is a problem. Like, that isn't even that obscure a situation, even without doing anything with virtual controllers.

That's why I like having a contiguous block of four controllers.

I like that too. Consistent player numbering is pretty much impossible otherwise -- some games are going to use the event node filenames, some games are going to use udev and (I think?) get them in the order they were connected. If only games could write out player numbers to devices similar to how they write out rumble events...

Alas, there isn't a silver bullet. MG users will need to be aware of the failure states of the various games, and the best we can do is try to let the appropriate decisions be made without having to restart MG.


Did your bash script to fill in the gaps work? I guess Unity isn't using udev to enumerate the devices, so silly filename shenanigans should work. Ugh, at least some sort of simple "event device compactor" script should be feasible with or without MG running. Might be time to start a little "MG Utils" collection.

MatthiasGrandl commented 3 years ago

@HugLifeTiZ can you tell me what your current solution for multiple dualshock 4 is? can't get it to work properly.

TiZ-HugLife commented 3 years ago

First, I'm realizing I never answered @jgeumlek's question. Yes, that old bash script did indeed work.

@MatthiasGrandl, my current solution is just a udev rule that deletes the event devices for the touchpad and the gyro.

Aura ~ $ cat /etc/udev/rules.d/40-rm-sony-touchpad-and-motion.rules
SUBSYSTEM=="input", ATTRS{name}=="Wireless Controller Touchpad", ENV{ID_INPUT_JOYSTICK}="", RUN+="/bin/rm %E{DEVNAME}"
SUBSYSTEM=="input", ATTRS{name}=="Wireless Controller Motion Sensors", ENV{ID_INPUT_JOYSTICK}="", RUN+="/bin/rm %E{DEVNAME}"
MatthiasGrandl commented 3 years ago

@HugLifeTiZ I am doing the same thing, but still can't get more than 2 gamepads to work reliably. So for example evtest shows all for gamepads working, but in Cemu when using Xinput only slot 1 and 3 are working. Same with Duck Game and other Steam games. In Cemu using DirectInput, all 4 work. I am not sure what the problem is, but I would absolutely love to have MoltenGamepad take care of everything.

Also interesting to note is that even though I remove the Motion Sensor Input Device with the udev rule, evtest is still showing motion data on the regular gamepad input device. Not sure if that might be part of the reason for my problems....