VirtualGamePad-PC icon indicating copy to clipboard operation
VirtualGamePad-PC copied to clipboard

Linux PC support (enhancement)

Open osering opened this issue 11 months ago • 2 comments

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!

osering avatar Jan 03 '25 04:01 osering

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.

kitswas avatar May 16 '25 08:05 kitswas

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.

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

shriramters avatar May 16 '25 11:05 shriramters

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.

kitswas avatar Jul 06 '25 13:07 kitswas

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.

kitswas avatar Jul 21 '25 10:07 kitswas

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

MrAn0nym avatar Jul 26 '25 16:07 MrAn0nym

(seems to be gamepad mode only)

MrAn0nym avatar Jul 26 '25 17:07 MrAn0nym

jstest-gtk does recognize dpad buttons but https://hardwaretester.com/gamepad (and probably other apps) doesn't

andreymal avatar Jul 29 '25 09:07 andreymal

@andreymal I'm confused.

Using hardwaretester on Firefox with Ubuntu 24.04.2 LTS. The dpad (arrow keys) activates B12, B13, B14 and B15.

Image

~~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.

kitswas avatar Jul 29 '25 13:07 kitswas

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)

andreymal avatar Jul 29 '25 13:07 andreymal

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)

kitswas avatar Jul 29 '25 13:07 kitswas

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)

andreymal avatar Jul 29 '25 14:07 andreymal

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()

andreymal avatar Jul 29 '25 15:07 andreymal

@MrAn0nym Can you cross-check whether this works?

https://drive.google.com/file/d/1Bpe--O4YX9RrEONT3LLpKH0J370GzFbw/view?usp=sharing

kitswas avatar Jul 29 '25 18:07 kitswas

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.

eMadman avatar Aug 04 '25 21:08 eMadman

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.

Image

kitswas avatar Aug 05 '25 03:08 kitswas