How external controllers work
Winlator uses Android’s standardInputDevice API to discover gamepads. Any device that exposes the SOURCE_GAMEPAD or SOURCE_JOYSTICK source flags is treated as a compatible controller — this covers most Bluetooth and USB HID gamepads including Xbox, PlayStation (via third-party drivers), and generic Android controllers.
Each controller is identified by its descriptor string (a stable hardware ID provided by Android), not by a transient device ID. This means bindings you configure survive reconnects and reboots.
Key components
ExternalController
ExternalController represents a single connected physical gamepad. It stores:
- id — the Android
InputDevicedescriptor (stable hardware identifier) - name — the human-readable device name shown in the UI
- controllerBindings — the list of
ExternalControllerBindingentries configured for this controller - state — a
GamepadStatesnapshot updated in real time from motion and key events
getControllers(), getController(id)) that enumerate all currently connected game controllers from the Android device list.
ExternalControllerBinding
AnExternalControllerBinding maps one physical input — a button key code or an analog axis direction — to a Binding target (a Windows input event). Each binding has two fields:
Analog axes are split by direction so you can bind, for example, left-stick-up to
W and left-stick-down to S:
Binding types
TheBinding enum defines every possible Windows input event that can be targeted. Bindings fall into three categories:
- Keyboard
- Mouse
- Gamepad
Full keyboard coverage including:
- Arrow keys (Up, Down, Left, Right)
- Letter keys A–Z and number keys 0–9
- Function keys F1–F12
- Numpad keys NP0–NP9
- Modifier keys: L/R Shift, L/R Ctrl, L/R Alt
- Special keys: Enter, Escape, Backspace, Delete, Tab, Space, Home, Page Up, Page Down, Print Screen, Caps Lock, Num Lock
- Punctuation:
[,],\,/,;,,,.,',-,+
GamepadState
GamepadState is a lightweight snapshot of a controller’s current state, updated from Android motion and key events:
The state is serialized into a compact binary format (
writeTo) and forwarded to Wine’s virtual gamepad driver. Diagonal D-pad presses are encoded as a POV hat value (0–7) following standard HID conventions.
Setting up an external controller
1
Connect your gamepad
Pair your controller via Android’s Bluetooth settings, or plug it in via USB. Verify Android recognizes it as a gamepad — the controller should appear under Settings → Connected devices.
2
Open a controls profile
In Winlator, navigate to Input Controls and select (or create) the profile you want to associate with your controller. Controller bindings are stored per-profile.
3
Tap the controller in the list
Any connected gamepad is shown in the External Controllers section of the Input Controls screen. Tap your controller’s name to open the bindings configuration screen (
ExternalControllerBindingsActivity).4
Record button presses
Press any button or move any axis on your physical controller. Winlator detects the input automatically and adds a new binding entry for it. Each entry shows the physical input name (e.g.
BUTTON A, AXIS X+) and a pair of dropdowns to set the binding type (Keyboard, Mouse, or Gamepad) and the target binding.5
Assign target bindings
For each recorded input, use the Binding Type dropdown to choose the category, then select the specific key or action from the Binding dropdown. Changes are saved to the profile immediately.
6
Remove unwanted entries
Tap the remove button next to any binding you no longer need. The controller entry is removed from the profile entirely if all its bindings are deleted.
Controller bindings are stored inside the controls profile (
.icp file), not globally. If you assign the same physical controller to multiple profiles, each profile can have different bindings for it.Supported button indices
The following physical buttons are recognized by Winlator’sExternalController:
D-pad inputs are handled separately through
KEYCODE_DPAD_UP/DOWN/LEFT/RIGHT or via AXIS_HAT_X/AXIS_HAT_Y motion events.
Controller compatibility
Winlator relies entirely on Android’s standard gamepad APIs. A controller works if:- Android classifies it as
SOURCE_GAMEPADorSOURCE_JOYSTICK - The controller is not a virtual device
