Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OTG support for Windows #3011

Merged
merged 10 commits into from
Feb 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ install the required packages:
```bash
# runtime dependencies
pacman -S mingw-w64-x86_64-SDL2 \
mingw-w64-x86_64-ffmpeg
mingw-w64-x86_64-ffmpeg \
mingw-w64-x86_64-libusb

# client build dependencies
pacman -S mingw-w64-x86_64-make \
Expand All @@ -175,7 +176,8 @@ For a 32 bits version, replace `x86_64` by `i686`:
```bash
# runtime dependencies
pacman -S mingw-w64-i686-SDL2 \
mingw-w64-i686-ffmpeg
mingw-w64-i686-ffmpeg \
mingw-w64-i686-libusb

# client build dependencies
pacman -S mingw-w64-i686-make \
Expand Down
15 changes: 14 additions & 1 deletion app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ if v4l2_support
src += [ 'src/v4l2_sink.c' ]
endif

usb_support = get_option('usb') and host_machine.system() != 'windows'
usb_support = get_option('usb')
if usb_support
src += [
'src/usb/aoa_hid.c',
Expand Down Expand Up @@ -141,9 +141,22 @@ else
include_directories: include_directories(ffmpeg_include_dir)
)

prebuilt_libusb = meson.get_cross_property('prebuilt_libusb')
prebuilt_libusb_root = meson.get_cross_property('prebuilt_libusb_root')
libusb_bin_dir = meson.current_source_dir() + '/prebuilt-deps/data/' + prebuilt_libusb + '/dll'
libusb_include_dir = 'prebuilt-deps/data/' + prebuilt_libusb_root + '/include'

libusb = declare_dependency(
dependencies: [
cc.find_library('libusb-1.0', dirs: libusb_bin_dir),
],
include_directories: include_directories(libusb_include_dir)
)

dependencies = [
ffmpeg,
sdl2,
libusb,
cc.find_library('mingw32')
]

Expand Down
28 changes: 28 additions & 0 deletions app/prebuilt-deps/prepare-libusb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e
DIR=$(dirname ${BASH_SOURCE[0]})
cd "$DIR"
. common
mkdir -p "$PREBUILT_DATA_DIR"
cd "$PREBUILT_DATA_DIR"

DEP_DIR=libusb-1.0.25

FILENAME=libusb-1.0.25.7z
SHA256SUM=3d1c98416f454026034b2b5d67f8a294053898cb70a8b489874e75b136c6674d

if [[ -d "$DEP_DIR" ]]
then
echo "$DEP_DIR" found
exit 0
fi

get_file "https://github.com/libusb/libusb/releases/download/v1.0.25/$FILENAME" "$FILENAME" "$SHA256SUM"

mkdir "$DEP_DIR"
cd "$DEP_DIR"

