xremap / xremap

Key remapper for X11 and Wayland
MIT License
1.49k stars 83 forks source link

xremap virtual keyboard buffer? #338

Closed Buttars closed 1 year ago

Buttars commented 1 year ago

I have a GPU passthough setup using virt-manager. To pass through they keyboard via virtio I have to pass the keyboard event device. In my case /dev/input/by-id/usb-SteelSeries_SteelSeries_Apex_7-event-kbd.

xremap intercepts my keyboard and makes it not output to this device, I'm assuming this is how the remapping works. This breaks my virtio and virt-manager setup because there is no way to map the keyboard to the virtio input device.

How can I bind to the events of xremap? Is there a virtual device I can bind to?

k0kubun commented 1 year ago

Yeah, xremap does create a virtual device. It lives under /dev/input, and if you read the name of devices under that directory, you should find something named like xremap pid=1234.

Buttars commented 1 year ago

Hey @k0kubun, thanks for the response.

When I search with find /dev -name "**xremap**" it returns nothing.

[~]$ xremap ~/.config/xremap/xremap.config
Selecting devices from the following list:
------------------------------------------------------------------------------
/dev/input/event0 : Sleep Button
/dev/input/event1 : Power Button
/dev/input/event10: RAZER Razer Mouse Dock
/dev/input/event11: RAZER Razer Mouse Dock
/dev/input/event14: Razer Razer DeathAdder V2 Pro
/dev/input/event15: Razer Razer DeathAdder V2 Pro Keyboard
/dev/input/event16: Razer Razer DeathAdder V2 Pro
/dev/input/event17: Razer Razer DeathAdder V2 Pro
/dev/input/event18: Razer Razer DeathAdder V2 Pro Consumer Control
/dev/input/event19: Asus WMI hotkeys
/dev/input/event2 : Power Button
/dev/input/event20: HDA ATI HDMI HDMI/DP,pcm=3
/dev/input/event21: HDA ATI HDMI HDMI/DP,pcm=7
/dev/input/event22: HDA ATI HDMI HDMI/DP,pcm=8
/dev/input/event23: HDA ATI HDMI HDMI/DP,pcm=9
/dev/input/event24: HDA ATI HDMI HDMI/DP,pcm=10
/dev/input/event25: HDA ATI HDMI HDMI/DP,pcm=11
/dev/input/event3 : SteelSeries SteelSeries Apex 7
/dev/input/event4 : SteelSeries SteelSeries Apex 7
/dev/input/event5 : SteelSeries SteelSeries Apex 7 Consumer Control
/dev/input/event7 : SteelSeries SteelSeries Apex 7 Mouse
/dev/input/event8 : RAZER Razer Mouse Dock
/dev/input/event9 : RAZER Razer Mouse Dock Keyboard
------------------------------------------------------------------------------
Selected keyboards automatically since --device options weren't specified:
------------------------------------------------------------------------------
/dev/input/event11: RAZER Razer Mouse Dock
/dev/input/event15: Razer Razer DeathAdder V2 Pro Keyboard
/dev/input/event17: Razer Razer DeathAdder V2 Pro
/dev/input/event3 : SteelSeries SteelSeries Apex 7
/dev/input/event4 : SteelSeries SteelSeries Apex 7
/dev/input/event9 : RAZER Razer Mouse Dock Keyboard
------------------------------------------------------------------------------

tree /dev/input

[~]$ tree /dev/input
/dev/input
├── by-id
│   ├── usb-Razer_Razer_DeathAdder_V2_Pro_000000000000-event-if01 -> ../event16
│   ├── usb-Razer_Razer_DeathAdder_V2_Pro_000000000000-event-if03 -> ../event18
│   ├── usb-Razer_Razer_DeathAdder_V2_Pro_000000000000-event-mouse -> ../event14
│   ├── usb-Razer_Razer_DeathAdder_V2_Pro_000000000000-if01-event-kbd -> ../event15
│   ├── usb-Razer_Razer_DeathAdder_V2_Pro_000000000000-if02-event-kbd -> ../event17
│   ├── usb-Razer_Razer_DeathAdder_V2_Pro_000000000000-mouse -> ../mouse2
│   ├── usb-RAZER_Razer_Mouse_Dock-event-if01 -> ../event10
│   ├── usb-RAZER_Razer_Mouse_Dock-event-mouse -> ../event8
│   ├── usb-RAZER_Razer_Mouse_Dock-if01-event-kbd -> ../event9
│   ├── usb-RAZER_Razer_Mouse_Dock-if02-event-kbd -> ../event11
│   ├── usb-RAZER_Razer_Mouse_Dock-mouse -> ../mouse1
│   ├── usb-SteelSeries_SteelSeries_Apex_7-event-if03 -> ../event5
│   ├── usb-SteelSeries_SteelSeries_Apex_7-event-kbd -> ../event3
│   ├── usb-SteelSeries_SteelSeries_Apex_7-if02-event-kbd -> ../event4
│   ├── usb-SteelSeries_SteelSeries_Apex_7-if03-event-mouse -> ../event7
│   └── usb-SteelSeries_SteelSeries_Apex_7-if03-mouse -> ../mouse0
├── by-path
│   ├── pci-0000:00:14.0-usb-0:7.1:1.0-event-mouse -> ../event14
│   ├── pci-0000:00:14.0-usb-0:7.1:1.0-mouse -> ../mouse2
│   ├── pci-0000:00:14.0-usb-0:7.1:1.1-event -> ../event16
│   ├── pci-0000:00:14.0-usb-0:7.1:1.1-event-kbd -> ../event15
│   ├── pci-0000:00:14.0-usb-0:7.1:1.2-event-kbd -> ../event17
│   ├── pci-0000:00:14.0-usb-0:7.1:1.3-event -> ../event18
│   ├── pci-0000:00:14.0-usb-0:7.2:1.0-event-mouse -> ../event8
│   ├── pci-0000:00:14.0-usb-0:7.2:1.0-mouse -> ../mouse1
│   ├── pci-0000:00:14.0-usb-0:7.2:1.1-event -> ../event10
│   ├── pci-0000:00:14.0-usb-0:7.2:1.1-event-kbd -> ../event9
│   ├── pci-0000:00:14.0-usb-0:7.2:1.2-event-kbd -> ../event11
│   ├── pci-0000:00:14.0-usb-0:8:1.0-event-kbd -> ../event3
│   ├── pci-0000:00:14.0-usb-0:8:1.2-event-kbd -> ../event4
│   ├── pci-0000:00:14.0-usb-0:8:1.3-event -> ../event5
│   ├── pci-0000:00:14.0-usb-0:8:1.3-event-mouse -> ../event7
│   ├── pci-0000:00:14.0-usb-0:8:1.3-mouse -> ../mouse0
│   ├── pci-0000:00:14.0-usbv2-0:7.1:1.0-event-mouse -> ../event14
│   ├── pci-0000:00:14.0-usbv2-0:7.1:1.0-mouse -> ../mouse2
│   ├── pci-0000:00:14.0-usbv2-0:7.1:1.1-event -> ../event16
│   ├── pci-0000:00:14.0-usbv2-0:7.1:1.1-event-kbd -> ../event15
│   ├── pci-0000:00:14.0-usbv2-0:7.1:1.2-event-kbd -> ../event17
│   ├── pci-0000:00:14.0-usbv2-0:7.1:1.3-event -> ../event18
│   ├── pci-0000:00:14.0-usbv2-0:7.2:1.0-event-mouse -> ../event8
│   ├── pci-0000:00:14.0-usbv2-0:7.2:1.0-mouse -> ../mouse1
│   ├── pci-0000:00:14.0-usbv2-0:7.2:1.1-event -> ../event10
│   ├── pci-0000:00:14.0-usbv2-0:7.2:1.1-event-kbd -> ../event9
│   ├── pci-0000:00:14.0-usbv2-0:7.2:1.2-event-kbd -> ../event11
│   ├── pci-0000:00:14.0-usbv2-0:8:1.0-event-kbd -> ../event3
│   ├── pci-0000:00:14.0-usbv2-0:8:1.2-event-kbd -> ../event4
│   ├── pci-0000:00:14.0-usbv2-0:8:1.3-event -> ../event5
│   ├── pci-0000:00:14.0-usbv2-0:8:1.3-event-mouse -> ../event7
│   ├── pci-0000:00:14.0-usbv2-0:8:1.3-mouse -> ../mouse0
│   └── platform-asus-nb-wmi-event -> ../event19
├── event0
├── event1
├── event10
├── event11
├── event14
├── event15
├── event16
├── event17
├── event18
├── event19
├── event2
├── event20
├── event21
├── event22
├── event23
├── event24
├── event25
├── event3
├── event4
├── event5
├── event6
├── event7
├── event8
├── event9
├── mice
├── mouse0
├── mouse1
├── mouse2
└── mouse3

Any idea where the virtual device is hiding?

k0kubun commented 1 year ago

Can you try running a couple of xremap processes at once? The second one could use --device xreamp, and that one should list the xremap device. It's intentional that the first process doesn't list a device it's going to create.

Buttars commented 1 year ago

That worked. /dev/input/event6 : xremap pid=18420

I'm assuming this wont be the same between reboots or even if xremap is killed and restarted? Is there any pattern I can use to help identify the correct input event?

I'm trying to pass this into a VM which requires an explicit reference. Not sure how to approach this problem. Is there a way to explicitly set the virtual output event/device name?

k0kubun commented 1 year ago

I'm not sure either.

As a workaround, you could modify this line to just pass "xremap", build xremap with that source, and run it. It should always give you xremap as a device name.

Buttars commented 1 year ago

I'm not sure either.

As a workaround, you could modify this line to just pass "xremap", build xremap with that source, and run it. It should always give you xremap as a device name.

Would it make sense for a feature to add named paths to each of the events under /dev/inputs/by-id for each instance of xremap? Might make a good first issue and give me a reason to dive into rust.

Buttars commented 1 year ago

@k0kubun Would you be open to having xremap create a symbolic link in /dev/input/by-id? Would you want this to be created for every virtual device or as a flag/config item? What would a reasonable syntax be? xremap-virtual-device-X?

k0kubun commented 1 year ago

Oh sorry, I somehow missed the notification for the previous reply.

/dev/inputs/by-id for each instance of xremap? What would a reasonable syntax be? xremap-virtual-device-X?

I'm tempted to call it xremap0, xremap1, ... if that's allowed.

Would you mind creating a PoC pull request? I'd be open to merging it as long as it's possible with or without sudo on the supported platforms. As of now, I'm not sure if I can implement it myself.

Buttars commented 1 year ago

I'm working on one now. Rust is very new to me so it's going to take some time as I learn the weird language. I'll open a PR as soon as possible. Thanks for the replies.

Buttars commented 1 year ago

@k0kubun I've got a POC working but it requires the user to change the owner of the /dev/input/by-id directory which is probably (certainly) a bad idea. Typically these symlinks are created using a udev rule. Do you know of any way udev or another native linux library can be used to create the symlink programmatically in rust?

k0kubun commented 1 year ago

No, I don't know of any. I have a couple of questions:

Buttars commented 1 year ago

Do you really need to have it under /dev/input? Can you specify non-/dev/input path in virt-manager?

Yes that is an option, although /dev/input/by-id is the linux appropriate place to put those type of device symlinks.

The trouble is that virt-manager does not offer a scripting feature without breaking down into qemu so the reference would have to be semi-static. Like xremap0, xremap1,...xremapN worked for me because I know I only run one instance and I control the instances of xremap. This would be less useful if the linux box was a long lived multi-user session but that's probably outside the scope of the desired change.

Could you check the permission of /dev/input and optionally create the symlink only if possible?

Changing the ownership of that directory is very likely a bad idea. Linux relies on that directory for all sorts of input related stuff and I'm not sure what the behavior would be. Ownership resets on each boot since it's the kernel that creates that directory and assigns ownership to root directly. It's possible to prevent this with a udev rule but that's a rabbit hole I don't want to go down and breaks the simplicity of using xremap.

I'm seriously considering opening an issue with the linux kernel to extend the uinput UI_DEV_SETUP macro to allow the definition of a device by-id name and have it create a symlink in kernel. What is weird is physical devices get this functionality but they don't extend that to virtual devices created with uinput.

...

Udev rules can target specific vendor and device ids. If something more unique was used than the current 1234 and 5678 then udev could run a script on each of those devices. That script would run as root since it's invoked by udev and would have the necessary permissions to create the symlinks.

The vendor and product id could be something like 1337 and 1111 as long as the vendor ID is unique that would work for udev. I could then write a local bash script to add the xremapX symlinks to /dev/input/by-id. This isn't a xremap centric approach but would do what I need it to.

I'd appreciate your feedback on the proposed approach.

k0kubun commented 1 year ago

I'd appreciate your feedback on the proposed approach.

I'm not sure if I follow what you're trying to do since I'm not familiar with udev/product id/vendor id. Could you make a change that is needed on the xremap side as a pull request? As long as the change on the xremap side is simple enough, I'd accept the patch to make your solution viable.

Buttars commented 1 year ago

Off topic but related, devices have a Uniq field that contains a unique identifier like a serial number but yet again uinput doesn't support that field...If it did it would be trivial to use that value in udev to create a symlink.

Buttars commented 1 year ago

Here is a rough draft. If the user proves the --unique-id flag the device is named xremap uniq=XXXXX. uniq was chosen to mimic the unique value that should go onto the Uniq field of the device if uinput supported it. I'm hoping in the future the kernel will support that field. The refactor would be simple to migrate that argument onto the device property and remove the conditional name logic from the device.rs

https://github.com/k0kubun/xremap/pull/346

k0kubun commented 1 year ago

I accidentally closed https://github.com/k0kubun/xremap/pull/346 and couldn't reopen it, so I opened another one with your change at https://github.com/k0kubun/xremap/pull/347. Now I understand what you're trying to do, so I appreciate your work.

I have one suggestion. Assuming there's no use case for running multiple xremap processes besides testing, can we just name the first device xremap and add the pid={} suffix only to devices added later? This would still give you a reliable device name while not adding a new command-line option. WDYT?

Buttars commented 1 year ago

What about multi-user sessions? That's not my use case but considering it might be others. How does xremapper handle multi-user sessions? I assume that since it runs in user space the remapping would only apply to the current signed in user. Am I wrong thinking that? Or since xremapper is somehow hooking the physical device and creating a virtual one does it carry across sessions?

It would make sense to have the ability to create named virtual devices for complex setups especially ones where multiple virtual devices are necessary.

Another use case where having named devices may be useful is combining multiple axis devices like controllers or joysticks into a single virtual device allowing the mapping of the signals to specific binds but that may be out of scope.

Buttars commented 1 year ago

I'm imagining a use case where a user wants two separate virtual inputs to group several devices together. Multiple many-to-one virtual devices.

Do you know how Linux handles when a user plugs in two of the same model of keyboard?

k0kubun commented 1 year ago

We could name it xremap user=$USER, but I think using xremap by default and falling back to xremap pid=$pid should be also fine unless somebody really needs it.

Changing device names is okay with me, but I'm reluctant to add a command line flag when your problem could be solved without it. As long as "that's not your use case" and I don't know of anybody else who needs it, at least I don't want to add a command like flag for somebody that does not exist.

k0kubun commented 1 year ago

If you want to object to the default xremap & xremap pid=$pid fallback idea, you should bring your own use case. Please don't "imagine a use case" or talk about something "that's not your use case". We can easily adjust device names when somebody says they need it. But once you add a command-line flag, that's harder to change. So I'm very reluctant to merge that portion unless we know there's a real person who will use it.

k0kubun commented 1 year ago

Do you know how Linux handles when a user plugs in two of the same model of keyboard?

No, I don't. Feel free to investigate that, but I'd rather not comply with that since leaving it pid=$pid is rather more backward compatible for secondary processes than changing it.

k0kubun commented 1 year ago

Filed https://github.com/k0kubun/xremap/pull/348. Can you test that branch? If that satisfies your use case, I don't want to add a command-line flag or change device names any further.

Buttars commented 1 year ago

Yes that satisfies my use case, for now.

Buttars commented 1 year ago

No, I don't. Feel free to investigate that, but I'd rather not comply with that since leaving it pid=$pid is rather more backward compatible for secondary processes than changing it.

I agree, I think working with the linux kernel to expand the uinput api to support the uniq device attribute is a more reasonable approach. I'll open up an issue to see what the kernel maintainers think.

I think you're correct that it's probably a bad idea to try and make xremap handle unique naming when that responsibility should lie with udev.

k0kubun commented 1 year ago

OK, I'll merge and release that feature.