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_addedprocess_gamepad_removedprocess_gamepad_axisprocess_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.ccreates 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.csends 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.cand routed through thesc_gamepad_processortrait interface to the active backend. - Enable the feature with
--gamepad=uhidor--gamepad=aoadepending 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →