houmain/keymapper

device specific settings

kbilsted opened this issue · 21 comments

Is it possible to match devices on something other than name - like mac or other unique

image

It seems unlikely given the docs say nothing about it. Give something like this a shot? https://www.eightforums.com/threads/tutorial-how-to-change-device-names-in-device-manager.15321/

Aside: do long press timing based remaps work for on your systems on v4.0? I had to revert back to a commit a bit later than v3.5.3 that introduced ContextActive. In v4.0.0 all keymaps using the form X{200ms} stopped working.

@ristomatti Many thanks. Did not work but this may.. https://www.thewindowsclub.com/how-to-rename-hardware-in-device-manager-of-windows It renames devices but i have yet to see it survives a reboot etc.

I have taken a detour from combo configurations ...
But I did notice, and reported here as an issue, that the timings were behaving really unexpected and change from many combos, and then taking a 2 sec pause and then invoking combo's again...

i have also tried changing the description field that held the name along with some other data. So i have given up on it for now.. really a shame because i had cool idea to try out using multiple identical keyboards. Its weird though, because my Razer does publis its name correctly...

Multiple identical sounds like a lot of headache especially on Windows (possibly also on Mac). On Linux there's no such limits. 😉

Well not sure about identical devices as Keymapper identifies the devices by the reported name. But I'd be surprised if that couldn't be worked around using udev rules. For instance, I've created a rule to map Keymapper's virtual input device to a specific device, so that I can attach evtest to listen on the events without tracing to which input device number it's currently linked to.

$ cat /etc/udev/rules.d/99-keyboard.rules 
#
# Create symlink for Keymapper's virtual input device
#
SUBSYSTEM=="input", ATTRS{name}=="Keymapper", SYMLINK+="keymapper"


$ ls -l /dev/keymapper
lrwxrwxrwx root root 13 B Tue Apr  9 22:02:10 2024  /dev/keymapper ⇒ input/event20

I can then do this:

$ sudo evtest /dev/keymapper | grep -Ev '(REL_X|REL_Y|SYN_REPORT)'
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0xd1ce product 0x1 version 0x1
Input device name: "Keymapper"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 1 (KEY_ESC)
    Event code 2 (KEY_1)
    Event code 3 (KEY_2)
...
      Value      0
      Min        0
      Max     1023
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1712698213.100691, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0
Event: time 1712698213.409776, type 1 (EV_KEY), code 35 (KEY_H), value 1
hEvent: time 1712698213.448761, type 1 (EV_KEY), code 35 (KEY_H), value 0
Event: time 1712698213.609793, type 1 (EV_KEY), code 18 (KEY_E), value 1
eEvent: time 1712698213.695726, type 1 (EV_KEY), code 18 (KEY_E), value 0
Event: time 1712698213.762754, type 1 (EV_KEY), code 38 (KEY_L), value 1
lEvent: time 1712698213.821754, type 1 (EV_KEY), code 38 (KEY_L), value 0
Event: time 1712698213.908614, type 1 (EV_KEY), code 38 (KEY_L), value 1
lEvent: time 1712698213.977647, type 1 (EV_KEY), code 38 (KEY_L), value 0
Event: time 1712698214.074795, type 1 (EV_KEY), code 24 (KEY_O), value 1
oEvent: time 1712698214.139815, type 1 (EV_KEY), code 24 (KEY_O), value 0
Event: time 1712698218.885735, type 1 (EV_KEY), code 57 (KEY_SPACE), value 1
 Event: time 1712698218.962762, type 1 (EV_KEY), code 57 (KEY_SPACE), value 0
Event: time 1712698218.991758, type 1 (EV_KEY), code 17 (KEY_W), value 1
wEvent: time 1712698219.071970, type 1 (EV_KEY), code 17 (KEY_W), value 0
Event: time 1712698219.106959, type 1 (EV_KEY), code 24 (KEY_O), value 1
oEvent: time 1712698219.194910, type 1 (EV_KEY), code 24 (KEY_O), value 0
Event: time 1712698219.264923, type 1 (EV_KEY), code 19 (KEY_R), value 1
rEvent: time 1712698219.341965, type 1 (EV_KEY), code 19 (KEY_R), value 0
Event: time 1712698219.430947, type 1 (EV_KEY), code 38 (KEY_L), value 1
lEvent: time 1712698219.510912, type 1 (EV_KEY), code 38 (KEY_L), value 0
Event: time 1712698219.525952, type 1 (EV_KEY), code 32 (KEY_D), value 1
dEvent: time 1712698219.619953, type 1 (EV_KEY), code 32 (KEY_D), value 0

This reminded me of an obscure Keymapper related issue I solved during the weekend. I found a Logitech trackball mouse from one of my gadget archive boxes. I don't remember ever using it, I got it from my father >10y ago. I figured to try if I can do something neat with it with Keymapper. With some testing I realized it's the perfect secondary/left hand mouse for scrolling long pages/files very precisely.

I just needed to find a way to make it effortless, e.g. scroll while pressing one of it's buttons. As happens time after time, I end up in Arch Wiki which has instructions on how to make it do just that (among other things) https://wiki.archlinux.org/title/Logitech_Marble_Mouse#Scroll_modifier. However, nothing I tried seemed to work. After hours of head scratching, it occurred to me to check if Keymapper could somehow be the culprit. Sure enough, if I stopped Keymapper the scroll modifier started working with this config:

$ cat /etc/X11/xorg.conf.d/10-logitech-marble-mouse.conf 
Section "InputClass"
    Identifier    "Marble Mouse"
	MatchIsPointer "on"
    MatchProduct  "Logitech USB Trackball"
	Driver        "libinput"
	Option        "ScrollMethod"        "button"
	Option        "ScrollButton"        "8"
	Option        "AccelProfile"        "flat"
	Option        "HorizontalScrolling" "false"
	Option        "ButtonMapping"       "1 2 3 4 5 6 7 8 8"
EndSection

It turned out, as a low level remapping tool, Keymapper was grabbing the events and sending them out through it's virtual mouse. The trick was to set the libinput settings on the virtual device! I then created an over engineered systemd configuration that involved wrapping keymapper in a systemd user service and adding another systemd service bound to it which was responsible of redoing the mappings using xinput when ever keymapper got started/restarted.

Today I realized I can reach the same end result using the new ContextActive mechanism:

# On startup
setScrollButton = $(sleep 1 && xinput set-prop "pointer:Keymapper" "libinput Button Scrolling Button" 2)
enableScrollButton = $(sleep 1 && xinput set-prop "pointer:Keymapper" "libinput Scroll Method Enabled" 0, 0, 1)

[default]
  ContextActive >> on_startup
  on_startup    >> setScrollButton enableScrollButton

To further make it a more effortless scrolling device, I came up with this config:

MouseScrollLock = Virtual7
MouseTrackball   = "Logitech USB Trackball"

DoubleTap = $0 !250ms $0{!250ms}


# Mouse scroll lock (NB: this affects all pointer devices)
[modifier=MouseScrollLock]
  ContextActive           >> context["modifier = MouseScrollLock"]
  MouseButton             >> MouseScrollLock

[default]
  MouseScrollLock         >> ButtonMiddle


# Mouse Buttons // Logitech Trackball
[device=MouseTrackball]
  ContextActive           >> context["device = MouseTrackball"]

  DoubleTap[ButtonLeft]   >> ButtonBack
  DoubleTap[ButtonRight]  >> ButtonForward
  
 (ButtonLeft ButtonRight) >> MouseScrollLock
  
  ButtonLeft              >> ButtonRight
  ButtonRight             >> ButtonLeft
  ButtonForward           >> ButtonMiddle

Pretty nifty? 😎

FYI @houmain, you might be curious about the libinput adventure.

Forgot to mention, I found out the mouse is still produced and commands a whopping 200€ price tag! https://www.amazon.com/Logitech-910-000808-Marble-Mouse/dp/B001DQY9AW... And I just had it buried under some cables longer than I can remember! 😆

(It looks awfully ugly next to my Keychron K15 Pro and Logitech G502X Lightspeed mouse though...)

Ill pay you €5 for it.. including shipping

Where does keymapper get the name from (on Linux if it matters)?

@Zireael07 It uses the names listed with xinput.

$ xinput list
⎡ Virtual core pointer                    	id=2	[master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad              	id=24	[slave  pointer  (2)]
⎜   ↳ TPPS/2 Elan TrackPoint                  	id=25	[slave  pointer  (2)]
⎜   ↳ SINO WEALTH Gaming KB  Consumer Control 	id=34	[slave  pointer  (2)]
⎜   ↳ SINO WEALTH Gaming KB  Mouse            	id=38	[slave  pointer  (2)]
⎜   ↳ Keymapper                               	id=31	[slave  pointer  (2)]
⎜   ↳ Logitech USB Trackball                  	id=11	[slave  pointer  (2)]
⎜   ↳ Keychron Keychron K15 Pro Mouse         	id=15	[slave  pointer  (2)]
⎜   ↳ Keychron Keychron K15 Pro Consumer Control	id=16	[slave  pointer  (2)]
⎜   ↳ Logitech MX Anywhere 2S                 	id=18	[slave  pointer  (2)]
⎜   ↳ Logitech USB Receiver Keyboard          	id=21	[slave  pointer  (2)]
⎜   ↳ Logitech USB Receiver                   	id=27	[slave  pointer  (2)]
⎜   ↳ Generic Blue Microphones Consumer Control	id=29	[slave  pointer  (2)]
⎣ Virtual core keyboard                   	id=3	[master keyboard (2)]
    ↳ Virtual core XTEST keyboard             	id=5	[slave  keyboard (3)]
    ↳ Power Button                            	id=6	[slave  keyboard (3)]
    ↳ Video Bus                               	id=7	[slave  keyboard (3)]
    ↳ Sleep Button                            	id=8	[slave  keyboard (3)]
    ↳ Integrated Camera: Integrated C         	id=9	[slave  keyboard (3)]
    ↳ Integrated Camera: Integrated I         	id=10	[slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard            	id=23	[slave  keyboard (3)]
    ↳ ThinkPad Extra Buttons                  	id=26	[slave  keyboard (3)]
    ↳ SINO WEALTH Gaming KB  System Control   	id=33	[slave  keyboard (3)]
    ↳ SINO WEALTH Gaming KB  Consumer Control 	id=35	[slave  keyboard (3)]
    ↳ SINO WEALTH Gaming KB  Keyboard         	id=36	[slave  keyboard (3)]
    ↳ SINO WEALTH Gaming KB                   	id=37	[slave  keyboard (3)]
    ↳ Keymapper                               	id=32	[slave  keyboard (3)]
    ↳ Keychron Keychron K15 Pro Keyboard      	id=12	[slave  keyboard (3)]
    ↳ Keychron Keychron K15 Pro System Control	id=13	[slave  keyboard (3)]
    ↳ Keychron Keychron K15 Pro               	id=14	[slave  keyboard (3)]
    ↳ Keychron Keychron K15 Pro Consumer Control	id=17	[slave  keyboard (3)]
    ↳ Logitech MX Anywhere 2S                 	id=19	[slave  keyboard (3)]
    ↳ Lenovo ThinkPad Thunderbolt 3 Dock USB Audio	id=20	[slave  keyboard (3)]
    ↳ Logitech USB Receiver Keyboard          	id=22	[slave  keyboard (3)]
    ↳ Generic Blue Microphones                	id=28	[slave  keyboard (3)]
    ↳ Generic Blue Microphones Consumer Control	id=30	[slave  keyboard (3)]
    ↳ Ugreen-70304E (AVRCP)                   	id=39	[slave  keyboard (3)]

@ristomatti Thanks a lot, this should be mentioned in the README tbh

You can see it also with dmesg -w if you attach the device while monitoring the output:

$ dmesg -w
[2688710.195134] usb 5-2.1.4: USB disconnect, device number 13
[2688712.775513] usb 5-2.1.4: new full-speed USB device number 24 using xhci_hcd
[2688712.925149] usb 5-2.1.4: New USB device found, idVendor=3434, idProduct=02f6, bcdDevice= 1.02
[2688712.925153] usb 5-2.1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[2688712.925155] usb 5-2.1.4: Product: Keychron K15 Pro
[2688712.925156] usb 5-2.1.4: Manufacturer: Keychron
[2688712.943498] input: Keychron Keychron K15 Pro as /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:01.0/0000:07:00.0/0000:08:02.0/0000:09:00.0/usb5/5-2/5-2.1/5-2.1.4/5-2.1.4:1.0/0003:3434:02F6.0179/input/input812
[2688713.003846] hid-generic 0003:3434:02F6.0179: input,hidraw4: USB HID v1.11 Keyboard [Keychron Keychron K15 Pro] on usb-0000:09:00.0-2.1.4/input0
[2688713.004505] hid-generic 0003:3434:02F6.017A: hiddev1,hidraw5: USB HID v1.11 Device [Keychron Keychron K15 Pro] on usb-0000:09:00.0-2.1.4/input1
[2688713.005387] input: Keychron Keychron K15 Pro Mouse as /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:01.0/0000:07:00.0/0000:08:02.0/0000:09:00.0/usb5/5-2/5-2.1/5-2.1.4/5-2.1.4:1.2/0003:3434:02F6.017B/input/input813
[2688713.005521] input: Keychron Keychron K15 Pro System Control as /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:01.0/0000:07:00.0/0000:08:02.0/0000:09:00.0/usb5/5-2/5-2.1/5-2.1.4/5-2.1.4:1.2/0003:3434:02F6.017B/input/input814
[2688713.063871] input: Keychron Keychron K15 Pro Consumer Control as /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:01.0/0000:07:00.0/0000:08:02.0/0000:09:00.0/usb5/5-2/5-2.1/5-2.1.4/5-2.1.4:1.2/0003:3434:02F6.017B/input/input815
[2688713.064063] input: Keychron Keychron K15 Pro Keyboard as /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:01.0/0000:07:00.0/0000:08:02.0/0000:09:00.0/usb5/5-2/5-2.1/5-2.1.4/5-2.1.4:1.2/0003:3434:02F6.017B/input/input816
[2688713.064461] hid-generic 0003:3434:02F6.017B: input,hidraw6: USB HID v1.11 Mouse [Keychron Keychron K15 Pro] on usb-0000:09:00.0-2.1.4/input2

...or tail -f /var/log/kernel.log

Since the 4.1.1 release the tray icon menu contains an entry "Devices" which creates a notification containing the names of the grabbed devices. This should help with making device filters without additional tools.

The tray is neat <3 <3 but doesn't solve the underlying problem. I now have two numpads, and not only are they named as super generic "USB szh keyboard" but both of them report the same name.

udev cannot rename devices (As I just discovered) and keymapper seems to entirely ignore the symlinks I just spent half a day creating

I ended up using https://github.com/Blugatroff/diversion for the time being which lets me grab only the two numpads and do the rebinding magic in a Lua file. Might be worth looking at in terms of how they grab the input devices

@Zireael07 Nice find! Perhaps Keymapper could be extended to support device files as an alternative way to define device identifiers. Any thoughts @houmain?

So the /dev/input/by-id path would be the best way to distinguish identical devices on Linux?

@houmain Rather any file under /dev/input file. They're all just symlinks to /dev/input/eventXXX anyhow. I don't have two identical devices to test to be able to tell how they appear.

Or maybe just any file under /dev. I've for instance created an udev rule like this:

#
# Create symlink for Keymapper's virtual input device
#
SUBSYSTEM=="input", ATTRS{name}=="Keymapper", SYMLINK+="keymapper"

This creates a symlink to Keymapper's current device file under /dev/keymapper:

$ ll /dev/keymapper 
lrwxrwxrwx root root 13 B Wed May  1 22:26:13 2024  /dev/keymapper ⇒ input/event12

The dev/input/by-id is how I differentiated the two numpads, yes.

What is needed is not just the ability to follow symlinks but also the ability to specify devices using filenames as opposed to device names because the latter are hardcoded at driver/kernel level apparently

(Except for special characters, proudly typed on the two numpads under discussion which both have a device name of "usb szh keyboard")

There is a new device_id filter in the 4.2.0 release. The id is available for devices which have a /dev/input/by-id link.