Skip to content

Native Controller Support Implementation - #508

Closed
varung2 wants to merge 3 commits into
ZalithLauncher:mainfrom
varung2:main
Closed

Native Controller Support Implementation#508
varung2 wants to merge 3 commits into
ZalithLauncher:mainfrom
varung2:main

Conversation

@varung2

@varung2 varung2 commented Dec 27, 2025

Copy link
Copy Markdown

Native Controller Support Implementation

🎮 Overview

This PR adds full native gamepad/controller support to ZalithLauncher, allowing Minecraft to receive direct controller input via the GLFW joystick API. Previously, controllers only worked through keyboard emulation. Now users can choose between keyboard emulation mode (legacy, default) or native gamepad mode (new, experimental) for a true controller experience in Minecraft 1.12+. My specific reason for adding this is because I wanted to use the native controller input built into my android handheld when playing minecraft (and with Controlify mod). Full disclosure: AI coding tools were partly used to help develop this feature and for this description below.

Test instructions are at the bottom, I've been testing with my integrated physical controller but this input flow should work with any bluetooth controller. For perf concerns, I think the impact should be minimal as it uses the same thread as before that processes inputs, so latency should be the same as before.

Please feel free to edit and modify this PR request. Also, if more documentation is desired, I'd be glad to add it. Thanks!

✨ Key Features

1. Native Gamepad Input

  • Direct controller input to Minecraft via GLFW joystick API
  • Full analog stick support (no WASD conversion)
  • Pressure-sensitive triggers
  • All 15 standard gamepad buttons supported
  • D-pad with proper button mapping

2. Non-Breaking Implementation

  • Defaults to keyboard emulation mode (backward compatible)
  • Users must explicitly opt-in to native mode

3. Dual Input Architecture

  • Keyboard Emulation Mode (default): Current behavior, works with all Minecraft versions
  • Native Gamepad Mode (opt-in): Direct controller input for Minecraft 1.12+
  • Users can switch between modes in settings

🏗️ Architecture

System Flow Diagram

graph TB
    subgraph "Hardware Layer"
        A[Physical Controller<br/>Xbox/PS/Generic]
    end
    
    subgraph "Android Input Layer"
        B[Android InputDevice API]
        C[KeyEvent / MotionEvent]
    end
    
    subgraph "ZalithLauncher Kotlin Layer"
        D[GamepadEvents.kt<br/>Event Capture]
        E[GamepadRemapper.kt<br/>Button/Axis Mapping]
        F[GamepadViewModel.kt<br/>State Management]
        G{Control Mode?}
        H[Keyboard Emulation<br/>Path]
        I[Native Gamepad<br/>Path]
    end
    
    subgraph "Java Bridge Layer"
        J[CallbackBridge.java<br/>sendGamepadButton/Axis]
        K[sendKeyPress<br/>Legacy Path]
    end
    
    subgraph "Native C/JNI Layer"
        L[input_bridge_v3.c<br/>Event Queue]
        M[pojavPumpEvents<br/>Event Processing]
    end
    
    subgraph "LWJGL/GLFW Layer"
        N[GLFW.java<br/>State Storage]
        O[GLFWGamepadState<br/>15 buttons + 6 axes]
        P[glfwGetGamepadState<br/>Polling API]
    end
    
    subgraph "Minecraft"
        Q[Minecraft Game Loop]
        R[Controller Input Handler]
    end
    
    A --> B
    B --> C
    C --> D
    D --> E
    E --> F
    F --> G
    
    G -->|Emulation Mode| H
    G -->|Native Mode| I
    
    H --> K
    K --> L
    
    I --> J
    J --> L
    
    L --> M
    M -->|Update State| N
    N --> O
    O --> P
    
    M -->|Legacy Callbacks| Q
    P -->|Modern Polling| Q
    
    Q --> R
    
    style G fill:#ff9800
    style I fill:#4caf50
    style H fill:#2196f3
    style O fill:#9c27b0
    style P fill:#9c27b0
Loading

Data Flow by Mode

Native Mode Flow

Physical Controller
  ↓
Android InputDevice API (KeyEvent/MotionEvent)
  ↓
GamepadEvents.kt (Raw event capture)
  ↓
GamepadRemapper.kt (Controller-specific mapping)
  ↓
GamepadViewModel.kt (Button/Axis mapping to GLFW)
  ↓
CallbackBridge.sendGamepadButton/Axis()
  ↓
input_bridge_v3.c (Native JNI - Event queueing)
  ↓
pojavPumpEvents() (Event processing)
  ↓
GLFW.internalUpdateGamepadButton/Axis()
  ↓
GLFWGamepadState (15 buttons + 6 axes storage)
  ↓
Minecraft calls glfwGetGamepadState() every frame
  ↓
Controller works natively in Minecraft!

🎯 User Experience

Access Path

Main Menu → Settings → Gamepad → [Native Controller Mode Toggle]

Default Behavior

  • Native mode defaults to OFF (keyboard emulation)
  • Zero impact on existing users
  • Must be explicitly enabled by user

When Native Mode is Enabled

  1. Toggle appears after main gamepad control switch
  2. Warning card displays version requirements
  3. Keyboard emulation settings remain visible but greyed out
  4. Button mappings disabled (configured in Minecraft instead)
  5. Controller sends direct input to Minecraft

Controller Connection Flow

  1. User connects controller (USB/Bluetooth)
  2. ZalithLauncher automatically detects controller
  3. Controller profile created/loaded
  4. Settings applied from profile
  5. Controller ready to use immediately

🔒 Backward Compatibility

Backwards Compatible

  • Default setting: OFF - Native mode is opt-in only
  • Existing keyboard emulation completely unchanged
  • No breaking changes to any API
  • Older Minecraft versions (<1.12) continue working via keyboard emulation
  • All existing functionality preserved

Safe Defaults

val gamepadNativeMode = SettingBoolean(
    key = "gamepad_native_mode",
    defaultValue = false  // ⚠️ CRITICAL: Must be OFF by default
)

🚀 How to Test This PR

Prerequisites

  1. Build and install the app
  2. Have a physical controller (Xbox, PS, or generic USB gamepad)
  3. Minecraft 1.12+ installed

Test Procedure

  1. Connect Controller: Plug in controller via USB or pair via Bluetooth
  2. Enable Native Mode: Settings → Gamepad → Enable "Native Controller Mode"
  3. Launch Minecraft: Start any Minecraft version 1.12 or newer
  4. Test Input:
    • All buttons (A, B, X, Y, bumpers, triggers, start, select)
    • Analog sticks (movement and camera)
    • L3/R3 (stick button presses)
    • D-pad (all 4 directions)
  5. Verify: Check that controller is recognized with real device name in Minecraft's controller settings

Expected Results

  • ✅ Controller detected with real device name
  • ✅ All buttons respond correctly
  • ✅ Analog sticks provide smooth movement
  • ✅ L3/R3 buttons work (crouch/sprint)
  • ✅ D-pad works for menu navigation

📖 References

GLFW Documentation

Android Documentation

LWJGL Documentation

@Lo-quee Lo-quee closed this Jan 30, 2026
@astralyear7-del

Copy link
Copy Markdown

Hi, I am on Android, how to connect my controller.

@fakemade

fakemade commented Feb 6, 2026

Copy link
Copy Markdown

Does it work? And why is it cancelled?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants