CFXControl Flex Wiki
English中文

Platform Support

Control Flex reads controllers through libgamepadjni + SDL3. On desktop, both the native library and SDL3 ship inside the JAR, so it works out of the box; on Android, SDL3 must come from the launcher APK, which is why behaviour varies a little between launchers.

At a Glance

Platform Architectures SDL3 source Status
Windows x86_64 Bundled in the JAR (SDL3.dll) Supported
macOS x86_64 · Apple Silicon (aarch64) Bundled in the JAR (libSDL3.0.dylib) Supported
Linux x86_64 · aarch64 Bundled in the JAR (libSDL3.so) Supported
Android
game JVM inside a launcher
arm64-v8a · armeabi-v7a · x86_64 Provided by the launcher APK; never bundled Supported
iOS — — Not yet

Detection reads os.version only: a value starting with Android- means Android, everything else is treated as desktop. os.name (Android launchers also report Linux) is written to diagnostic logs and never used for detection.

Desktop

Android (inside a Launcher)

On Android, Minecraft: Java Edition runs on a JVM provided by a launcher, so the controller path is split in two: SDL3 comes from the launcher APK (the launcher already installed the SDL glue on the ART side), while libgamepadjni.so ships with the mod and is extracted per os.arch. The order is fixed — SDL3 first, then the JNI bridge — or the dynamic linker binds to a different copy.

Two Launcher Settings to Change First

Both live in the launcher, not in the mod, and both have to be redone whenever you switch launchers or the instance directory gets reset. Skipping them does not stop the game from starting — it just looks broken: a stray pointer floating over the screen, or a pad that never appears in settings.

1 · Hide the on-screen virtual mouse pointer

The pointer the launcher draws is its own overlay and has nothing to do with the mod's virtual cursor — the two do not track each other, so you end up with two pointers on screen, or with one that sits wherever you last touched while the actual click lands somewhere else.

Three equivalent ways to get rid of it; pick whichever your launcher exposes:

  • turn the pointer display off (hide the virtual mouse cursor)
  • set the pointer size to 0
  • set the pointer opacity to 0, i.e. fully transparent

After that only the mod's own cursor is on screen and its position matches where clicks land. Style and scale are set in game under Cursor Style: System / Bedrock / Hand, scale 0.1–1.0.

2 · Set Gamepad Input Mode to SDL Direct (ZL2 / FCL)

In Zalith Launcher 2 and Fold Craft Launcher, Gamepad Input Mode defaults to Mapped (Virtual Keys): buttons and sticks are first translated into virtual keyboard/mouse events, and only then handed to the game. ControlFlex never sees the raw device — no pad in settings, and deadzone, trigger travel, and rumble are all decided by the launcher.

Pick SDL Direct instead. In ZL2 the path is Settings → Gamepad → Gamepad Input Mode, and the option describes itself: raw gamepad input is delivered to the game through the SDL/GLFW gamepad API with button remapping bypassed — exactly what is needed here. SDL3 opens the device directly and the mod reads raw axes and buttons through libgamepadjni, which is the only reason multiple pads, hotplug, and rumble work at all.

  • First make sure the Gamepad Control master switch at the top of the same page is on — turn it off only when some other device is being mistaken for a gamepad.
  • Check: in game, Settings → Gamepad Selection lists the device by name and reacts to any button.
  • Trade-off: once SDL Direct is on, Mapped (Virtual Keys) no longer applies to that pad. That is expected — bind everything in the mod's key mapping panel instead.
  • Quit the game and relaunch after changing it, so the launcher re-enumerates devices.

Launchers Verified

Launcher How the launcher's libSDL3.so is found
Fold Craft Launcher (FCL) Sets POJAV_NATIVEDIR to the APK native dir; loaded by absolute path
Zalith Launcher 2 (ZL2) Same as above
Mojo Launcher
v3_openjdk
No POJAV_NATIVEDIR; the APK native dir sits on LD_LIBRARY_PATH, so System.loadLibrary("SDL3") resolves it
Amethyst Android Sets POJAV_NATIVEDIR and loads by absolute path

This is a compatibility note, not a runtime allowlist: any launcher that sets os.version to Android-* takes the Android path, listed or not.

ABIs and Natives

os.arch Directory in the JAR
aarch64 / arm64native/android-arm64-v8a/
arm / armv7l / armv8lnative/android-armeabi-v7a/
x86_64 / amd64native/android-x86_64/
i386 and others Unsupported — initialization fails and the game still launches, just without a controller

Android Caveats

Do not override os.version. Some launchers' custom JVM arguments or version JSON replace it with a plain kernel number (e.g. 5.0) or drop the Android- prefix. The mod then treats the JVM as desktop Linux, loads a desktop SDL3, and the controller never shows up in settings. Make sure the launcher's -Dos.version=Android-<release> is not overridden by your own arguments.

Platform & Dependency Matrix

Platform Extra installs Config location
Windows / macOS / Linux None (SDL3 bundled) config/controlflex/
Android None (SDL3 comes from the launcher; the launcher must be covered by one of the mechanisms above) — plus the two launcher settings above config/controlflex/ under the launcher's instance dir

Troubleshooting

Symptom Check first
No device at all in Settings → Gamepad Selection (desktop) Does the JAR match your MC version and loader (see Minecraft Versions)? Is another program (Steam Input, remote play) holding the pad?
On Android the game runs but no controller is ever visible Is os.version overridden? Is the launcher one of the verified ones? Do the logs show POJAV_NATIVEDIR and the SDL3 path?
Two pointers on screen, or the pointer sits away from where clicks land The launcher's virtual mouse pointer is still visible — hide it, or set its size / opacity to 0
The pad works through the launcher's own key mapping, but never appears in Settings → Gamepad Selection Gamepad Input Mode is set to Mapped (Virtual Keys) instead of SDL Direct — see the two settings above
UnsatisfiedLinkError in the log On Android, either an unsupported ABI (e.g. 32-bit x86) or the launcher APK lacks SDL3; on desktop, the native does not match the OS architecture — note that Windows only carries x86_64 natives, so ARM64 machines need an x64 JVM.
Nothing available on iOS iOS is not wired up yet (the platform layer reserves a flag, but no natives exist)