FunKeyMonkey


I did a simple keyboard remapping module yesterday with some example mappings (each key is passthrough by default) I'll add a simpler Fn-style mapping support for it, but currently Fn could be implemented by having a "complex" behavior on the Fn key that just sets a variable based on the key's state. Then each key with Fn mapping would have a "complex" behavior that checks whether the fn-variable is set and send an event based on it. I'll add a function like "behaviors->shiftable(KEY_K, &fn, KEY_K, KEY_COMMA) " later for easier configuration. This should make creating various keyboard->keyboard modules quite simple.
 
Last edited by a moderator:
Just too funky for me... lalalaa...

I'm going to ramble, ignore the message if you hate that. Read if you have time and hope to get neat ideas...

So, there's been some teeth gnashing and headbanging over here. That's not FunKeyMonkey's fault as such, but it acted as a "primus motor".

After examining the "altered input is merged with original"-situation further (makes no sense to me...) I quickly realized that my understanding of uinput's function was not originally that far off, please correct me when this is wrong:

Opening uinput for writing creates a new /dev/input/eventX node. The stuff dumped into uinput appears in that node.

Therefore it is certainly possible to NOT have the original input "merged" from /dev/input/event0 (for example), simply by only reading /dev/input/eventX.

Great, so I just point SDL to /dev/input/eventX... which is dynamically allocated... whoops... udev can luckily give the name, vendor and product - ample data for locating the right node. But a bit of coding to undertake... hilarity with this shall therefore ensue later... addition to my list-of-not-so-nice-things-todo...

First things first: Do something about X showing both funkeymonkey and original input. Having "k" as every second key kind of makes using the terminal a bit of a pain...

Not so great: By default X grabs everything that appears under /dev/input. Of course, suppose you plug in a USB mouse: naturally it should "just work". Well, uinput generates such things. And thus we again get the "merged with original" situation.

Well, no worries, thought I: I'll just explicitly configure xorg.conf to ignore the FunKeeMonkee inputs... you can ignore by product and vendor... which funkeymonkey set to 0, so I instead and evilly tried to set them to the only vendor/prod hex values I remember of the top of my head: 3DFX/Voodoo3 (don't, please just DON'T ask!-)... And not too surprisingly this ignorematchattempt fails... And there's little debuginfo to show why... and I download X11 server sources, check that it's udev where X11 gets the strings (WHY NOT MATCH THE BLASTED INTEGERS DIRECTLY!!-( I HATE it when things try to be oh so very user friendly :-( )... and so I resolve to figure this **** out later... addition to my not-so-nice-things-todo...

Well, still no worries, I never use external mice or keyboards on the Pandora, so I'll just ruddy well configure the blasted xorg.conf by hand to have the specific inputs (0-5) I want. And figure the InputClass wizardry later... Done, works...

...except now xkb went totally bonkers on me and no longer obeys me when I set keyrepeat off from a couple of keys (powerbutton switch and lid switch).

So, I bask in the glory of my cleverness until I flick the powerswitch... and the Pandora screeches to a halt... it was supposed to, actually, but not like this... it was also supposed to, like, wake when flicking the switch back on... did not, because keyrepeat, when every keypress and keyrelease event spawns a bash script, can do that to you :-(. A quick retest: ps shows tens and tens of the buggers when the switch is just briefly flicked. Before these changes there was ever only one...

At which point I go to google and buried within endless links I see that: No, not many people do this anymore - they want the automation and yes: XKB extensions has a bug in keypress config if you disable automatic adding of devices. Luckily it was supposed to be fixed in a new version... in 2009... but whatthehay...

And so, I'm now fixing my window manager (dwm), which I have set up to launch those bash scripts... At first I tried to just ignore repeats, but I think I'll just hook the relevant parts to /dev/input/events directly (no repeats there at least in my setup).

So, this adventure has already shown that this solution is not completely troublefree either: The key, I guess, is getting InputClass vendor/product matching to work - funkeymonkey can then be identified and either grabbed or ignored. It won't do that everything in X receives both original and altered input!? And grabbing/locking is a bit "crude" too, since... well, obviously that blocks access from others...

Another thing I had not though about when I've griped about SDL not being hooked up to X events is keyrepeat. Keyrepeat as I now see it in xev is pretty horrid: It does press-release-press-release cycles. I think it should do press-press-press-press-release instead? Having keyrepeat on for games in the directionpad is definitely not desirable?

This adventure will resume as my 4 kids (on holiday, attention demanding) allow it...

I also have a real funkeymonkey related task in my sights: the PC joystick nubcircle --> joysticksquare thing I already did in SDL some months ago might be an actually fun module excercise (as opposed to the endless list of notnicethingstodo...).
 
I am very happy that something is being done (instead of just claiming it should be easy as in the other threads). Thanks!
 
Last edited by a moderator:
Keyrepeat as I now see it in xev is pretty horrid: It does press-release-press-release cycles. I think it should do press-press-press-press-release instead? Having keyrepeat on for games in the directionpad is definitely not desirable?
There's a XkbSetDetectableAutoRepeat which turns keyrepeat to press-press-press-release. But it is a per client call, I don't know which apps use it. However with that SDL can trivially be made to ignore repeats, should that be needed.

I noted one final odd anomaly: When I configure inputs to xorg.conf individually, xkb apparently inits them when first input arrives? Or something odd like that... at least that is the impression I get from xmodmap-changes being reset after the first keypress. Reloading them after the first press fixes things. I "fixed" this by adding a "wait for keypress" to my .profile to a place I run x-specifics in any case...

Other than that things seem cool now: Inputs work again and funkeymonkey no longer injects on top of regular input and I can now access it's output from input6,7,8, whatever as needed.

Of course the real fix would still be InputClassing... the setup I run now is downright hacky :).
 
