How scrcpy Maps Gamepad Input to Android: UHID vs AOA Architecture Explained

scrcpy forwards gamepad input from your PC to an Android device by translating SDL2 events into HID reports using either UHID (Linux kernel virtual device) or AOA (Android Open Accessory protocol) backends, making the host controller appear as a physical gamepad on the Android side.

The Genymobile/scrcpy project implements gamepad forwarding through a modular architecture that supports two distinct transport mechanisms. When you launch scrcpy with the --gamepad (-G) flag, the application captures local gamepad events and routes them through either the UHID or AOA processor, depending on your host operating system and connection method.

scrcpy Gamepad Input Architecture Overview

SDL2 Event Capture

scrcpy relies on SDL2 to discover and monitor gamepads connected to the host PC. The app/src/input_manager.c file contains the event handling logic that intercepts device additions, axis movements, and button presses.

When SDL generates a gamepad event, sc_input_manager_process_gamepad_* functions dispatch the event to the active processor:

// input_manager.c – routing SDL events to the gamepad processor
im->gp->ops->process_gamepad_added(im->gp, &evt);   // device connected
im->gp->ops->process_gamepad_axis(im->gp, &evt);     // analog stick/trigger
im->gp->ops->process_gamepad_button(im->gp, &evt);    // button press/release

The Gamepad Processor Trait

Both UHID and AOA implementations conform to the sc_gamepad_processor trait defined in app/src/trait/gamepad_processor.h. This interface standardizes four callbacks:

  • process_gamepad_added
  • process_gamepad_removed
  • process_gamepad_axis
  • process_gamepad_button

This trait-based design allows scrcpy to swap between UHID and AOA backends at runtime based on the --gamepad command-line argument.

UHID Backend: Linux Kernel Virtual Gamepad

The UHID (--gamepad=uhid) mode creates a virtual HID device on Linux hosts using the kernel's UHID interface. This method requires the uhid kernel module but provides the most transparent gamepad input mapping to Android.

In app/src/uhid/gamepad_uhid.c, the implementation translates SDL events into standard HID gamepad reports:

// gamepad_uhid.c – generating HID reports from button events
sc_hid_gamepad_generate_input_from_button(&gamepad->hid, &hid_input, event);
uhid_output_write(&gamepad->uhid, &hid_input);

The uhid_output_write function writes the report to the virtual device, which then appears to Android as a physical Bluetooth or USB HID gamepad. This approach works over both USB and Wi-Fi connections because the HID device is created on the host side and forwarded through scrcpy's standard transport.

AOA Backend: Android Open Accessory Protocol

The AOA (--gamepad=aoa) mode uses the Android Open Accessory protocol to send HID reports directly over the USB accessory channel. This backend is implemented in app/src/usb/gamepad_aoa.c and works on any host OS that supports AOA, though it requires a USB connection (it does not work over Wi-Fi).

The AOA processor builds HID input reports and pushes them through the AOA channel:

// gamepad_aoa.c – pushing axis events over AOA
sc_hid_gamepad_generate_input_from_axis(&gamepad->hid, &hid_input, event);
sc_aoa_push_input(gamepad->aoa, &hid_input);

In OTG mode (--otg), scrcpy uses app/src/usb/screen_otg.c to route SDL events to the AOA gamepad processor. The screen_otg.c file acts as the bridge between the input manager and the specific backend, calling gp->ops->process_gamepad_axis(gp, &evt) to forward events.

Enabling and Configuring Gamepad Support in scrcpy

Activate gamepad forwarding using the --gamepad or -G flag. You can explicitly select the backend or let scrcpy choose based on your platform.

Command Backend Requirements Use Case
scrcpy --gamepad=uhid UHID Linux host, uhid kernel module Best compatibility, works over Wi-Fi
scrcpy --gamepad=aoa AOA USB connection Cross-platform, requires USB cable
scrcpy --otg --gamepad=aoa AOA USB-OTG adapter Direct accessory mode, no ADB needed
scrcpy --no-video --no-audio --gamepad=uhid UHID Linux host Input-only mode for remote gaming

When using UHID mode, ensure your Linux kernel has the uhid module loaded. AOA mode automatically negotiates the accessory protocol when the device connects, but requires the Android device to support AOA (most modern devices do).

Summary

  • scrcpy implements gamepad input mapping through two distinct backends: UHID (Linux kernel virtual device) and AOA (Android Open Accessory protocol).
  • The UHID backend in app/src/uhid/gamepad_uhid.c creates a virtual HID device that appears as a physical gamepad to Android, supporting both USB and Wi-Fi connections.
  • The AOA backend in app/src/usb/gamepad_aoa.c sends HID reports directly over the USB accessory channel, working cross-platform but requiring a physical USB connection.
  • SDL2 events are captured in app/src/input_manager.c and routed through the sc_gamepad_processor trait interface to the active backend.
  • Enable the feature with --gamepad=uhid or --gamepad=aoa depending on your host OS and connection type.

Frequently Asked Questions

Does scrcpy support Xbox and PlayStation controllers?

Yes. scrcpy uses SDL2 for gamepad discovery, which supports standard USB and Bluetooth controllers including Xbox, PlayStation, and Nintendo Switch Pro controllers. The SDL events are normalized and translated to standard HID gamepad reports in app/src/uhid/gamepad_uhid.c or app/src/usb/gamepad_aoa.c, ensuring compatibility with Android's standard gamepad input stack regardless of the host controller brand.

What is the difference between UHID and AOA gamepad modes?

UHID (--gamepad=uhid) creates a virtual HID device on Linux hosts using the kernel's UHID interface. The device appears to Android as a physical Bluetooth or USB gamepad and works over both USB and Wi-Fi connections. AOA (--gamepad=aoa) uses the Android Open Accessory protocol to push HID reports directly through the USB accessory channel. AOA works on any host OS but requires a USB cable and does not function over Wi-Fi. Choose UHID for flexibility on Linux; use AOA for cross-platform USB-only connections.

Can I use scrcpy gamepad forwarding over Wi-Fi?

Yes, but only when using the UHID backend. The UHID implementation in app/src/uhid/gamepad_uhid.c creates the virtual HID device on the Linux host, and scrcpy's standard ADB transport forwards the input events to the Android device. This works over TCP/IP (Wi-Fi) because the HID device is emulated on the host side. The AOA backend requires a direct USB connection to negotiate the Android Open Accessory protocol and cannot function over Wi-Fi.

How do I troubleshoot gamepad input lag in scrcpy?

First, verify you are using the appropriate backend for your connection type. For USB connections, ensure you are using --gamepad=uhid on Linux or --gamepad=aoa on other platforms. Over Wi-Fi, only UHID mode is available. Check that your controller is properly recognized by SDL2 by running scrcpy --gamepad=uhid --verbosity=debug and monitoring for sc_input_manager_process_gamepad_* events in the logs. If using UHID, confirm the uhid kernel module is loaded with lsmod | grep uhid. For AOA lag, ensure your USB cable supports data transfer (not charge-only) and try a direct USB 3.0 port rather than a hub.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →