Skip to content

Commit

Permalink
Use explict option for input mode
Browse files Browse the repository at this point in the history
  • Loading branch information
AlynxZhou committed Sep 13, 2021
1 parent 60fd7a5 commit c2f337b
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 30 deletions.
12 changes: 12 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ Start in fullscreen.
.B \-h, \-\-help
Print this help.

.TP
.B \-i, \-\-input\-mode mode
Select input mode for keyboard events.

Possible values are "auto", "hid" and "inject".

"auto" is default if not specified, which attemps "hid" first and will fallback to "inject" if failed.

"hid" uses Android's USB HID over AoAv2 feature to simulate physical keyboard's events, which provides better experience for IME users if supported by you device.

"inject" is the legacy scrcpy way by injecting keycode events on Android, works on most devices.

.TP
.B \-\-legacy\-paste
Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+Shift+v).
Expand Down
4 changes: 4 additions & 0 deletions app/src/aoa_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ get_usb_serial(libusb_device *device, char *buffer, int size) {
}

libusb_device *aoa_find_usb_device(const char *serial) {
if (!serial) {
return NULL;
}

libusb_device **list;
libusb_device *result = NULL;
ssize_t count = libusb_get_device_list(NULL, &list);
Expand Down
36 changes: 35 additions & 1 deletion app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ scrcpy_print_usage(const char *arg0) {
" -h, --help\n"
" Print this help.\n"
"\n"
" -i, --input-mode mode\n"
" Select input mode for keyboard events.\n"
" Possible values are \"auto\", \"hid\" and \"inject\".\n"
" \"auto\" is default if not specified, which attemps \"hid\"\n"
" first and will fallback to \"inject\" if failed.\n"
" \"hid\" uses Android's USB HID over AoAv2 feature to\n"
" simulate physical keyboard's events, which provides better\n"
" experience for IME users if supported by you device.\n"
" \"inject\" is the legacy scrcpy way by injecting keycode\n"
" events on Android, works on most devices.\n"
"\n"
" --legacy-paste\n"
" Inject computer clipboard text as a sequence of key events\n"
" on Ctrl+v (like MOD+Shift+v).\n"
Expand Down Expand Up @@ -673,6 +684,23 @@ parse_record_format(const char *optarg, enum sc_record_format *format) {
return false;
}

static bool
parse_input_mode(const char *optarg, enum sc_input_mode *input_mode) {
if (!strcmp(optarg, "auto")) {
*input_mode = SC_INPUT_MODE_AUTO;
return true;
} else if (!strcmp(optarg, "hid")) {
*input_mode = SC_INPUT_MODE_HID;
return true;
} else if (!strcmp(optarg, "inject")) {
*input_mode = SC_INPUT_MODE_INJECT;
return true;
}
LOGE("Unsupported input mode: %s (expected auto, hid or inject)", optarg);
return false;
}


static enum sc_record_format
guess_record_format(const char *filename) {
size_t len = strlen(filename);
Expand Down Expand Up @@ -738,6 +766,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
OPT_FORWARD_ALL_CLICKS},
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"input-mode", required_argument, NULL, 'i'},
{"legacy-paste", no_argument, NULL, OPT_LEGACY_PASTE},
{"lock-video-orientation", optional_argument, NULL,
OPT_LOCK_VIDEO_ORIENTATION},
Expand Down Expand Up @@ -784,7 +813,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
optind = 0; // reset to start from the first argument in tests

int c;
while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTvV:w",
while ((c = getopt_long(argc, argv, "b:c:fF:hi:m:nNp:r:s:StTvV:w",
long_options, NULL)) != -1) {
switch (c) {
case 'b':
Expand Down Expand Up @@ -817,6 +846,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case 'h':
args->help = true;
break;
case 'i':
if (!parse_input_mode(optarg, &opts->input_mode)) {
return false;
}
break;
case OPT_MAX_FPS:
if (!parse_max_fps(optarg, &opts->max_fps)) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ input_manager_init(struct input_manager *im, struct controller *controller,
im->screen = screen;
im->repeat = 0;

im->usb_handle = NULL;
im->use_hid_over_aoa = false;
im->usb_handle = NULL;

im->control = options->control;
im->forward_key_repeat = options->forward_key_repeat;
Expand Down
2 changes: 1 addition & 1 deletion app/src/input_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ struct input_manager {
struct controller *controller;
struct screen *screen;

libusb_device_handle *usb_handle;
bool use_hid_over_aoa;
libusb_device_handle *usb_handle;

// SDL reports repeated events as a boolean, but Android expects the actual
// number of repetitions. This variable keeps track of the count.
Expand Down
78 changes: 51 additions & 27 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -416,78 +416,102 @@ scrcpy(const struct scrcpy_options *options) {

input_manager_init(&s->input_manager, &s->controller, &s->screen, options);

bool use_hid_over_aoa = false;
libusb_device_handle *usb_handle = NULL;
libusb_device *usb_device = NULL;

if (options->serial) {
LOGD("USB serial provided, starting in HID over AoAv2 mode");
s->input_manager.use_hid_over_aoa = true;
if (options->input_mode != SC_INPUT_MODE_INJECT) {
LOGD("Starting in HID over AoAv2 mode");
use_hid_over_aoa = true;
} else {
LOGD("Starting in inject mode because of --input-mode=inject");
use_hid_over_aoa = false;
}

if (use_hid_over_aoa) {
libusb_init(NULL);
usb_device = aoa_find_usb_device(options->serial);
if (!usb_device) {
LOGW("USB device of serial %s not found", options->serial);
LOGW("Fallback to inject mode");
s->input_manager.use_hid_over_aoa = false;
if (options->input_mode == SC_INPUT_MODE_AUTO) {
LOGW("Fallback to inject mode");
use_hid_over_aoa = false;
} else {
LOGE("Remove --input-mode=hid from parameters to allow input mode fallback");
goto end;
}
}
}

if (s->input_manager.use_hid_over_aoa &&
aoa_open_usb_handle(usb_device, &usb_handle) < 0) {
if (use_hid_over_aoa && aoa_open_usb_handle(usb_device, &usb_handle) < 0) {
LOGW("Open USB handle failed");
LOGW("Fallback to inject mode");
libusb_unref_device(usb_device);
s->input_manager.use_hid_over_aoa = false;
if (options->input_mode == SC_INPUT_MODE_AUTO) {
LOGW("Fallback to inject mode");
use_hid_over_aoa = false;
} else {
LOGE("Remove --input-mode=hid from parameters to allow input mode fallback");
goto end;
}
}

if (s->input_manager.use_hid_over_aoa &&
aoa_register_hid(usb_handle, REPORT_DESC.size) < 0) {
if (use_hid_over_aoa && aoa_register_hid(usb_handle, REPORT_DESC.size) < 0) {
LOGW("Register HID failed");
LOGW("Fallback to inject mode");
libusb_close(usb_handle);
libusb_unref_device(usb_device);
s->input_manager.use_hid_over_aoa = false;
if (options->input_mode == SC_INPUT_MODE_AUTO) {
LOGW("Fallback to inject mode");
use_hid_over_aoa = false;
} else {
LOGE("Remove --input-mode=hid from parameters to allow input mode fallback");
goto end;
}
}

struct libusb_device_descriptor desc;
// Make sure usb_device is found.
if (s->input_manager.use_hid_over_aoa) {
if (use_hid_over_aoa) {
libusb_get_device_descriptor(usb_device, &desc);
}

if (s->input_manager.use_hid_over_aoa &&
aoa_set_hid_report_desc(usb_handle, &REPORT_DESC,
desc.bMaxPacketSize0) < 0) {
if (use_hid_over_aoa && aoa_set_hid_report_desc(usb_handle, &REPORT_DESC,
desc.bMaxPacketSize0) < 0) {
LOGW("Set HID report desc failed");
LOGW("Fallback to inject mode");
libusb_close(usb_handle);
libusb_unref_device(usb_device);
s->input_manager.use_hid_over_aoa = false;
if (options->input_mode == SC_INPUT_MODE_AUTO) {
LOGW("Fallback to inject mode");
use_hid_over_aoa = false;
} else {
LOGE("Remove --input-mode=hid from parameters to allow input mode fallback");
goto end;
}
}

// Finally successfully set up HID over AoAv2.
if (s->input_manager.use_hid_over_aoa) {
if (use_hid_over_aoa) {
LOGD("Successfully set up HID over AoAv2 mode");
s->input_manager.use_hid_over_aoa = use_hid_over_aoa;
s->input_manager.usb_handle = usb_handle;
// Events sent too early after setting the HID descriptor
// may leads into error.
usleep(1000000);
}

// Events sent too early after setting the HID descriptor
// may leads into error.
usleep(1000000);

ret = event_loop(s, options);
LOGD("quit...");

// Close the window immediately on closing, because screen_destroy() may
// only be called once the stream thread is joined (it may take time)
screen_hide_window(&s->screen);

end:
// Close HID over AoAv2 so the soft keyboard shows again on Android.
if (s->input_manager.use_hid_over_aoa) {
if (use_hid_over_aoa) {
aoa_unregister_hid(usb_handle);
libusb_close(usb_handle);
libusb_unref_device(usb_device);
}

end:
// The stream is not stopped explicitly, because it will stop by itself on
// end-of-stream
if (controller_started) {
Expand Down
8 changes: 8 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ enum sc_shortcut_mod {
SC_MOD_RSUPER = 1 << 5,
};

enum sc_input_mode {
SC_INPUT_MODE_AUTO,
SC_INPUT_MODE_HID,
SC_INPUT_MODE_INJECT
};

struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
Expand All @@ -68,6 +74,7 @@ struct scrcpy_options {
const char *v4l2_device;
enum sc_log_level log_level;
enum sc_record_format record_format;
enum sc_input_mode input_mode;
struct sc_port_range port_range;
struct sc_shortcut_mods shortcut_mods;
uint16_t max_size;
Expand Down Expand Up @@ -112,6 +119,7 @@ struct scrcpy_options {
.v4l2_device = NULL, \
.log_level = SC_LOG_LEVEL_INFO, \
.record_format = SC_RECORD_FORMAT_AUTO, \
.input_mode = SC_INPUT_MODE_AUTO, \
.port_range = { \
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST, \
.last = DEFAULT_LOCAL_PORT_RANGE_LAST, \
Expand Down

0 comments on commit c2f337b

Please sign in to comment.