Maybe I'm missing something but isn't the -g option in FunKeyMonkey supposed to take care of preventing the double inputs?

-Neelix
 
Maybe I'm missing something but isn't the -g option in FunKeyMonkey supposed to take care of preventing the double inputs?

-Neelix
No, you're not missing anything. Grabbing will prevent anything but funkeymonkey from receiving events from an input device. Thus doubling stops occuring. And I also believe for most use cases that is enough.

I just want to take this a bit further - I want X to have the "normal" keyboard and something like SDL to see something completely different. Also, this allows me to monitor what happens without affecting the actual working environment in any way.
 
What you're trying to accomplish is a bit beyond current scope, but if you come up with a good option to add, please share :)
 
Funkeymonkey now supports aggregating multiple input devices. These are given with separate -i parameters, like so:


./funkeymonkey -i /dev/input/event0 -i /dev/input/event24 -p libkeyboard.so -g

The events are read from the sources in round-robin fashion, so all should be treated sufficiently fairly. There's currently no way of telling from what device events originate inside a plugin, but on the other hand it shouldn't matter in most cases anyway.

This should make it possible to create plugins for example for pandora that aggregate nubs, keyboard and gaming buttons to form some smart compound device in case anyone wants to do that.

Also added a simple fn-style functionality in the "keyboard" plugin template.

EDIT: To clarify, I use "select" to read from all devices synchronously, and pick the device to actually get events from from the set of ready devices using round robin. I don't just blindly expect all devices to produce events :p
 
Last edited by a moderator:
Added --list-devices (-l) --match-devices (-m) to ease device selection.

List-devices lists available evdev devices in format "<path>: <vendor>,<product>,<version>,<name>". For example on this desktop:


/dev/input/event0: 1,1,ab41,AT Translated Set 2 keyboard
/dev/input/event1: 46d,c051,110,Logitech USB-PS/2 Optical Mouse
/dev/input/event2: 0,1,0,Power Button
/dev/input/event3: 0,1,0,Power Button
/dev/input/event5: 1f,1,100,PC Speaker
/dev/input/event6: 0,0,0,Eee PC WMI hotkeys
/dev/input/event4: 0,0,0,HDA Intel PCH Front Mic
/dev/input/event7: 0,0,0,HDA Intel PCH Rear Mic
/dev/input/event8: 0,0,0,HDA Intel PCH Line
/dev/input/event9: 0,0,0,HDA Intel PCH Line Out Front
/dev/input/event10: 0,0,0,HDA Intel PCH Line Out Surround
/dev/input/event11: 0,0,0,HDA Intel PCH Line Out CLFE
/dev/input/event12: 0,0,0,HDA Intel PCH Line Out Side
/dev/input/event13: 0,0,0,HDA Intel PCH Front Headphone
/dev/input/event14: 0,0,0,HDA Intel PCH HDMI/DP,pcm=3
/dev/input/event15: 0,0,0,HDA Intel PCH HDMI/DP,pcm=7
/dev/input/event16: 46d,8d7,100,gspca_zc3xx
/dev/input/event17: 0,0,0,HDA ATI HDMI HDMI/DP,pcm=3
/dev/input/event18: 0,0,0,HDA ATI HDMI HDMI/DP,pcm=7
/dev/input/event19: 0,0,0,HDA ATI HDMI HDMI/DP,pcm=8
/dev/input/event20: 0,0,0,HDA ATI HDMI HDMI/DP,pcm=9
/dev/input/event21: 0,0,0,HDA ATI HDMI HDMI/DP,pcm=10
/dev/input/event22: 0,0,0,HDA ATI HDMI HDMI/DP,pcm=11
/dev/input/event23: 0,6,0,Video Bus

Match-devices takes a string parameter that contains a regular expression to match device strings to. A device string is "<vendor>,<product>,<version>,<name>", so devices can be matched based on the numeric ids or name. Multiple match-devices instances can be provided. For example


