mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-12-18 14:04:20 +01:00
Add UHID gamepad support
Similar to UHID keyboard and mouse, but for gamepads. Can be enabled with --gamepad=uhid or -G. It is not enabled by default because not all devices support UHID (there is a permission error on old Android versions). PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
This commit is contained in:
@@ -373,13 +373,19 @@ static const struct sc_option options[] = {
|
||||
.longopt_id = OPT_FORWARD_ALL_CLICKS,
|
||||
.longopt = "forward-all-clicks",
|
||||
},
|
||||
{
|
||||
.shortopt = 'G',
|
||||
.text = "Same as --gamepad=uhid.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_GAMEPAD,
|
||||
.longopt = "gamepad",
|
||||
.argdesc = "mode",
|
||||
.text = "Select how to send gamepad inputs to the device.\n"
|
||||
"Possible values are \"disabled\" and \"aoa\".\n"
|
||||
"Possible values are \"disabled\", \"uhid\" and \"aoa\".\n"
|
||||
"\"disabled\" does not send gamepad inputs to the device.\n"
|
||||
"\"uhid\" simulates physical HID gamepads using the Linux UHID "
|
||||
"kernel module on the device.\n"
|
||||
"\"aoa\" simulates physical gamepads using the AOAv2 protocol."
|
||||
"It may only work over USB.\n"
|
||||
"Also see --keyboard and --mouse.",
|
||||
@@ -2065,6 +2071,11 @@ parse_gamepad(const char *optarg, enum sc_gamepad_input_mode *mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(optarg, "uhid")) {
|
||||
*mode = SC_GAMEPAD_INPUT_MODE_UHID;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(optarg, "aoa")) {
|
||||
#ifdef HAVE_USB
|
||||
*mode = SC_GAMEPAD_INPUT_MODE_AOA;
|
||||
@@ -2645,6 +2656,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
case OPT_AUDIO_DUP:
|
||||
opts->audio_dup = true;
|
||||
break;
|
||||
case 'G':
|
||||
opts->gamepad_input_mode = SC_GAMEPAD_INPUT_MODE_UHID;
|
||||
break;
|
||||
case OPT_GAMEPAD:
|
||||
if (!parse_gamepad(optarg, &opts->gamepad_input_mode)) {
|
||||
return false;
|
||||
|
||||
@@ -158,6 +158,7 @@ enum sc_mouse_input_mode {
|
||||
|
||||
enum sc_gamepad_input_mode {
|
||||
SC_GAMEPAD_INPUT_MODE_DISABLED,
|
||||
SC_GAMEPAD_INPUT_MODE_UHID,
|
||||
SC_GAMEPAD_INPUT_MODE_AOA,
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "recorder.h"
|
||||
#include "screen.h"
|
||||
#include "server.h"
|
||||
#include "uhid/gamepad_uhid.h"
|
||||
#include "uhid/keyboard_uhid.h"
|
||||
#include "uhid/mouse_uhid.h"
|
||||
#ifdef HAVE_USB
|
||||
@@ -80,9 +81,12 @@ struct scrcpy {
|
||||
struct sc_mouse_aoa mouse_aoa;
|
||||
#endif
|
||||
};
|
||||
union {
|
||||
struct sc_gamepad_uhid gamepad_uhid;
|
||||
#ifdef HAVE_USB
|
||||
struct sc_gamepad_aoa gamepad_aoa;
|
||||
struct sc_gamepad_aoa gamepad_aoa;
|
||||
#endif
|
||||
};
|
||||
struct sc_timeout timeout;
|
||||
};
|
||||
|
||||
@@ -750,6 +754,11 @@ aoa_complete:
|
||||
mp = &s->mouse_uhid.mouse_processor;
|
||||
}
|
||||
|
||||
if (options->gamepad_input_mode == SC_GAMEPAD_INPUT_MODE_UHID) {
|
||||
sc_gamepad_uhid_init(&s->gamepad_uhid, &s->controller);
|
||||
gp = &s->gamepad_uhid.gamepad_processor;
|
||||
}
|
||||
|
||||
sc_controller_configure(&s->controller, acksync, uhid_devices);
|
||||
|
||||
if (!sc_controller_start(&s->controller)) {
|
||||
|
||||
122
app/src/uhid/gamepad_uhid.c
Normal file
122
app/src/uhid/gamepad_uhid.c
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "gamepad_uhid.h"
|
||||
|
||||
#include "hid/hid_gamepad.h"
|
||||
#include "input_events.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast gamepad processor to sc_gamepad_uhid */
|
||||
#define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor)
|
||||
|
||||
static void
|
||||
sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad,
|
||||
const struct sc_hid_input *hid_input,
|
||||
const char *name) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_UHID_INPUT;
|
||||
msg.uhid_input.id = hid_input->hid_id;
|
||||
|
||||
assert(hid_input->size <= SC_HID_MAX_SIZE);
|
||||
memcpy(msg.uhid_input.data, hid_input->data, hid_input->size);
|
||||
msg.uhid_input.size = hid_input->size;
|
||||
|
||||
if (!sc_controller_push_msg(gamepad->controller, &msg)) {
|
||||
LOGE("Could not push UHID_INPUT message (%s)", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_gamepad_uhid_send_open(struct sc_gamepad_uhid *gamepad,
|
||||
const struct sc_hid_open *hid_open) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
|
||||
msg.uhid_create.id = hid_open->hid_id;
|
||||
msg.uhid_create.report_desc = hid_open->report_desc;
|
||||
msg.uhid_create.report_desc_size = hid_open->report_desc_size;
|
||||
|
||||
if (!sc_controller_push_msg(gamepad->controller, &msg)) {
|
||||
LOGE("Could not push UHID_CREATE message (gamepad)");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_gamepad_uhid_send_close(struct sc_gamepad_uhid *gamepad,
|
||||
const struct sc_hid_close *hid_close) {
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_UHID_DESTROY;
|
||||
msg.uhid_create.id = hid_close->hid_id;
|
||||
|
||||
if (!sc_controller_push_msg(gamepad->controller, &msg)) {
|
||||
LOGE("Could not push UHID_DESTROY message (gamepad)");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp,
|
||||
const struct sc_gamepad_device_event *event) {
|
||||
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
|
||||
|
||||
if (event->type == SC_GAMEPAD_DEVICE_ADDED) {
|
||||
struct sc_hid_open hid_open;
|
||||
if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open,
|
||||
event->gamepad_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_gamepad_uhid_send_open(gamepad, &hid_open);
|
||||
} else {
|
||||
assert(event->type == SC_GAMEPAD_DEVICE_REMOVED);
|
||||
|
||||
struct sc_hid_close hid_close;
|
||||
if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close,
|
||||
event->gamepad_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_gamepad_uhid_send_close(gamepad, &hid_close);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_gamepad_processor_process_gamepad_axis(struct sc_gamepad_processor *gp,
|
||||
const struct sc_gamepad_axis_event *event) {
|
||||
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
|
||||
|
||||
struct sc_hid_input hid_input;
|
||||
if (!sc_hid_gamepad_generate_input_from_axis(&gamepad->hid, &hid_input,
|
||||
event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_gamepad_uhid_send_input(gamepad, &hid_input, "gamepad axis");
|
||||
}
|
||||
|
||||
static void
|
||||
sc_gamepad_processor_process_gamepad_button(struct sc_gamepad_processor *gp,
|
||||
const struct sc_gamepad_button_event *event) {
|
||||
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
|
||||
|
||||
struct sc_hid_input hid_input;
|
||||
if (!sc_hid_gamepad_generate_input_from_button(&gamepad->hid, &hid_input,
|
||||
event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_gamepad_uhid_send_input(gamepad, &hid_input, "gamepad button");
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
sc_gamepad_uhid_init(struct sc_gamepad_uhid *gamepad,
|
||||
struct sc_controller *controller) {
|
||||
sc_hid_gamepad_init(&gamepad->hid);
|
||||
|
||||
gamepad->controller = controller;
|
||||
|
||||
static const struct sc_gamepad_processor_ops ops = {
|
||||
.process_gamepad_device = sc_gamepad_processor_process_gamepad_device,
|
||||
.process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis,
|
||||
.process_gamepad_button = sc_gamepad_processor_process_gamepad_button,
|
||||
};
|
||||
|
||||
gamepad->gamepad_processor.ops = &ops;
|
||||
}
|
||||
23
app/src/uhid/gamepad_uhid.h
Normal file
23
app/src/uhid/gamepad_uhid.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef SC_GAMEPAD_UHID_H
|
||||
#define SC_GAMEPAD_UHID_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "hid/hid_gamepad.h"
|
||||
#include "trait/gamepad_processor.h"
|
||||
|
||||
struct sc_gamepad_uhid {
|
||||
struct sc_gamepad_processor gamepad_processor; // gamepad processor trait
|
||||
|
||||
struct sc_hid_gamepad hid;
|
||||
struct sc_controller *controller;
|
||||
};
|
||||
|
||||
void
|
||||
sc_gamepad_uhid_init(struct sc_gamepad_uhid *mouse,
|
||||
struct sc_controller *controller);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user