7z x "../$FILENAME" \
MinGW32/dll/libusb-1.0.dll \
MinGW64/dll/libusb-1.0.dll \
include /
14 changes: 12 additions & 2 deletions app/src/adb/adb.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,17 @@ process_check_success_internal(sc_pid pid, const char *name, bool close,
static bool
process_check_success_intr(struct sc_intr *intr, sc_pid pid, const char *name,
unsigned flags) {
if (!sc_intr_set_process(intr, pid)) {
if (intr && !sc_intr_set_process(intr, pid)) {
// Already interrupted
return false;
}

// Always pass close=false, interrupting would be racy otherwise
bool ret = process_check_success_internal(pid, name, false, flags);

sc_intr_set_process(intr, SC_PROCESS_NONE);
if (intr) {
sc_intr_set_process(intr, SC_PROCESS_NONE);
}

// Close separately
sc_process_close(pid);
Expand Down Expand Up @@ -202,6 +204,14 @@ sc_adb_start_server(struct sc_intr *intr, unsigned flags) {
return process_check_success_intr(intr, pid, "adb start-server", flags);
}

bool
sc_adb_kill_server(struct sc_intr *intr, unsigned flags) {
const char *const argv[] = SC_ADB_COMMAND("kill-server");

sc_pid pid = sc_adb_execute(argv, flags);
return process_check_success_intr(intr, pid, "adb kill-server", flags);
}

bool
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
const char *device_socket_name, unsigned flags) {
Expand Down
3 changes: 3 additions & 0 deletions app/src/adb/adb.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ sc_adb_execute(const char *const argv[], unsigned flags);
bool
sc_adb_start_server(struct sc_intr *intr, unsigned flags);

bool
sc_adb_kill_server(struct sc_intr *intr, unsigned flags);

bool
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
const char *device_socket_name, unsigned flags);
Expand Down
21 changes: 15 additions & 6 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -1370,8 +1370,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
break;
#else
LOGE("HID over AOA (-K/--hid-keyboard) is disabled (or "
"unsupported on this platform).");
LOGE("HID over AOA (-K/--hid-keyboard) is disabled.");
return false;
#endif
case OPT_MAX_FPS:
Expand All @@ -1389,8 +1388,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
break;
#else
LOGE("HID over AOA (-M/--hid-mouse) is disabled (or "
"unsupported on this platform).");
LOGE("HID over AOA (-M/--hid-mouse) is disabled.");
return false;
#endif
case OPT_LOCK_VIDEO_ORIENTATION:
Expand Down Expand Up @@ -1559,8 +1557,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->otg = true;
break;
#else
LOGE("OTG mode (--otg) is disabled (or unsupported on this "
"platform).");
LOGE("OTG mode (--otg) is disabled.");
return false;
#endif
case OPT_V4L2_SINK:
Expand Down Expand Up @@ -1683,6 +1680,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}

#ifdef HAVE_USB

# ifdef _WIN32
if (!opts->otg && (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID
|| opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID)) {
LOGE("On Windows, it is not possible to open a USB device already open "
"by another process (like adb).");
LOGE("Therefore, -K/--hid-keyboard and -M/--hid-mouse may only work in "
"OTG mode (--otg).");
return false;
}
# endif

if (opts->otg) {
// OTG mode is compatible with only very few options.
// Only report obvious errors.
Expand Down
4 changes: 4 additions & 0 deletions app/src/usb/aoa_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ sc_aoa_register_hid(struct sc_aoa *aoa, uint16_t accessory_id,
DEFAULT_TIMEOUT);
if (result < 0) {
LOGE("REGISTER_HID: libusb error: %s", libusb_strerror(result));
sc_usb_check_disconnected(aoa->usb, result);
return false;
}

Expand Down Expand Up @@ -131,6 +132,7 @@ sc_aoa_set_hid_report_desc(struct sc_aoa *aoa, uint16_t accessory_id,
DEFAULT_TIMEOUT);
if (result < 0) {
LOGE("SET_HID_REPORT_DESC: libusb error: %s", libusb_strerror(result));
sc_usb_check_disconnected(aoa->usb, result);
return false;
}

Expand Down Expand Up @@ -173,6 +175,7 @@ sc_aoa_send_hid_event(struct sc_aoa *aoa, const struct sc_hid_event *event) {
DEFAULT_TIMEOUT);
if (result < 0) {
LOGE("SEND_HID_EVENT: libusb error: %s", libusb_strerror(result));
sc_usb_check_disconnected(aoa->usb, result);
return false;
}

Expand All @@ -195,6 +198,7 @@ sc_aoa_unregister_hid(struct sc_aoa *aoa, const uint16_t accessory_id) {
DEFAULT_TIMEOUT);
if (result < 0) {
LOGE("UNREGISTER_HID: libusb error: %s", libusb_strerror(result));
sc_usb_check_disconnected(aoa->usb, result);
return false;
}

Expand Down
14 changes: 12 additions & 2 deletions app/src/usb/scrcpy_otg.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <SDL2/SDL.h>

#include "adb/adb.h"
#include "events.h"
#include "screen_otg.h"
#include "util/log.h"
Expand Down Expand Up @@ -75,6 +76,15 @@ scrcpy_otg(struct scrcpy_options *options) {
bool aoa_started = false;
bool aoa_initialized = false;

#ifdef _WIN32
// On Windows, only one process could open a USB device
// <https://github.com/Genymobile/scrcpy/issues/2773>
LOGI("Killing adb daemon (if any)...");
unsigned flags = SC_ADB_NO_STDOUT | SC_ADB_NO_STDERR | SC_ADB_NO_LOGERR;
// uninterruptible (intr == NULL), but in practice it's very quick
sc_adb_kill_server(NULL, flags);
#endif

static const struct sc_usb_callbacks cbs = {
.on_disconnected = sc_usb_on_disconnected,
};
Expand All @@ -91,8 +101,8 @@ scrcpy_otg(struct scrcpy_options *options) {

usb_device_initialized = true;

LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
usb_device.serial, usb_device.vid, usb_device.pid,
LOGI("USB device: %s (%04x:%04x) %s %s", usb_device.serial,
(unsigned) usb_device.vid, (unsigned) usb_device.pid,
usb_device.manufacturer, usb_device.product);

ok = sc_usb_connect(&s->usb, usb_device.device, &cbs, NULL);
Expand Down
37 changes: 28 additions & 9 deletions app/src/usb/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ sc_usb_read_device(libusb_device *device, struct sc_usb_device *out) {
if (result < 0) {
// Log at debug level because it is expected that some non-Android USB
// devices present on the computer require special permissions
LOGD("Open USB device %04" PRIx16 ":%04" PRIx16 ": libusb error: %s",
desc.idVendor, desc.idProduct, libusb_strerror(result));
LOGD("Open USB device %04x:%04x: libusb error: %s",
(unsigned) desc.idVendor, (unsigned) desc.idProduct,
libusb_strerror(result));
return false;
}

Expand Down Expand Up @@ -146,8 +147,10 @@ sc_usb_devices_log(enum sc_log_level level, struct sc_usb_device *devices,
for (size_t i = 0; i < count; ++i) {
struct sc_usb_device *d = &devices[i];
const char *selection = d->selected ? "-->" : " ";
LOG(level, " %s %-18s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
selection, d->serial, d->vid, d->pid, d->manufacturer, d->product);
// Convert uint16_t to unsigned because PRIx16 may not exist on Windows
LOG(level, " %s %-18s (%04x:%04x) %s %s",
selection, d->serial, (unsigned) d->vid, (unsigned) d->pid,
d->manufacturer, d->product);
}
}

Expand Down Expand Up @@ -216,7 +219,25 @@ sc_usb_destroy(struct sc_usb *usb) {
libusb_exit(usb->context);
}

static int
static void
sc_usb_report_disconnected(struct sc_usb *usb) {
if (usb->cbs && !atomic_flag_test_and_set(&usb->disconnection_notified)) {
assert(usb->cbs && usb->cbs->on_disconnected);
usb->cbs->on_disconnected(usb, usb->cbs_userdata);
}
}

bool
sc_usb_check_disconnected(struct sc_usb *usb, int result) {
if (result == LIBUSB_ERROR_NO_DEVICE || result == LIBUSB_ERROR_NOT_FOUND) {
sc_usb_report_disconnected(usb);
return false;
}

return true;
}

static LIBUSB_CALL int
sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
libusb_hotplug_event event, void *userdata) {
(void) ctx;
Expand All @@ -232,8 +253,7 @@ sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
return 0;
}

assert(usb->cbs && usb->cbs->on_disconnected);
usb->cbs->on_disconnected(usb, usb->cbs_userdata);
sc_usb_report_disconnected(usb);

// Do not automatically deregister the callback by returning 1. Instead,
// manually deregister to interrupt libusb_handle_events() from the libusb
Expand Down Expand Up @@ -307,6 +327,7 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device,

if (cbs) {
atomic_init(&usb->stopped, false);
usb->disconnection_notified = (atomic_flag) ATOMIC_FLAG_INIT;
if (sc_usb_register_callback(usb)) {
// Create a thread to process libusb events, so that device
// disconnection could be detected immediately
Expand All @@ -317,8 +338,6 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device,
LOGW("Libusb event thread handler could not be created, USB "
"device disconnection might not be detected immediately");
}
} else {
LOGW("Could not register USB device disconnection callback");
}
}

Expand Down
6 changes: 6 additions & 0 deletions app/src/usb/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct sc_usb {
sc_thread libusb_event_thread;

atomic_bool stopped; // only used if cbs != NULL
atomic_flag disconnection_notified;
};

struct sc_usb_callbacks {
Expand Down Expand Up @@ -73,6 +74,11 @@ sc_usb_connect(struct sc_usb *usb, libusb_device *device,
void
sc_usb_disconnect(struct sc_usb *usb);

// A client should call this function with the return value of a libusb call
// to detect disconnection immediately
bool
sc_usb_check_disconnected(struct sc_usb *usb, int result);

void
sc_usb_stop(struct sc_usb *usb);

Expand Down
14 changes: 10 additions & 4 deletions app/src/util/process_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@
ssize_t
sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,
size_t len) {
if (!sc_intr_set_process(intr, pid)) {
if (intr && !sc_intr_set_process(intr, pid)) {
// Already interrupted
return false;
}

ssize_t ret = sc_pipe_read(pipe, data, len);

sc_intr_set_process(intr, SC_PROCESS_NONE);
if (intr) {
sc_intr_set_process(intr, SC_PROCESS_NONE);
}

return ret;
}

ssize_t
sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe,
char *data, size_t len) {
if (!sc_intr_set_process(intr, pid)) {
if (intr && !sc_intr_set_process(intr, pid)) {
// Already interrupted
return false;
}

ssize_t ret = sc_pipe_read_all(pipe, data, len);

sc_intr_set_process(intr, SC_PROCESS_NONE);
if (intr) {
sc_intr_set_process(intr, SC_PROCESS_NONE);
}

return ret;
}
2 changes: 2 additions & 0 deletions cross_win32.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ ffmpeg_avformat = 'avformat-58'
ffmpeg_avutil = 'avutil-56'
prebuilt_ffmpeg = 'ffmpeg-win32-4.3.1'
prebuilt_sdl2 = 'SDL2-2.0.20/i686-w64-mingw32'
prebuilt_libusb_root = 'libusb-1.0.25'
prebuilt_libusb = prebuilt_libusb_root + '/MinGW32'
2 changes: 2 additions & 0 deletions cross_win64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ ffmpeg_avformat = 'avformat-59'
ffmpeg_avutil = 'avutil-57'
prebuilt_ffmpeg = 'ffmpeg-win64-5.0'
prebuilt_sdl2 = 'SDL2-2.0.20/x86_64-w64-mingw32'
prebuilt_libusb_root = 'libusb-1.0.25'
prebuilt_libusb = prebuilt_libusb_root + '/MinGW64'
Loading