Pyrainput (FunKeyMonkey plugin + pyrainputctl) design


bzar

A Commando
Joined
Sep 22, 2008
Messages
4,500
Location
Finland
Website
Visit site
Background
Pyra uses a daemon process for turning raw input events from several input devices into a virtual keyboard, gamepad and a mouse. The daemon is an instance of FunKeyMonkey running the pyrainput plugin. The plugin is configured by /etc/pyrainput.cfg that can set stuff like what the nubs should do (mouse move, scroll, analog stick, click modes...) and such. There are also exportMouse, exportKeypad and exportGamepad flags for disabling sending specific types of events. Currently the main user-facing way of interacting with pyrainput is the pyrainputctl script, that modifies /etc/pyrainput.cfg and then signals the pyrainput daemon to reload the config using SIGUSR1.

Problem
As different use cases have emerged, a few issues have surfaced:
  1. Sometimes packages require specific input modes (like having the nubs in analog stick mode instead of a mouse). It's very unergonomic to require the user to change the mode beforehand using pyrainputctl and equally bad to have the package call pyrainputctl (requires sudo, hard to revert to previous state afterwards)
  2. There are cases where a key should do multiple things, like a nub act as both an analog stick and a mouse. Currently this is not configurable as each configuration key only takes a single value, so it would need to be hard coded and controlled with the exportX values, meaning no swapping nubs to your liking and such.
  3. If the inputs are in some wonky state (like all exportX values are false), it's kinda hard to get back to a safe state without resorting to SSHing into the device

RFC
I'm considering adding the following features to pyrainput (in some time frame, I only have a few hours a week to hobby code nowadays)
  1. Multivalue support to pyrainput configuration
  2. Configuration file loading with key combos
  3. Configuration injection through a /var/run/pyrainput pipe to pyrainput
The idea would be to have "profiles" in, say, /etc/pyrainput.cfg.d/ that would be configured to /etc/pyrainput.cfg like "profiles.1 = keyboard_and_mouse.cfg" and "profiles.current = 1". Then the user could change profiles with some key combo + number and have each set up the way they like them. A couple of stock profiles would be provided and assumed to be in specific positions, like default profile in 1 and gamepad profile in 2, so packages could send for example "profiles.current = 2" to /var/run/pyrainput before running and "profiles.current = 1" on exit. Perhaps only sending a configuration key could even read current values to make it easier to revert changes. If something went wrong, you could always get to a familiar layout by key comboing into a profile.

Multivalue support would incur a slight latency cost in handling the events, though I'm not sure if it would ever be noticable.

This all would require some thought into what the default profiles should be and such. As my progress on implementing any of this is slow (help appreciated! the codebase is relatively simple modernish C++), I thought it could be helpful to crowdsource some thoughts before inflicting this on you people. The earlier we get this stuff sorted, the less people need to change their workflows and packages later on to accommodate the new ways of doing stuff.
 
As discussed on discord, I think config files would be the best option then we can embed them properly in a DBP and also people can change options by dumping their own into the appdata directory.

Would also enable multiple configuration file options via the DBP too as you could script one to start on different scenarios
 
On discord I thought this would mean making profiles.current a path to profile, but actually the dbp can just pipe its configuration to /var/run/pyrainput, then just issue a reload of the current profile after it's run to revert.
 
Supposing there are system-wide definitions for (say) mouse mode and gamepad mode. Then suppose the user wants to rearrange the ABXY button map, so edits the system definition of gamepad mode.

Should a full-screen DBP game override that config when it starts, or adopt it? It seems anti-user to ignore global configuration, but some games will need specific mappings. What if there is a control-action mapping tool built-into the game?

What about games which can be either fullscreen or windowed? Trying to juggle dynamic keymapping on a per-window basis sounds like like trouble!
 
Well, the user can always override package specific mappings with appdata. I think package specific configuration should be used sparingly and in as limited a fashion as possible. Or stick to enabling a specific profile.
 
As far as rearranging ABXY, it seems confusing if different games map to them differently. Could the buttons have some sort of meaningful names which games could refer to them by?

On the PS Vita, those buttons have different mappings in Western and Japanese games. You can also remap them in the system settings. But that means to get the same layout when playing Western and Japanese games, you have to remap them in the system settings any time you want to play the other one. It's annoying ;)
 
What about having a special game profile slot:
Profile.1
Profile.2
...
Profile.auto

on start the Game Sets Auto to its own profile and the user can just run his key combo to select the auto slot.
 
Well, the user can always override package specific mappings with appdata. I think package specific configuration should be used sparingly and in as limited a fashion as possible. Or stick to enabling a specific profile.
I'll be honest and say going through each appdata dir and pasting a profile file there after noticing that my config got overridden sounds like a pain in the butt. Can still be a thing of course, but how about a single switch that gives me the power to say no package should override my input scheme? I'd be fully content with manually switching between mouse and gamepad profiles then and probably wouldn't need anything else.

Also, I don't know a damn about if the daemon even has access to this information or not, but the Mupen GUI case reminded me how much more useful it would be if the input settings were application-local and switched back when they're not the foreground window anymore. In the case of applications messing with the input stuff at least.
 
As far as rearranging ABXY, it seems confusing if different games map to them differently. Could the buttons have some sort of meaningful names which games could refer to them by?

On the PS Vita, those buttons have different mappings in Western and Japanese games. You can also remap them in the system settings. But that means to get the same layout when playing Western and Japanese games, you have to remap them in the system settings any time you want to play the other one. It's annoying ;)
Currently there's no configuration support for remapping ABXY, but I'm thinking people who want to do it could simply, for example, have two profiles that swap or unswap B and X. Those could be layered on top of any configuration the application uses. Meaningful names have been discussed before (see the flipBX thread), but it's not a simple issue.

What about having a special game profile slot:
Profile.1
Profile.2
...
Profile.auto

on start the Game Sets Auto to its own profile and the user can just run his key combo to select the auto slot.
The configuration file may allow you to set executable stuff that is run using the input daemon's privileges. I'd rather not give them full configuration file support, but rather allow them to change a limited set of things.

I'll be honest and say going through each appdata dir and pasting a profile file there after noticing that my config got overridden sounds like a pain in the butt. Can still be a thing of course, but how about a single switch that gives me the power to say no package should override my input scheme? I'd be fully content with manually switching between mouse and gamepad profiles then and probably wouldn't need anything else.

Also, I don't know a damn about if the daemon even has access to this information or not, but the Mupen GUI case reminded me how much more useful it would be if the input settings were application-local and switched back when they're not the foreground window anymore. In the case of applications messing with the input stuff at least.
You could always just load your profile with a hotkey if the application makes changes to it. Disallowing applications to make changes would just be not having the pipe file. Application-local configuration is possible, but currently out of scope.
 
Update: I've implemented support for multivalue settings and have been working on profile support.

I first made the hotkey Fn+Pyra+<number>, but that was both hard to use and not really nice code wise.

The current implementation I'm trying out includes adding a "command mode" that can be toggled with Fn+Pyra, that can be used (for example) for controlling pyrainput itself. The Fn+Pyra works as a toggle, so you can hop in and out of command mode. I'm thinking htis mode could be indicated somehow visibly, though I'm not sure what would be a good indicator.

Pressing a number key in command mode loads the profile of that slot and exits command mode.

Thoughts?
 
The current implementation I'm trying out includes adding a "command mode" that can be toggled with Fn+Pyra, that can be used (for example) for controlling pyrainput itself. The Fn+Pyra works as a toggle, so you can hop in and out of command mode. I'm thinking htis mode could be indicated somehow visibly, though I'm not sure what would be a good indicator.

Pressing a number key in command mode loads the profile of that slot and exits command mode.

Thoughts?
on pressing Fn+Pyra, trigger
Code:
notify-send "FunkyMonkey is now in command mode, press 1 for ... press 2 for ..."
 
Last edited:
Profile support is done and merged. Next up is the configuration pipe.

After these I'd like to do
  1. Configuration of all face buttons
    • Just a simple system with a couple of options for now that enable the current keyboard/gamepad/mouse functionalities, not full remapping
  2. Replace the currently hardcoded functionality of emitting events to multiple devices with multivalued profiles
    • Possible after 1
  3. Centralize the handling of export configuration values to UInputDevice and mark them as deprecated in the documentation
 
Probably less of a priority, but do you think you can make the speed of scroll events from the nub configurable? It sends them really fast atm
 
Sure, though it's a bit tricky to get right.

See, the events specify the scroll amount as an integer, with the smallest changes being +1 or -1. The current implementation uses a thread for mouse handling that sends mouse events every 16ms if there's been changes. This means the simplest, and current, method is to fire an event of the smallest change every tick the nub is over the scroll deadzone. If slower rates are needed the loop needs to track some fraction to send N events every M ticks evenly. It's basically PWM. Adding to the complexity, some applications seem to interpret the events as wheel "clicks" while others interpret them as wheel "distances", so in one context a change of 1 may be half a page where in other it may be one line. This has something to do with HiDPI mouses.

Maybe a simple frequency setting would do for most scenarios, like a setting of 2 would send an event every other tick? That would be just a couple of lines of code for the new configuration option and a few more for the data structures and handling.

I also considered a variable speed scrolling behavior where the nub position would affect the scroll event frequency, but that would probably just mess things up :D
 
Oh, I expected it to be analogue in some form, since the nubs are intrinsically analogue.
Yeah, the nubs are, but the uinput input event interface is not. It would be possible to fake it, but that's a bit more involved to get consistent. We could make an experimental mode for it, but for now I think it's more important to get the basics nailed down before too many packages depend on stuff.
 
Is uinput the right interface for it then? Presumably the pyra input tester uses some lower level interface to get it's nub position. I'm having trouble finding the source for that though, I've found that it's at /usr/share/pyra/bin/pyra_input_tester, but that's it so far.
 
Indeed. Pyrainput does read it as analog axes through evdev, but mouse wheels are not analog. So transforming the analog position into a series of mouse wheel scroll events for uinput is not that straightforward. Of course for example a joystick made from the nub events works as expected.
 
Back
Top