LITIENGINE Case Study

How LITIENGINE replaced JInput with Input4j for gamepad support

From JInput to Input4j

LITIENGINE, the pure 2D Java game engine, migrated from JInput to Input4j in version 0.11.0. This migration eliminated the need for native third-party binaries and significantly improved cross-platform support.

Background: The JInput Challenge

LITIENGINE originally used JInput, a popular Java library for game controller input. However, JInput has several significant drawbacks that led the LITIENGINE team to seek an alternative:

🔧

Native Dependencies

JInput requires native DLL/SO files (jinput.dll, libjinput.so, libjinput.dylib) to be distributed with the application, complicating deployment.

🐛

Maintenance Issues

JInput is no longer actively maintained, with the last major updates years ago, leading to compatibility issues with newer Java versions.

🔨

JNI Complexity

JInput uses traditional JNI (Java Native Interface), which requires writing C code and managing native build toolchains.

The Migration to Input4j

In LITIENGINE 0.11.0, the team replaced JInput with Input4j, their own custom input library built on the Foreign Function & Memory API (FFM API). This migration was a major milestone that:

  • Eliminated all native third-party binaries from the engine distribution
  • Improved cross-platform support for Windows, Linux, and macOS
  • Enabled the engine to leverage modern Java features
  • Simplified the build and release process
Gradle
// LITIENGINE 0.11.0+ uses Input4j automatically
dependencies {
  implementation 'de.gurkenlabs:litiengine:0.11.1'
}

When you use LITIENGINE 0.11.0 or later, Input4j is automatically included as a transitive dependency, providing seamless gamepad support without any additional configuration.

Benefits of Using Input4j

Input4j provides several advantages over traditional JNI-based libraries like JInput:

📦

No Native Artifacts

Input4j uses FFM API instead of JNI, eliminating the need for DLLs, SOs, or DYLIBs. Just add the JAR to your classpath.

🌍

Cross-Platform

Single codebase works on Windows (XInput/DirectInput), Linux (evdev), and macOS (IOKit/HID).

High Performance

Direct native calls without JNI overhead provide low-latency input handling for real-time gaming.

🔒

Type Safety

FFM API provides compile-time type checking for native function calls, reducing runtime errors.

🚀

Future-Proof

Built on standardized Java technology (FFM API in Java 22+) ensuring long-term support.

🎮

Modern API

Clean, intuitive API with both polling and event-based input handling patterns.

Code Examples

Here are examples of how Input4j provides gamepad input handling, the same functionality that LITIENGINE leverages:

Polling Input

Java
try (var inputDevices = InputDevices.init()) {
  while (gameIsRunning) {
    for (var device : inputDevices.getAll()) {
      device.poll();
      
      // Check analog sticks
      float leftX = device.getComponentValue(Axis.AXIS_X);
      float leftY = device.getComponentValue(Axis.AXIS_Y);
      
      // Check buttons
      boolean aPressed = device.getComponentValue(Button.BUTTON_0) > 0;
      
      // Use values for game input
      if (Math.abs(leftX) > 0.1f) {
        player.moveX(leftX * speed);
      }
    }
    Thread.sleep(16); // ~60 FPS polling
  }
}

Event-Based Input

Java
try (var devices = InputDevices.init()) {
  var device = devices.getAll().get(0);
  
  // Listen for button presses
  device.onButtonPressed(Button.BUTTON_0, () -> {
    System.out.println("A button pressed!");
    player.jump();
  });
  
  // Listen for axis changes
  device.onAxisChanged(Axis.AXIS_Y, value -> {
    System.out.println("Left stick Y: " + value);
  });
  
  // Handle controller disconnect
  device.onDisconnected(() -> {
    System.out.println("Controller disconnected!");
  });
  
  // Keep application running
  while (device.isConnected()) {
    Thread.sleep(100);
  }
}

XInput Support (Xbox Controllers)

Java
// Xbox controller specific mappings
device.onButtonPressed(XInput.A, () -> jump());
device.onButtonPressed(XInput.B, () -> attack());
device.onButtonPressed(XInput.X, () -> interact());
device.onButtonPressed(XInput.Y, () -> menu());

// Triggers and vibration
device.onAxisChanged(XInput.TRIGGER_LEFT, value -> {
  // Left trigger - typically used for aim/sprint
});
device.onAxisChanged(XInput.TRIGGER_RIGHT, value -> {
  // Right trigger - typically used for fire/shoot
});

