Compare commits

..

10 Commits

Author SHA1 Message Date
Romain Vimont
d9bc5082ab Disable USB features for win32
Currently, there is an issue with the libusb prebuilt dll.

Refs libusb/#1049 <https://github.com/libusb/libusb/issues/1049>
PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:52 +01:00
Romain Vimont
73a5311ac6 Forbid HID input without OTG on Windows
On Windows, if the adb daemon is running, opening the USB device will
necessarily fail, so HID input is not possible.

Refs #2773 <https://github.com/Genymobile/scrcpy/issues/2773>
PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:42 +01:00
Romain Vimont
25296ae167 Kill adb daemon in OTG mode on Windows
On Windows, it is not possible to open a USB device from several
process, so HID events may only work if no adb daemon is running.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:39 +01:00
Romain Vimont
3bb24b3926 Make intr optional for adb commands
All adb commands are executed with an "interruptor", so that they can be
interrupted on Ctrl+C.

Make this interruptor optional, so that we could call "adb kill-server"
in OTG mode. This command always returns almost immediately anyway.

Ideally, we should make all blocking calls interruptible (including
libusb calls, by using the asynchronous API), but it's a lot of work,
and in practice it works well enough.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:36 +01:00
Romain Vimont
6ee75c0cff Remove obsolete text in error message
The HID/OTG features are now available on all platforms.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:30 +01:00
Romain Vimont
6b65cd405a Build for Windows with libusb support
Fixes #2773 <https://github.com/Genymobile/scrcpy/issues/2773>
PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:12 +01:00
Romain Vimont
ff3cb31cb4 Fix libusb callback for Windows
Add LIBUSB_CALL so that the callback has the correct signature on
Windows (including __attribute__((stdcall))).

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:09 +01:00
Romain Vimont
06243e7c3c Avoid PRIx16 printf format on Windows
Convert uint16_t to unsigned to avoid using PRIx16, which may not exist
on Windows.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:39:04 +01:00
Romain Vimont
b9b2879789 Remove USB hotplug callback error log
If it fails, the error is already logged by sc_usb_register_callback().

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:38:51 +01:00
Romain Vimont
be1936bb85 Report USB device disconnection when detected
USB device disconnection is detected via a hotplug callback when it is
supported.

In addition, report disconnection on libusb calls returning
LIBUSB_ERROR_NO_DEVICE or LIBUSB_ERROR_NOT_FOUND. This allows to detect
disconnection after a libusb call when hotplug is not available.

PR #3011 <https://github.com/Genymobile/scrcpy/pull/3011>
2022-02-20 17:38:14 +01:00
14 changed files with 146 additions and 26 deletions

View File

@@ -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 \
@@ -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 \

View File

@@ -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',
@@ -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')
]

View File

@@ -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 /

View File

@@ -150,7 +150,7 @@ 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;
}
@@ -158,7 +158,9 @@ process_check_success_intr(struct sc_intr *intr, sc_pid pid, const char *name,
// 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);
@@ -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) {

View File

@@ -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);

View File

@@ -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:
@@ -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:
@@ -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:
@@ -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.

View File

@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}

View File

@@ -2,6 +2,7 @@
#include <SDL2/SDL.h>
#include "adb/adb.h"
#include "events.h"
#include "screen_otg.h"
#include "util/log.h"
@@ -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,
};
@@ -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);

View File

@@ -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;
}
@@ -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);
}
}
@@ -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;
@@ -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
@@ -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
@@ -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");
}
}

View File

@@ -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 {
@@ -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);

View File

@@ -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;
}

View File

@@ -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'

View File

@@ -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'

View File

@@ -66,17 +66,21 @@ prepare-deps-win32:
@app/prebuilt-deps/prepare-adb.sh
@app/prebuilt-deps/prepare-sdl.sh
@app/prebuilt-deps/prepare-ffmpeg-win32.sh
@app/prebuilt-deps/prepare-libusb.sh
prepare-deps-win64:
@app/prebuilt-deps/prepare-adb.sh
@app/prebuilt-deps/prepare-sdl.sh
@app/prebuilt-deps/prepare-ffmpeg-win64.sh
@app/prebuilt-deps/prepare-libusb.sh
build-win32: prepare-deps-win32
# -Dusb=false because of libusb-win32 build issue, cf #3011
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
meson "$(WIN32_BUILD_DIR)" \
--cross-file cross_win32.txt \
--buildtype release --strip -Db_lto=true \
-Dusb=false \
-Dcompile_server=false \
-Dportable=true )
ninja -C "$(WIN32_BUILD_DIR)"
@@ -107,6 +111,7 @@ dist-win32: build-server build-win32
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
cp app/prebuilt-deps/data/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
#cp app/prebuilt-deps/data/libusb-1.0.25/MinGW32/dll/libusb-1.0.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
dist-win64: build-server build-win64
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
@@ -125,6 +130,7 @@ dist-win64: build-server build-win64
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
cp app/prebuilt-deps/data/libusb-1.0.25/MinGW64/dll/libusb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \