Linux PC support (enhancement)
Hi. Cool piece of software. And using cross-platform tools (not, for instance C#). Can it be repurposed/compiled for free OS GNU/Linux (WSL under Windows), Debian/Ubuntu at least? This would be much more in line with Android which also runs Linux kernel and has FOSS license. Would be great!
I currently do not have the time and energy to support Linux.
If anyone wants to try (looking at you @shriramters), they are welcome to.
We'll need a Linux counterpart to the Windows input injection API (SendInput).
A quick websearch reveals /dev/uinput which could be a starting point.
The functions in src/simulation will need to behave differently based on whether it is Windows or Linux.
P.S.: Apologies for the delay. I had accidentally turned off notifications for this repo so I missed this issue.
I currently do not have the time and energy to support Linux.
If anyone wants to try (looking at you @shriramters), they are welcome to.
We'll need a Linux counterpart to the Windows input injection API (SendInput). A quick websearch reveals
/dev/uinputwhich could be a starting point.The functions in src/simulation will need to behave differently based on whether it is Windows or Linux.
P.S.: Apologies for the delay. I had accidentally turned off notifications for this repo so I missed this issue.
This will involve a pretty significant rewrite of the entire backend
specifically, all low-level system input code (SendInput etc) needs to be refactored to switch to linux syscalls (e.g., open("/dev/uinput"), ioctl, write events etc.) based on platform
Work on Linux support is underway at: https://github.com/kitswas/VirtualGamePad-PC/tree/experimental/multiplatform
There are two executors, a GamepadExecutor and a KeyboardMouseExecutor. Switch between the two via the settings UI.
I request people familiar with libevdev or uinput to verify the code in src/simulation/linux.
There are a lot of magic numbers in the code which I got from the documentation.
The Linux app is ready for preliminary testing.
Attaching a Google Drive link as GitHub has a 25MB limit for file uploads.
https://drive.google.com/file/d/1FJgecll9lPuflNrWZR3uXKCAekN9KVCG/view?usp=sharing
File hash sha256:baab08a64e1d59a40641f641ff3d6b8396d350b8f6ba42c1d5fd41b3bca4aa9c
@osering Since you created this issue, go ahead and do the honours.
Extract the zip file and run bin/VGamepadPC.
If the launch fails due to missing libraries, I need to know. (Packing for Linux has been a huge pain)
See the in-app preferences screen to set up access to /dev/uinput or run with sudo.
Server has no problems starting on Fedora, even without root. Only tested functionality in Minecraft with Controlify but there I've noticed the DPad not working (Down, Left & Right didn't register at all and Up was recognized as a press of the right joystick). Apart from that i've noticed no issues
(seems to be gamepad mode only)
jstest-gtk does recognize dpad buttons but https://hardwaretester.com/gamepad (and probably other apps) doesn't
@andreymal I'm confused.
Using hardwaretester on Firefox with Ubuntu 24.04.2 LTS. The dpad (arrow keys) activates B12, B13, B14 and B15.
~~The UI for the server looks different. (Did you modifiy it?)~~
~~Are we talking about the same app here?~~
Edit: I mistook the jstest-gtk UI for a modified server.
Also, hardwaretester.com says:
The Gamepad API is still inconsistent across browsers and OSes.
Yeah, Firefox is different, I tested Chromium
Anyway, Steam doesn't recognize the dpad by default either, so I guess Chromium is more relevant here
(but Steam allows to remap gamepad buttons, so the dpad is probably usable in Steam games, just not by default)
After some digging around, I have a guess for the DPad buttons.
From the Linux Gamepad Specification for Kernel v6,
D-Pad:
Every gamepad provides a D-Pad with four directions: Up, Down, Left, Right Some of these are available as digital buttons, some as analog buttons. Some may even report both. The kernel does not convert between these so applications should support both and choose what is more appropriate if both are reported.
- Digital buttons are reported as: BTN_DPAD_*
- Analog buttons are reported as: ABS_HAT0X and ABS_HAT0Y (for ABS values negative is left/up, positive is right/down)
The current GamepadExecutor implementation reports BTN_DPAD_*, which might not be recognized by some apps.
This hypothesis needs to be tested. (The true reason could be much different)
This doesn't explain why "Up was recognized as a press of the right joystick"
I think I found the real cause — the SDL game controller database:
$ sdl2-jstest -l
Found 1 joystick(s)
Joystick Name: 'Xbox 360 Controller'
Joystick GUID: 0300226a5e0400008e02000010010000
Joystick Number: 0
Number of Axes: 6
Number of Buttons: 14
Number of Hats: 0
Number of Balls: 0
GameControllerConfig:
Name: 'Xbox 360 Controller'
Mapping: '0300226a5e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,'
The default mapping is incorrect, for example, rightstick:b10 — the dpad Up is button 10, this is probably why it's recognized as "press of the right joystick"
(idk how to properly fix this though)
h0.1 also means that SDL expects analog D-pad
So I read some SDL code, patched VirtualGamePad-PC to match SDL's expectations, and this did the trick, the D-pad works by default with my patch
diff --git a/src/simulation/linux/gamepadSim.cpp b/src/simulation/linux/gamepadSim.cpp
index 7f88f7d..06f9ecb 100644
--- a/src/simulation/linux/gamepadSim.cpp
+++ b/src/simulation/linux/gamepadSim.cpp
@@ -44,10 +44,11 @@ GamepadInjector::GamepadInjector()
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TR, nullptr); // Right shoulder
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_SELECT, nullptr); // View/Back button
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_START, nullptr); // Menu/Start button
+ libevdev_enable_event_code(dev.get(), EV_KEY, BTN_MODE, nullptr); // Unused, just increments button id
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_THUMBL, nullptr); // Left thumbstick click
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_THUMBR, nullptr); // Right thumbstick click
- // D-pad
+ // D-pad (digital)
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_DPAD_UP, nullptr);
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_DPAD_DOWN, nullptr);
libevdev_enable_event_code(dev.get(), EV_KEY, BTN_DPAD_LEFT, nullptr);
@@ -69,6 +70,15 @@ GamepadInjector::GamepadInjector()
libevdev_enable_event_code(dev.get(), EV_ABS, ABS_RX, &absinfo);
libevdev_enable_event_code(dev.get(), EV_ABS, ABS_RY, &absinfo);
+ // D-pad (analog)
+ // The [-1, 1] range causes SDL to assume that the D-pad is actually digital
+ absinfo.minimum = -1;
+ absinfo.maximum = 1;
+ absinfo.fuzz = 0;
+ absinfo.flat = 0;
+ libevdev_enable_event_code(dev.get(), EV_ABS, ABS_HAT0X, &absinfo); // Left/Right
+ libevdev_enable_event_code(dev.get(), EV_ABS, ABS_HAT0Y, &absinfo); // Up/Down
+
// Triggers (0 to 255)
absinfo.minimum = 0;
absinfo.maximum = 255;
@@ -133,11 +143,25 @@ void GamepadInjector::setTriggers(float leftTrigger, float rightTrigger)
void GamepadInjector::pressButton(int buttonCode)
{
libevdev_uinput_write_event(uidev.get(), EV_KEY, buttonCode, 1);
+ if (buttonCode == BTN_DPAD_LEFT) {
+ libevdev_uinput_write_event(uidev.get(), EV_ABS, ABS_HAT0X, -1);
+ } else if (buttonCode == BTN_DPAD_RIGHT) {
+ libevdev_uinput_write_event(uidev.get(), EV_ABS, ABS_HAT0X, 1);
+ } else if (buttonCode == BTN_DPAD_UP) {
+ libevdev_uinput_write_event(uidev.get(), EV_ABS, ABS_HAT0Y, -1);
+ } else if (buttonCode == BTN_DPAD_DOWN) {
+ libevdev_uinput_write_event(uidev.get(), EV_ABS, ABS_HAT0Y, 1);
+ }
}
void GamepadInjector::releaseButton(int buttonCode)
{
libevdev_uinput_write_event(uidev.get(), EV_KEY, buttonCode, 0);
+ if (buttonCode == BTN_DPAD_LEFT || buttonCode == BTN_DPAD_RIGHT) {
+ libevdev_uinput_write_event(uidev.get(), EV_ABS, ABS_HAT0X, 0);
+ } else if (buttonCode == BTN_DPAD_UP || buttonCode == BTN_DPAD_DOWN) {
+ libevdev_uinput_write_event(uidev.get(), EV_ABS, ABS_HAT0Y, 0);
+ }
}
void GamepadInjector::inject()
@MrAn0nym Can you cross-check whether this works?
https://drive.google.com/file/d/1Bpe--O4YX9RrEONT3LLpKH0J370GzFbw/view?usp=sharing
The Linux app is ready for preliminary testing.
Attaching a Google Drive link as GitHub has a 25MB limit for file uploads. https://drive.google.com/file/d/1FJgecll9lPuflNrWZR3uXKCAekN9KVCG/view?usp=sharing File hash
sha256:baab08a64e1d59a40641f641ff3d6b8396d350b8f6ba42c1d5fd41b3bca4aa9c@osering Since you created this issue, go ahead and do the honours.
Extract the zip file and run bin/VGamepadPC. If the launch fails due to missing libraries, I need to know. (Packing for Linux has been a huge pain)
See the in-app preferences screen to set up access to /dev/uinput or run with sudo.
This is a somewhat fresh Ubuntu install so i haven't had to set up Qt for any reason yet.
[2025-08-04T17:04:01.715 warning] Could not find the Qt platform plugin "wayland" in "" [2025-08-04T17:04:01.715 warning] From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugi> [2025-08-04T17:04:01.715 info] Could not load the Qt platform plugin "xcb" in "" even though it was found. [2025-08-04T17:04:01.716 fatal] This application failed to start because no Qt platform plugin could be initialized. Re>
Available platform plugins are: xcb.
This is a somewhat fresh Ubuntu install so i haven't had to set up Qt for any reason yet.
[2025-08-04T17:04:01.715 warning] Could not find the Qt platform plugin "wayland" in "" [2025-08-04T17:04:01.715 warning] From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin. [2025-08-04T17:04:01.715 info] Could not load the Qt platform plugin "xcb" in "" even though it was found. [2025-08-04T17:04:01.716 fatal] This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: xcb.
Excellent! I have been looking for similar problems.
Did a clean install of Ubuntu 24 LTS on a VM and got the same error.
In this case, sudo apt install libxcb-cursor0 fixes the problem. (The message is clear enough) (Ideally we should not require this step)
The app runs after that.