Conversation
… guard
Axis.value setter computed the raw vJoy position as:
half_range + half_range * value
which is correct only when axis_min == 0. The guard that enforced that
assumption (raise VJoyError if min != 0) makes it impossible to use an
axis whose DLL-reported minimum is non-zero.
Fix both issues together:
- Remove the guard: a non-zero minimum is a valid hardware configuration.
- Add self._min_value to the formula so the result is always in
[axis_min, axis_max] regardless of the reported minimum.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs in the original code: 1. The hook was always installed unconditionally (the second bare assignment outside the if-block overwrote the conditional one, making the guard a no-op). 2. The guard relied on the executable being named "joystick_gremlin.exe", which is Windows-only and would break any future non-Windows build. Replace both with a single check on sys.frozen, which PyInstaller sets to True on all platforms when the application is bundled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce three PAL trees – dill/platform/, gremlin/platform/, vjoy/platform/ – and the central gremlin/app_platform.py entry point. Each tree contains: - base/ : abstract base classes (ABC) that define the interface - windows/: full Win32 implementations (moved from original modules) - linux/ : stub implementations that compile and import on Linux app_platform.py selects the right backend at import time via sys.platform. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
keyboard.py, sendinput.py, windows_event_hook.py, tts.py, process_monitor.py now contain only a short module docstring and platform-dispatch imports. All Win32 implementation code has been moved to gremlin/platform/windows/. Equivalent Linux stubs live in gremlin/platform/linux/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dill/__init__.py
- Remove inline _GUID ctypes struct definition (moved to dill/platform/base/types.py)
- Import portable struct types and AbstractDillBackend from the PAL base layer
- Use app_backend for device enumeration; keeps public API identical
vjoy/vjoy_interface.py
- Replace direct ctypes DLL loading with AbstractVJoyBackend facade
- Select WindowsVJoyBackend or LinuxVJoyBackend at import time via sys.platform
- VJoyInterface class becomes a thin dispatcher over the backend
vjoy/vjoy.py
- Use the new VJoyInterface API (GetVJDAxisMin/Max now return values directly)
- Fix axis value calculation: add self._min_value offset (was missing, causing
off-by-min errors when axis min != 0)
- Allow VJoyState.Missing when acquiring a device (Linux creates vJoy devices
on first acquire)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gremlin/util.py
- is_user_admin(): delegate to app_backend instead of ctypes.windll.shell32
- userprofile_path(): delegate to app_backend.user_data_dir() (cross-platform)
gremlin/profile.py
- Use app_backend.temp_dir() instead of Windows-only %TEMP% env variable
gremlin/ui/backend.py
- Add executableFileFilter Qt property (returns "*.exe" on Windows, "All files"
on Linux) — consumed by QML file dialogs
- Keep a stable _default_identifier on UIState to prevent the transient
InputIdentifier from being garbage-collected before QML finishes using it
joystick_gremlin.py
- Replace SetCurrentProcessExplicitAppUserModelID ctypes call with
app_backend.configure_app()
- Use app_backend.font_family() so the UI font is chosen per platform
- Fix double sys.excepthook assignment (dead code); use sys.frozen check
instead of Windows-only .exe name comparison
qml/OptionProfileAutoLoading.qml
- Use backend.executableFileFilter instead of hardcoded "*.exe"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pyproject.toml - Make pywin32 Windows-only (platform marker: sys_platform == 'win32') - Add evdev >=1.7 as a Linux-only dependency (used by Linux input backend) gremlin/repeater.py - Fix stale PyQt5 import; replace with PySide6 (matches the rest of the project) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both fixes are prerequisites for the cross-platform branch: - fix/vjoy-axis: axis formula must handle non-zero min_value (required on Linux) - fix/excepthook: sys.frozen check works on all platforms; .exe name check does not Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3cb8382 to
60d4bf8
Compare
|
Modifying the code base such that it can run on linux is something that can be done. However, that would have to happen in a significantly different way. There would have to be an actual discussion on the approach, as there is a significant design space and whatever an LLM comes up with is likely short-sighted. Using LLMs is not bad per se, but they are problematic if used to whole sale modify large chunks of code in one fell swoop as done here. Also even if this entire PR was written by a human it would not be mergable either, as the amount of change is too big and too spread out. There are also changes made that do not relate to the linux side or are plain dangerous. For example, the modification of the vJoy axis value clearly shows the LLM did not understand the code and the supervising person also failed to realize the intent. The check is mandatory as it safeguards an implied assumption that should hold by DirectInput definition but, should it not hold, the code should fail as it's an exceptional circumstance. The way to integrate linux support into Gremlin is something along the following lines:
Without a good generalizable foundation there is a strong limitation how the Windows version can evolve and there is a significant risk of the entire software becoming brittle due to local hacks and patches if the support is not architected properly. |
|
Thank you for your very insightful feedback. From the way you bring up the subject, I gather that you consider the abstraction of I/O and virtual device management to be insufficient, and that a more comprehensive design is needed to incorporate fully functional cross-platform management. I admit I’d be curious to hear your perspective, but for now I imagine your priorities are currently fully focused on R14. As I mentioned earlier, my goal was to assess the feasibility of such a project. It turns out that while this is a feasible challenge, it does present many subtle issues that require a thorough understanding of how I/O works. In that sense, LLMs are indeed a practical and quick tool for producing a first draft, but I’ve noticed that they still struggle to fully grasp the intricacies of Gremlin. For now, I'm going to close this PR to spare you unnecessary clutter and I hope I won't had make you loose too much time. |
|
The issue with the abstraction is that it replicates the existing interface which is far from ideal. Replicating it also makes adding new inputs or outputs a significant burden as new platform interface and then two or more concrete implementations need to be written, when this may make no sense. For example on Windows vJoy and vigem are sensible output options, but on linux there is no such thing as xinput so replicating a "vigem for linux" bit would be pointless. So it would be better to have several output options that work with their corresponding actions. So vjoy, vigem, uinput, etc. Those would be able to decide if they exist etc. The map to vjoy action, for example, checks if vJoy devices exist and if they don't it doesn't show up as available. In that sense the entire input and output side would ideally be generic enough to allow many different "providers" to exist which then may happen to be os specific. Though all of the above is my current thinking and likely incomplete and has short comings, which is why the change to the profile from R13 to R14 took probably four or five refinements as I encountered new things it couldn't do or things that would be better for long-term usability if it changed. That's where the discussion comes in to find what flaws exist or constraints are overlooked etc. |
Context
As a long-time user of Joystick Gremlin — a truly fantastic tool — I've been particularly frustrated since wanting to migrate to Linux, as the native solutions, while functional, are far less advanced. I therefore took on the somewhat ambitious challenge of trying to port JG to Linux! Since this work is starting to bear fruit, I thought it would be a good idea to open a PR (even though there are still quite a few issues to resolve) in order to start the conversation.
In practice, I imagine that Joystick Gremlin is a project that is far from easy to develop and maintain, and that it has always been very strongly tied to Windows. I understand that someone showing up out of nowhere to propose Linux support could represent a lot of potential headaches! So in all honesty, I would completely understand if this PR were rejected for being too large, too complex to maintain over time, or simply not aligned with the project's desired philosophy. That said, I still think it's worth opening the discussion, as more and more users are migrating to Linux.
I want to be clear that my main goals throughout the development of this PR were:
If I got some free time soon, and if you have some interest in, I'll try to add a demo !
Overview of Changes
(I know, there's a lot of changes ! And I messed up my commits at some point so it's not easy to read ! Sorry for that...)
Adding Linux support to Joystick Gremlin is not extremely complicated on paper: there are only two Windows-specific dependencies to replace, and the equivalent building blocks exist on Linux and are relatively straightforward to work with. The bulk of the work therefore consists of teaching Joystick Gremlin to use these building blocks the same way it would use the Windows DLLs.
The dependencies to replace are:
Joystick Gremlin's code is already very well organized in the sense that the DILL and VJoy wrappers are already identified and isolated. I therefore simply had to develop a Platform Abstraction Layer (PAL) to transparently manage the Windows and Linux dependencies.
There are 3 PALs in total:
Most of the changes made to Joystick Gremlin's core only aim to replace direct Windows system calls with calls to these abstraction layers.
Within the PALs, the Windows-specific code has not been rewritten: the original code has simply been moved and reorganized to fit into this new architecture.
Beyond that, the vast majority of the new additions concern the Linux-specific implementation.
Supported Features
This section is a work in progress: Joystick Gremlin has many features, and it's not easy to quickly go through all of them and validate everything!
Overall, I would say that the core features are working:
Known Bugs
It is not easy to distinguish existing bugs in R14 from bugs introduced by this PR, but I'm working on it! This list is therefore not exhaustive:
Transparency About AI Usage
I know this is a debated topic, so I prefer to be upfront about it. If this is a major concern, I fully understand and have no objection. Everyone is free to act as they see fit.
Although I was fully aware of the major changes needed to make the project Linux-compatible, I used Claude Code to handle the bulk of the code writing, as the scope of the work was very significant. My original intention was more to produce a proof of concept than anything else. I personally oversaw all design decisions and made sure the AI did not touch anything fundamental in Joystick Gremlin. I also used AI to generate commit descriptions that are admittedly verbose, but thorough and relevant.