// Rumble/vibration feedback
device.rumble(0.5f, 0.5f); // left motor, right motor (0.0 - 1.0)

How LITIENGINE Uses Input4j

LITIENGINE wraps Input4j with a high-level API that makes gamepad handling simple for game developers. Here's how you use gamepads in LITIENGINE:

Basic Gamepad Input

Java
// Access gamepads through LITIENGINE's Input API
GamepadManager gamepads = Input.gamepads();

// Get the currently connected gamepad
Gamepad gamepad = gamepads.current();

if (gamepad != null) {
  // Check analog stick values for movement
  float leftStickX = gamepad.getAxisValue(Gamepad.Axis.LEFT_STICK_X);
  float leftStickY = gamepad.getAxisValue(Gamepad.Axis.LEFT_STICK_Y);
  
  // Apply to entity movement
  entity.setVelocityX(leftStickX * speed);
  entity.setVelocityY(leftStickY * speed);
}

Event-Based Gamepad Input

Java
// Listen for button presses
Input.gamepads().onPressed((button, value) -> {
  if (button.equals(Gamepad.Xbox.A)) {
    player.jump();
  } else if (button.equals(Gamepad.Xbox.B)) {
    player.attack();
  } else if (button.equals(Gamepad.Xbox.X)) {
    player.interact();
  } else if (button.equals(Gamepad.Xbox.Y)) {
    openMenu();
  }
});

// Listen for button releases
Input.gamepads().onReleased((button) -> {
  if (button.equals(Gamepad.Xbox.RT)) {
    player.stopFiring();
  }
});

// Continuous polling for analog inputs
Input.gamepads().onPoll(pollValue -> {
  float leftStickY = pollValue.getValue(Gamepad.Axis.LEFT_STICK_Y);
  if (leftStickY > 0) {
    player.moveDown(leftStickY);
  } else if (leftStickY < 0) {
    player.moveUp(Math.abs(leftStickY));
  }
});

Gamepad Detection

Java
// Detect when gamepads are connected
Input.gamepads().onGamepadAdded(gamepad -> {
  System.out.println("Gamepad connected: " + gamepad.getName());
  System.out.println("Type: " + gamepad.getType());
});

// Detect when gamepads are disconnected
Input.gamepads().onGamepadRemoved(gamepad -> {
  System.out.println("Gamepad disconnected: " + gamepad.getName());
});

// Get all connected gamepads
for (Gamepad gamepad : Input.gamepads().getAll()) {
  System.out.println("Found: " + gamepad.getName());
}

Supported Controller Types

LITIENGINE automatically detects and supports various controller types through Input4j:

  • Xbox controllers - Xbox One, Series X/S (via XInput on Windows)
  • PlayStation controllers - DualShock 4, DualSense
  • Generic USB gamepads - Any HID-compliant controller
  • Joysticks - Flight sticks, racing wheels, and specialized input devices
Java
// LITIENGINE provides predefined mappings for common controllers
// Xbox controller buttons
Gamepad.Xbox.A           // A button
Gamepad.Xbox.B           // B button  
Gamepad.Xbox.X           // X button
Gamepad.Xbox.Y           // Y button
Gamepad.Xbox.LEFT_STICK_X   // Left stick X axis
Gamepad.Xbox.LEFT_STICK_Y   // Left stick Y axis
Gamepad.Xbox.RIGHT_STICK_X  // Right stick X axis
Gamepad.Xbox.RIGHT_STICK_Y  // Right stick Y axis
Gamepad.Xbox.LT           // Left trigger
Gamepad.Xbox.RT           // Right trigger

// DualShock4 controller buttons
Gamepad.DualShock4.CROSS
Gamepad.DualShock4.CIRCLE
Gamepad.DualShock4.TRIANGLE
Gamepad.DualShock4.SQUARE
Gamepad.DualShock4.OPTIONS
Gamepad.DualShock4.SHARE

Summary

The migration from JInput to Input4j represents a significant improvement for LITIENGINE and its users:

For Game Developers

Simpler deployment with no native DLLs to manage. Just add LITIENGINE as a dependency and gamepad support works out of the box.

For End Users

Better cross-platform compatibility. Plug in any gamepad and it just works on Windows, Linux, or macOS.

For the LITIENGINE Team

Easier build and release process with fewer dependencies to manage. Direct control over input handling implementation.

Input4j powers the gamepad experience in LITIENGINE.
Learn More About Input4j