./funkeymonkey -m "[Kk]eyboard" -m "[Mm]ouse" -p libkeyboard.so

would match both event0 and event1 here. "^0,6,0" would only match event23.

Because that's pretty much all identifying information evdev provides, I'm not sure how this could be made more precise and/or flexible. Any ideas are welcome :D
 
just trying to figure out how this works, and make an example virtual keyboard or two. i currently want to wrangle some events from a USB gamepad to send them to nx-engine, which monitors only keyboard events. i could potentially also look into hacking the engine directly (i already did to make some physics "improvements"), and there's probably other ways to do it, but thought this might be educational.

i can get the USB gamepad hooked up to funkeymonkey:

blah:~/code/funkeymonkey/build$ funkeymonkey -l
...
/dev/input/event14: 81f,e401,110,USB gamepad


and then i can also run it with funkeymonkey, and get button-presses, but I get a ible-specified error: 

blah:~/code/funkeymonkey/build$ funkeymonkey -i /dev/input/event14 -p libtoymodule.so -g

Successfully added device '/dev/input/event14'.

Init!

ERROR: file removed from writing!  wrong sizes:  -1, 1116


Event! 4 4 589827 

Event! 1 290 1 

Event! 4 4 589827 

Event! 1 290 0 

Event! 4 4 589826 

Event! 1 289 1 

Event! 4 4 589826 

Event! 1 289 0 

Event! 4 4 589825 

Event! 1 288 1 

Event! 4 4 589825 

Event! 1 288 0 

Event! 4 4 589828 

Event! 1 291 1 

Event! 4 4 589828 

Event! 1 291 0 

Event! 4 4 589830 

Event! 1 293 1 

Event! 4 4 589830 

Event! 1 293 0 

Event! 4 4 589829 

Event! 1 292 1 

Event! 4 4 589829 

Event! 1 292 0 

Event! 4 4 589834 

Event! 1 297 1 

Event! 4 4 589834 

Event! 1 297 0 

Event! 4 4 589833 

Event! 1 296 1 

Event! 4 4 589833 

Event! 1 296 0 

Event! 3 0 255 

Event! 3 0 127 

Event! 3 1 0 

Event! 3 1 127 

Event! 3 1 255 

Event! 3 1 127 

Event! 3 0 0 

Event! 3 0 127 
 

The error is after I modify include/uinputdevice.h, when creating a new UinputDevice:


    uinput_user_dev device;

    memset(&device,0,sizeof(device));

    strncpy(device.name, name.data(), UINPUT_MAX_NAME_SIZE);

    device.id.bustype = bus;

    device.id.product = product;

    device.id.vendor = vendor;

    device.id.version = version;

    int x, y;

    if((x = write(_fd, &device, sizeof(device))) != (y =sizeof(device)))

    {

      close(_fd);

      _fd = 0;

      std::cerr << "ERROR: file removed from writing!  wrong sizes:  " << x << ", " << y << "\n";

    }

 


 

so I get an error writing to the _fd file descriptor, for some reason.  If this is a legitimate issue and legitimate use-case, I'll file an issue up at github.  note:  not running this on a pandy, but on a linux laptop.
 
Last edited by a moderator:
Does your user have the privileges to create uinput devices? Try running with sudo as a quick test. If it works, it's about permissions.

If it's not, I'll look into it more :)
 
Last edited by a moderator:
nope, that was it.  thanks very much!  i will investigate further and report back ;) .
 
sorry to take a while in getting back.  i started off with a luxmo (cheapo) controller, which wasn't the funnest to play with [not very responsive].  got an 8bitdo SNES controller, and that is very nice to play with.  i was worried about responsiveness with funkeymonkey but as far as i can tell throughput is instantaneous ;).  

what's neat with funkeymonkey is you can make buttons do context-sensitive things.  i'm playing cave story, and i've mapped start and select to bring up the menu (Esc), but then in the menu hitting start resumes play (F1), while hitting select restarts the game (F2).  (that's because, in cavestory, Esc+Esc quits the game, and that's not what i want to do with the controller in hand.)

if anyone is interested in a simple cavestory mapping for the 8bitdo SNES controller (or if Bzar you'd like it as an example on github, i can do a PR), i'm happy to share it.
 
Please do! The more examples there are the easier it is for others to make their own.

I'm mostly waiting for use cases myself. Though I could make the core better by substituting select-polling with epoll.
 
Thanks for your work pal, I'm the kind of guy who loves to map action on a shortcut key!
 
random bug/feature:  if you keep the funkeymonkey instance going and unplug the usb device, the CPU usage of funkeymonkey goes through the roof.
 
Whoops. Gonna fix that ASAP. The correct thing to do would probably be exiting when the device file is no longer readable.
 
For some reason that reminds me of this:
https://xkcd.com/1172/
workflow.png


:)

-Neelix
 
Back
Top