Compare commits
21 Commits
macos_open
...
libusb-win
Author | SHA1 | Date | |
---|---|---|---|
26ee63d9e9 | |||
0d3ed2f669 | |||
0480581789 | |||
c0bc183157 | |||
dddd960a9e | |||
5b05936093 | |||
14b608dbcc | |||
1718bfe00e | |||
4ad12755da | |||
a6bedb4301 | |||
a432134685 | |||
e78eb83cb9 | |||
f5b737857f | |||
4a759f9b55 | |||
c28e616a44 | |||
70a0d9cc37 | |||
b11c6550c7 | |||
7d7953e278 | |||
54d48321fd | |||
3456ca19c7 | |||
79eb78a9e8 |
15
README.md
15
README.md
@ -422,7 +422,7 @@ scrcpy -b2M -m800 # short version
|
|||||||
|
|
||||||
#### Multi-devices
|
#### Multi-devices
|
||||||
|
|
||||||
If several devices are listed in `adb devices`, you must specify the _serial_:
|
If several devices are listed in `adb devices`, you can specify the _serial_:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --serial 0123456789abcdef
|
scrcpy --serial 0123456789abcdef
|
||||||
@ -436,6 +436,19 @@ scrcpy --serial 192.168.0.1:5555
|
|||||||
scrcpy -s 192.168.0.1:5555 # short version
|
scrcpy -s 192.168.0.1:5555 # short version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If only one device is connected via either USB or TCP/IP, it is possible to
|
||||||
|
select it automatically:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Select the only device connected via USB
|
||||||
|
scrcpy --select-usb
|
||||||
|
scrcpy -U # short version
|
||||||
|
|
||||||
|
# Select the only device connected via TCP/IP
|
||||||
|
scrcpy --select-tcpip
|
||||||
|
scrcpy -T # short version
|
||||||
|
```
|
||||||
|
|
||||||
You can start several instances of _scrcpy_ for several devices.
|
You can start several instances of _scrcpy_ for several devices.
|
||||||
|
|
||||||
#### Autostart on device connection
|
#### Autostart on device connection
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
src = [
|
src = [
|
||||||
'src/main.c',
|
'src/main.c',
|
||||||
'src/adb/adb.c',
|
'src/adb/adb.c',
|
||||||
|
'src/adb/adb_device.c',
|
||||||
'src/adb/adb_parser.c',
|
'src/adb/adb_parser.c',
|
||||||
'src/adb/adb_tunnel.c',
|
'src/adb/adb_tunnel.c',
|
||||||
'src/cli.c',
|
'src/cli.c',
|
||||||
@ -72,7 +73,7 @@ if v4l2_support
|
|||||||
src += [ 'src/v4l2_sink.c' ]
|
src += [ 'src/v4l2_sink.c' ]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
usb_support = host_machine.system() == 'linux'
|
usb_support = host_machine.system() == 'linux' or host_machine.system() == 'windows'
|
||||||
if usb_support
|
if usb_support
|
||||||
src += [
|
src += [
|
||||||
'src/usb/aoa_hid.c',
|
'src/usb/aoa_hid.c',
|
||||||
@ -139,9 +140,22 @@ else
|
|||||||
include_directories: include_directories(ffmpeg_include_dir)
|
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 = [
|
dependencies = [
|
||||||
ffmpeg,
|
ffmpeg,
|
||||||
sdl2,
|
sdl2,
|
||||||
|
libusb,
|
||||||
cc.find_library('mingw32')
|
cc.find_library('mingw32')
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -221,6 +235,7 @@ if get_option('buildtype') == 'debug'
|
|||||||
tests = [
|
tests = [
|
||||||
['test_adb_parser', [
|
['test_adb_parser', [
|
||||||
'tests/test_adb_parser.c',
|
'tests/test_adb_parser.c',
|
||||||
|
'src/adb/adb_device.c',
|
||||||
'src/adb/adb_parser.c',
|
'src/adb/adb_parser.c',
|
||||||
'src/util/str.c',
|
'src/util/str.c',
|
||||||
'src/util/strbuf.c',
|
'src/util/strbuf.c',
|
||||||
|
@ -273,6 +273,14 @@ Set the TCP port of the adb tunnel to reach the scrcpy server. This option autom
|
|||||||
|
|
||||||
Default is 0 (not forced): the local port used for establishing the tunnel will be used.
|
Default is 0 (not forced): the local port used for establishing the tunnel will be used.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-T, \-\-select\-tcpip
|
||||||
|
Use TCP/IP device (if there is exactly one).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-U, \-\-select\-usb
|
||||||
|
Use USB device (if there is exactly one).
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-v4l2-sink " /dev/videoN
|
.BI "\-\-v4l2-sink " /dev/videoN
|
||||||
Output to v4l2loopback device.
|
Output to v4l2loopback device.
|
||||||
|
@ -194,6 +194,14 @@ sc_adb_execute(const char *const argv[], unsigned flags) {
|
|||||||
return sc_adb_execute_p(argv, flags, NULL);
|
return sc_adb_execute_p(argv, flags, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_start_server(struct sc_intr *intr, unsigned flags) {
|
||||||
|
const char *const argv[] = SC_ADB_COMMAND("start-server");
|
||||||
|
|
||||||
|
sc_pid pid = sc_adb_execute(argv, flags);
|
||||||
|
return process_check_success_intr(intr, pid, "adb start-server", flags);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
||||||
const char *device_socket_name, unsigned flags) {
|
const char *device_socket_name, unsigned flags) {
|
||||||
@ -374,6 +382,234 @@ sc_adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
|
|||||||
return process_check_success_intr(intr, pid, "adb disconnect", flags);
|
return process_check_success_intr(intr, pid, "adb disconnect", flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
|
||||||
|
struct sc_adb_device *devices, size_t len) {
|
||||||
|
const char *const argv[] = SC_ADB_COMMAND("devices", "-l");
|
||||||
|
|
||||||
|
sc_pipe pout;
|
||||||
|
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
|
||||||
|
if (pid == SC_PROCESS_NONE) {
|
||||||
|
LOGE("Could not execute \"adb devices -l\"");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[4096];
|
||||||
|
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
||||||
|
sc_pipe_close(pout);
|
||||||
|
|
||||||
|
bool ok = process_check_success_intr(intr, pid, "adb devices -l", flags);
|
||||||
|
if (!ok) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((size_t) r < sizeof(buf));
|
||||||
|
if (r == sizeof(buf) - 1) {
|
||||||
|
// The implementation assumes that the output of "adb devices -l" fits
|
||||||
|
// in the buffer in a single pass
|
||||||
|
LOGW("Result of \"adb devices -l\" does not fit in 4Kb. "
|
||||||
|
"Please report an issue.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is parsed as a NUL-terminated string
|
||||||
|
buf[r] = '\0';
|
||||||
|
|
||||||
|
// List all devices to the output list directly
|
||||||
|
return sc_adb_parse_devices(buf, devices, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_adb_accept_device(const struct sc_adb_device *device,
|
||||||
|
const struct sc_adb_device_selector *selector) {
|
||||||
|
switch (selector->type) {
|
||||||
|
case SC_ADB_DEVICE_SELECT_ALL:
|
||||||
|
return true;
|
||||||
|
case SC_ADB_DEVICE_SELECT_SERIAL:
|
||||||
|
assert(selector->serial);
|
||||||
|
char *device_serial_colon = strchr(device->serial, ':');
|
||||||
|
if (device_serial_colon) {
|
||||||
|
// The device serial is an IP:port...
|
||||||
|
char *serial_colon = strchr(selector->serial, ':');
|
||||||
|
if (!serial_colon) {
|
||||||
|
// But the requested serial has no ':', so only consider
|
||||||
|
// the IP part of the device serial. This allows to use
|
||||||
|
// "192.168.1.1" to match any "192.168.1.1:port".
|
||||||
|
size_t serial_len = strlen(selector->serial);
|
||||||
|
size_t device_ip_len = device_serial_colon - device->serial;
|
||||||
|
if (serial_len != device_ip_len) {
|
||||||
|
// They are not equal, they don't even have the same
|
||||||
|
// length
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !strncmp(selector->serial, device->serial,
|
||||||
|
device_ip_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !strcmp(selector->serial, device->serial);
|
||||||
|
case SC_ADB_DEVICE_SELECT_USB:
|
||||||
|
return !sc_adb_is_serial_tcpip(device->serial);
|
||||||
|
case SC_ADB_DEVICE_SELECT_TCPIP:
|
||||||
|
return sc_adb_is_serial_tcpip(device->serial);
|
||||||
|
default:
|
||||||
|
assert(!"Missing SC_ADB_DEVICE_SELECT_* handling");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
sc_adb_devices_select(struct sc_adb_device *devices, size_t len,
|
||||||
|
const struct sc_adb_device_selector *selector,
|
||||||
|
size_t *idx_out) {
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
struct sc_adb_device *device = &devices[i];
|
||||||
|
device->selected = sc_adb_accept_device(device, selector);
|
||||||
|
if (device->selected) {
|
||||||
|
if (idx_out && !count) {
|
||||||
|
*idx_out = i;
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_adb_devices_log(enum sc_log_level level, struct sc_adb_device *devices,
|
||||||
|
size_t count) {
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
struct sc_adb_device *d = &devices[i];
|
||||||
|
const char *selection = d->selected ? "-->" : " ";
|
||||||
|
const char *type = sc_adb_is_serial_tcpip(d->serial) ? "(tcpip)"
|
||||||
|
: " (usb)";
|
||||||
|
LOG(level, " %s %s %-20s %16s %s",
|
||||||
|
selection, type, d->serial, d->state, d->model ? d->model : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_adb_device_check_state(struct sc_adb_device *device,
|
||||||
|
struct sc_adb_device *devices, size_t count) {
|
||||||
|
const char *state = device->state;
|
||||||
|
|
||||||
|
if (!strcmp("device", state)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp("unauthorized", state)) {
|
||||||
|
LOGE("Device is unauthorized:");
|
||||||
|
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
||||||
|
LOGE("A popup should open on the device to request authorization.");
|
||||||
|
LOGE("Check the FAQ: "
|
||||||
|
"<https://github.com/Genymobile/scrcpy/blob/master/FAQ.md>");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_select_device(struct sc_intr *intr,
|
||||||
|
const struct sc_adb_device_selector *selector,
|
||||||
|
unsigned flags, struct sc_adb_device *out_device) {
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count =
|
||||||
|
sc_adb_list_devices(intr, flags, devices, ARRAY_LEN(devices));
|
||||||
|
if (count == -1) {
|
||||||
|
LOGE("Could not list ADB devices");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
LOGE("Could not find any ADB device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sel_idx; // index of the single matching device if sel_count == 1
|
||||||
|
size_t sel_count =
|
||||||
|
sc_adb_devices_select(devices, count, selector, &sel_idx);
|
||||||
|
|
||||||
|
if (sel_count == 0) {
|
||||||
|
// if count > 0 && sel_count == 0, then necessarily a selection is
|
||||||
|
// requested
|
||||||
|
assert(selector->type != SC_ADB_DEVICE_SELECT_ALL);
|
||||||
|
|
||||||
|
switch (selector->type) {
|
||||||
|
case SC_ADB_DEVICE_SELECT_SERIAL:
|
||||||
|
assert(selector->serial);
|
||||||
|
LOGE("Could not find ADB device %s:", selector->serial);
|
||||||
|
break;
|
||||||
|
case SC_ADB_DEVICE_SELECT_USB:
|
||||||
|
LOGE("Could not find any ADB device over USB:");
|
||||||
|
break;
|
||||||
|
case SC_ADB_DEVICE_SELECT_TCPIP:
|
||||||
|
LOGE("Could not find any ADB device over TCP/IP:");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(!"Unexpected selector type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sel_count > 1) {
|
||||||
|
switch (selector->type) {
|
||||||
|
case SC_ADB_DEVICE_SELECT_ALL:
|
||||||
|
LOGE("Multiple (%" SC_PRIsizet ") ADB devices:", sel_count);
|
||||||
|
break;
|
||||||
|
case SC_ADB_DEVICE_SELECT_SERIAL:
|
||||||
|
assert(selector->serial);
|
||||||
|
LOGE("Multiple (%" SC_PRIsizet ") ADB devices with serial %s:",
|
||||||
|
sel_count, selector->serial);
|
||||||
|
break;
|
||||||
|
case SC_ADB_DEVICE_SELECT_USB:
|
||||||
|
LOGE("Multiple (%" SC_PRIsizet ") ADB devices over USB:",
|
||||||
|
sel_count);
|
||||||
|
break;
|
||||||
|
case SC_ADB_DEVICE_SELECT_TCPIP:
|
||||||
|
LOGE("Multiple (%" SC_PRIsizet ") ADB devices over TCP/IP:",
|
||||||
|
sel_count);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(!"Unexpected selector type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
||||||
|
if (selector->type != SC_ADB_DEVICE_SELECT_ALL) {
|
||||||
|
LOGE("Specify the device via -s or --serial");
|
||||||
|
}
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
|
||||||
|
struct sc_adb_device *device = &devices[sel_idx];
|
||||||
|
|
||||||
|
bool ok = sc_adb_device_check_state(device, devices, count);
|
||||||
|
if (!ok) {
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("ADB device found:");
|
||||||
|
sc_adb_devices_log(SC_LOG_LEVEL_DEBUG, devices, count);
|
||||||
|
|
||||||
|
// Move devics into out_device (do not destroy device)
|
||||||
|
sc_adb_device_move(out_device, device);
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
|
sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
|
||||||
unsigned flags) {
|
unsigned flags) {
|
||||||
@ -409,38 +645,6 @@ sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
|
|||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
sc_adb_get_serialno(struct sc_intr *intr, unsigned flags) {
|
|
||||||
const char *const argv[] = SC_ADB_COMMAND("get-serialno");
|
|
||||||
|
|
||||||
sc_pipe pout;
|
|
||||||
sc_pid pid = sc_adb_execute_p(argv, flags, &pout);
|
|
||||||
if (pid == SC_PROCESS_NONE) {
|
|
||||||
LOGE("Could not execute \"adb get-serialno\"");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char buf[128];
|
|
||||||
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
|
||||||
sc_pipe_close(pout);
|
|
||||||
|
|
||||||
bool ok = process_check_success_intr(intr, pid, "adb get-serialno", flags);
|
|
||||||
if (!ok) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert((size_t) r < sizeof(buf));
|
|
||||||
buf[r] = '\0';
|
|
||||||
size_t len = strcspn(buf, " \r\n");
|
|
||||||
buf[len] = '\0';
|
|
||||||
|
|
||||||
return strdup(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
char *
|
||||||
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
||||||
assert(serial);
|
assert(serial);
|
||||||
@ -482,3 +686,8 @@ sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
|||||||
|
|
||||||
return sc_adb_parse_device_ip_from_output(buf);
|
return sc_adb_parse_device_ip_from_output(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_is_serial_tcpip(const char *serial) {
|
||||||
|
return strchr(serial, ':');
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include "adb_device.h"
|
||||||
#include "util/intr.h"
|
#include "util/intr.h"
|
||||||
|
|
||||||
#define SC_ADB_NO_STDOUT (1 << 0)
|
#define SC_ADB_NO_STDOUT (1 << 0)
|
||||||
@ -17,9 +18,24 @@
|
|||||||
const char *
|
const char *
|
||||||
sc_adb_get_executable(void);
|
sc_adb_get_executable(void);
|
||||||
|
|
||||||
|
enum sc_adb_device_selector_type {
|
||||||
|
SC_ADB_DEVICE_SELECT_ALL,
|
||||||
|
SC_ADB_DEVICE_SELECT_SERIAL,
|
||||||
|
SC_ADB_DEVICE_SELECT_USB,
|
||||||
|
SC_ADB_DEVICE_SELECT_TCPIP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_adb_device_selector {
|
||||||
|
enum sc_adb_device_selector_type type;
|
||||||
|
const char *serial;
|
||||||
|
};
|
||||||
|
|
||||||
sc_pid
|
sc_pid
|
||||||
sc_adb_execute(const char *const argv[], unsigned flags);
|
sc_adb_execute(const char *const argv[], unsigned flags);
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_start_server(struct sc_intr *intr, unsigned flags);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
sc_adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
|
||||||
const char *device_socket_name, unsigned flags);
|
const char *device_socket_name, unsigned flags);
|
||||||
@ -69,6 +85,16 @@ sc_adb_connect(struct sc_intr *intr, const char *ip_port, unsigned flags);
|
|||||||
bool
|
bool
|
||||||
sc_adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags);
|
sc_adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute `adb devices` and parse the result to select a device
|
||||||
|
*
|
||||||
|
* Return true if a single matching device is found, and write it to out_device.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sc_adb_select_device(struct sc_intr *intr,
|
||||||
|
const struct sc_adb_device_selector *selector,
|
||||||
|
unsigned flags, struct sc_adb_device *out_device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute `adb getprop <prop>`
|
* Execute `adb getprop <prop>`
|
||||||
*/
|
*/
|
||||||
@ -76,14 +102,6 @@ char *
|
|||||||
sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
|
sc_adb_getprop(struct sc_intr *intr, const char *serial, const char *prop,
|
||||||
unsigned flags);
|
unsigned flags);
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute `adb get-serialno`
|
|
||||||
*
|
|
||||||
* Return the result, to be freed by the caller, or NULL on error.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
sc_adb_get_serialno(struct sc_intr *intr, unsigned flags);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to retrieve the device IP
|
* Attempt to retrieve the device IP
|
||||||
*
|
*
|
||||||
@ -93,4 +111,13 @@ sc_adb_get_serialno(struct sc_intr *intr, unsigned flags);
|
|||||||
char *
|
char *
|
||||||
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags);
|
sc_adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if the serial represents an IP address
|
||||||
|
*
|
||||||
|
* In practice, it just returns true if and only if it contains a ':', which is
|
||||||
|
* sufficient to distinguish an ip:port from a real USB serial.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sc_adb_is_serial_tcpip(const char *serial);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
26
app/src/adb/adb_device.c
Normal file
26
app/src/adb/adb_device.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "adb_device.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_device_destroy(struct sc_adb_device *device) {
|
||||||
|
free(device->serial);
|
||||||
|
free(device->state);
|
||||||
|
free(device->model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_device_move(struct sc_adb_device *dst, struct sc_adb_device *src) {
|
||||||
|
*dst = *src;
|
||||||
|
src->serial = NULL;
|
||||||
|
src->state = NULL;
|
||||||
|
src->model = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_devices_destroy_all(struct sc_adb_device *devices, size_t count) {
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
sc_adb_device_destroy(&devices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
34
app/src/adb/adb_device.h
Normal file
34
app/src/adb/adb_device.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef SC_ADB_DEVICE_H
|
||||||
|
#define SC_ADB_DEVICE_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct sc_adb_device {
|
||||||
|
char *serial;
|
||||||
|
char *state;
|
||||||
|
char *model;
|
||||||
|
bool selected;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_device_destroy(struct sc_adb_device *device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move src to dest
|
||||||
|
*
|
||||||
|
* After this call, the content of src is undefined, except that
|
||||||
|
* sc_adb_device_destroy() can be called.
|
||||||
|
*
|
||||||
|
* This is useful to take a device from a list that will be destroyed, without
|
||||||
|
* making unnecessary copies.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sc_adb_device_move(struct sc_adb_device *dst, struct sc_adb_device *src);
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_adb_devices_destroy_all(struct sc_adb_device *devices, size_t count);
|
||||||
|
|
||||||
|
#endif
|
@ -1,11 +1,170 @@
|
|||||||
#include "adb_parser.h"
|
#include "adb_parser.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/str.h"
|
#include "util/str.h"
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_adb_parse_device(char *line, struct sc_adb_device *device) {
|
||||||
|
// One device line looks like:
|
||||||
|
// "0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
// "device:MyDevice transport_id:1"
|
||||||
|
|
||||||
|
if (line[0] == '*') {
|
||||||
|
// Garbage lines printed by adb daemon while starting start with a '*'
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp("adb server", line, sizeof("adb server") - 1)) {
|
||||||
|
// Ignore lines starting with "adb server":
|
||||||
|
// adb server version (41) doesn't match this client (39); killing...
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = line; // cursor in the line
|
||||||
|
|
||||||
|
// After the serial:
|
||||||
|
// - "adb devices" writes a single '\t'
|
||||||
|
// - "adb devices -l" writes multiple spaces
|
||||||
|
// For flexibility, accept both.
|
||||||
|
size_t serial_len = strcspn(s, " \t");
|
||||||
|
if (!serial_len) {
|
||||||
|
// empty serial
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool eol = s[serial_len] == '\0';
|
||||||
|
if (eol) {
|
||||||
|
// serial alone is unexpected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s[serial_len] = '\0';
|
||||||
|
char *serial = s;
|
||||||
|
s += serial_len + 1;
|
||||||
|
// After the serial, there might be several spaces
|
||||||
|
s += strspn(s, " \t"); // consume all separators
|
||||||
|
|
||||||
|
size_t state_len = strcspn(s, " ");
|
||||||
|
if (!state_len) {
|
||||||
|
// empty state
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
eol = s[state_len] == '\0';
|
||||||
|
s[state_len] = '\0';
|
||||||
|
char *state = s;
|
||||||
|
|
||||||
|
char *model = NULL;
|
||||||
|
if (!eol) {
|
||||||
|
s += state_len + 1;
|
||||||
|
|
||||||
|
// Iterate over all properties "key:value key:value ..."
|
||||||
|
for (;;) {
|
||||||
|
size_t token_len = strcspn(s, " ");
|
||||||
|
if (!token_len) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
eol = s[token_len] == '\0';
|
||||||
|
s[token_len] = '\0';
|
||||||
|
char *token = s;
|
||||||
|
|
||||||
|
if (!strncmp("model:", token, sizeof("model:") - 1)) {
|
||||||
|
model = &token[sizeof("model:") - 1];
|
||||||
|
// We only need the model
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eol) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
s+= token_len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device->serial = strdup(serial);
|
||||||
|
if (!device->serial) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->state = strdup(state);
|
||||||
|
if (!device->state) {
|
||||||
|
free(device->serial);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model) {
|
||||||
|
device->model = strdup(model);
|
||||||
|
if (!device->model) {
|
||||||
|
LOG_OOM();
|
||||||
|
// model is optional, do not fail
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
device->model = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->selected = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
|
||||||
|
size_t devices_len) {
|
||||||
|
size_t dev_count = 0;
|
||||||
|
|
||||||
|
#define HEADER "List of devices attached"
|
||||||
|
#define HEADER_LEN (sizeof(HEADER) - 1)
|
||||||
|
bool header_found = false;
|
||||||
|
|
||||||
|
size_t idx_line = 0;
|
||||||
|
while (str[idx_line] != '\0') {
|
||||||
|
char *line = &str[idx_line];
|
||||||
|
size_t len = strcspn(line, "\n");
|
||||||
|
|
||||||
|
// The next line starts after the '\n' (replaced by `\0`)
|
||||||
|
idx_line += len;
|
||||||
|
|
||||||
|
if (str[idx_line] != '\0') {
|
||||||
|
// The next line starts after the '\n'
|
||||||
|
++idx_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!header_found) {
|
||||||
|
if (!strncmp(line, HEADER, HEADER_LEN)) {
|
||||||
|
header_found = true;
|
||||||
|
}
|
||||||
|
// Skip everything until the header, there might be garbage lines
|
||||||
|
// related to daemon starting before
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The line, but without any trailing '\r'
|
||||||
|
size_t line_len = sc_str_remove_trailing_cr(line, len);
|
||||||
|
line[line_len] = '\0';
|
||||||
|
|
||||||
|
bool ok = sc_adb_parse_device(line, &devices[dev_count]);
|
||||||
|
if (!ok) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++dev_count;
|
||||||
|
|
||||||
|
assert(dev_count <= devices_len);
|
||||||
|
if (dev_count == devices_len) {
|
||||||
|
// Max number of devices reached
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!header_found) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev_count;
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
sc_adb_parse_device_ip_from_line(char *line) {
|
sc_adb_parse_device_ip_from_line(char *line) {
|
||||||
// One line from "ip route" looks like:
|
// One line from "ip route" looks like:
|
||||||
@ -64,7 +223,7 @@ sc_adb_parse_device_ip_from_output(char *str) {
|
|||||||
|
|
||||||
if (str[idx_line] != '\0') {
|
if (str[idx_line] != '\0') {
|
||||||
// The next line starts after the '\n'
|
// The next line starts after the '\n'
|
||||||
idx_line += 1;
|
++idx_line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,19 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "adb_device.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the available devices from the output of `adb devices`
|
||||||
|
*
|
||||||
|
* The parameter must be a NUL-terminated string.
|
||||||
|
*
|
||||||
|
* Warning: this function modifies the buffer for optimization purposes.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
|
||||||
|
size_t devices_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the ip from the output of `adb shell ip route`
|
* Parse the ip from the output of `adb shell ip route`
|
||||||
*
|
*
|
||||||
|
@ -416,6 +416,16 @@ static const struct sc_option options[] = {
|
|||||||
"Default is 0 (not forced): the local port used for "
|
"Default is 0 (not forced): the local port used for "
|
||||||
"establishing the tunnel will be used.",
|
"establishing the tunnel will be used.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.shortopt = 'T',
|
||||||
|
.longopt = "select-tcpip",
|
||||||
|
.text = "Use TCP/IP device (if there is exactly one).",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortopt = 'U',
|
||||||
|
.longopt = "select-usb",
|
||||||
|
.text = "Use USB device (if there is exactly one).",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.longopt_id = OPT_V4L2_SINK,
|
.longopt_id = OPT_V4L2_SINK,
|
||||||
.longopt = "v4l2-sink",
|
.longopt = "v4l2-sink",
|
||||||
@ -1401,6 +1411,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
case 't':
|
case 't':
|
||||||
opts->show_touches = true;
|
opts->show_touches = true;
|
||||||
break;
|
break;
|
||||||
|
case 'T':
|
||||||
|
opts->select_tcpip = true;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
opts->select_usb = true;
|
||||||
|
break;
|
||||||
case OPT_ALWAYS_ON_TOP:
|
case OPT_ALWAYS_ON_TOP:
|
||||||
opts->always_on_top = true;
|
opts->always_on_top = true;
|
||||||
break;
|
break;
|
||||||
@ -1559,8 +1575,16 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
// If a TCP/IP address is provided, then tcpip must be enabled
|
// If a TCP/IP address is provided, then tcpip must be enabled
|
||||||
assert(opts->tcpip || !opts->tcpip_dst);
|
assert(opts->tcpip || !opts->tcpip_dst);
|
||||||
|
|
||||||
if (opts->serial && opts->tcpip_dst) {
|
unsigned selectors = !!opts->serial
|
||||||
LOGE("Incompatible options: -s/--serial and --tcpip with an argument");
|
+ !!opts->tcpip_dst
|
||||||
|
+ opts->select_tcpip
|
||||||
|
+ opts->select_usb;
|
||||||
|
if (selectors > 1) {
|
||||||
|
LOGE("At most one device selector option may be passed, among:\n"
|
||||||
|
" --serial (-s)\n"
|
||||||
|
" --select-usb (-U)\n"
|
||||||
|
" --select-tcpip (-T)\n"
|
||||||
|
" --tcpip=<addr> (with an argument)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
#ifndef __WIN32
|
#ifndef __WIN32
|
||||||
# define PRIu64_ PRIu64
|
# define PRIu64_ PRIu64
|
||||||
|
# define SC_PRIsizet "zu"
|
||||||
#else
|
#else
|
||||||
# define PRIu64_ "I64u" // Windows...
|
# define PRIu64_ "I64u" // Windows...
|
||||||
|
# define SC_PRIsizet "Iu"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// In ffmpeg/doc/APIchanges:
|
// In ffmpeg/doc/APIchanges:
|
||||||
|
@ -113,7 +113,7 @@ sc_controller_start(struct sc_controller *controller) {
|
|||||||
bool ok = sc_thread_create(&controller->thread, run_controller,
|
bool ok = sc_thread_create(&controller->thread, run_controller,
|
||||||
"scrcpy-ctl", controller);
|
"scrcpy-ctl", controller);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start controller thread");
|
LOGE("Could not start controller thread");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ sc_demuxer_start(struct sc_demuxer *demuxer) {
|
|||||||
bool ok = sc_thread_create(&demuxer->thread, run_demuxer, "scrcpy-demuxer",
|
bool ok = sc_thread_create(&demuxer->thread, run_demuxer, "scrcpy-demuxer",
|
||||||
demuxer);
|
demuxer);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start demuxer thread");
|
LOGE("Could not start demuxer thread");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -156,7 +156,7 @@ sc_file_pusher_start(struct sc_file_pusher *fp) {
|
|||||||
|
|
||||||
bool ok = sc_thread_create(&fp->thread, run_file_pusher, "scrcpy-file", fp);
|
bool ok = sc_thread_create(&fp->thread, run_file_pusher, "scrcpy-file", fp);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start file_pusher thread");
|
LOGE("Could not start file_pusher thread");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,4 +60,6 @@ const struct scrcpy_options scrcpy_options_default = {
|
|||||||
.downsize_on_error = true,
|
.downsize_on_error = true,
|
||||||
.tcpip = false,
|
.tcpip = false,
|
||||||
.tcpip_dst = NULL,
|
.tcpip_dst = NULL,
|
||||||
|
.select_tcpip = false,
|
||||||
|
.select_usb = false,
|
||||||
};
|
};
|
||||||
|
@ -135,6 +135,8 @@ struct scrcpy_options {
|
|||||||
bool downsize_on_error;
|
bool downsize_on_error;
|
||||||
bool tcpip;
|
bool tcpip;
|
||||||
const char *tcpip_dst;
|
const char *tcpip_dst;
|
||||||
|
bool select_usb;
|
||||||
|
bool select_tcpip;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct scrcpy_options scrcpy_options_default;
|
extern const struct scrcpy_options scrcpy_options_default;
|
||||||
|
@ -114,7 +114,7 @@ receiver_start(struct receiver *receiver) {
|
|||||||
bool ok = sc_thread_create(&receiver->thread, run_receiver,
|
bool ok = sc_thread_create(&receiver->thread, run_receiver,
|
||||||
"scrcpy-receiver", receiver);
|
"scrcpy-receiver", receiver);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start receiver thread");
|
LOGE("Could not start receiver thread");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ sc_recorder_open(struct sc_recorder *recorder, const AVCodec *input_codec) {
|
|||||||
ok = sc_thread_create(&recorder->thread, run_recorder, "scrcpy-recorder",
|
ok = sc_thread_create(&recorder->thread, run_recorder, "scrcpy-recorder",
|
||||||
recorder);
|
recorder);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start recorder thread");
|
LOGE("Could not start recorder thread");
|
||||||
goto error_avio_close;
|
goto error_avio_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
// Minimal SDL initialization
|
// Minimal SDL initialization
|
||||||
if (SDL_Init(SDL_INIT_EVENTS)) {
|
if (SDL_Init(SDL_INIT_EVENTS)) {
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
LOGE("Could not initialize SDL: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,6 +297,8 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
struct sc_server_params params = {
|
struct sc_server_params params = {
|
||||||
.req_serial = options->serial,
|
.req_serial = options->serial,
|
||||||
|
.select_usb = options->select_usb,
|
||||||
|
.select_tcpip = options->select_tcpip,
|
||||||
.log_level = options->log_level,
|
.log_level = options->log_level,
|
||||||
.crop = options->crop,
|
.crop = options->crop,
|
||||||
.port_range = options->port_range,
|
.port_range = options->port_range,
|
||||||
@ -341,7 +343,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
// Initialize SDL video in addition if display is enabled
|
// Initialize SDL video in addition if display is enabled
|
||||||
if (options->display && SDL_Init(SDL_INIT_VIDEO)) {
|
if (options->display && SDL_Init(SDL_INIT_VIDEO)) {
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
LOGE("Could not initialize SDL: %s", SDL_GetError());
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,32 +432,19 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(serial);
|
assert(serial);
|
||||||
struct sc_usb_device usb_devices[16];
|
struct sc_usb_device usb_device;
|
||||||
ssize_t count = sc_usb_find_devices(&s->usb, serial, usb_devices,
|
ok = sc_usb_select_device(&s->usb, serial, &usb_device);
|
||||||
ARRAY_LEN(usb_devices));
|
if (!ok) {
|
||||||
if (count <= 0) {
|
|
||||||
LOGE("Could not find USB device %s", serial);
|
|
||||||
sc_usb_destroy(&s->usb);
|
sc_usb_destroy(&s->usb);
|
||||||
sc_acksync_destroy(&s->acksync);
|
|
||||||
goto aoa_hid_end;
|
goto aoa_hid_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 1) {
|
|
||||||
LOGE("Multiple (%d) devices with serial %s", (int) count, serial);
|
|
||||||
sc_usb_device_destroy_all(usb_devices, count);
|
|
||||||
sc_usb_destroy(&s->usb);
|
|
||||||
sc_acksync_destroy(&s->acksync);
|
|
||||||
goto aoa_hid_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sc_usb_device *usb_device = &usb_devices[0];
|
|
||||||
|
|
||||||
LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
||||||
usb_device->serial, usb_device->vid, usb_device->pid,
|
usb_device.serial, usb_device.vid, usb_device.pid,
|
||||||
usb_device->manufacturer, usb_device->product);
|
usb_device.manufacturer, usb_device.product);
|
||||||
|
|
||||||
ok = sc_usb_connect(&s->usb, usb_device->device, NULL, NULL);
|
ok = sc_usb_connect(&s->usb, usb_device.device, NULL, NULL);
|
||||||
sc_usb_device_destroy(usb_device);
|
sc_usb_device_destroy(&usb_device);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Failed to connect to USB device %s", serial);
|
LOGE("Failed to connect to USB device %s", serial);
|
||||||
sc_usb_destroy(&s->usb);
|
sc_usb_destroy(&s->usb);
|
||||||
|
@ -423,14 +423,14 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
screen->window =
|
screen->window =
|
||||||
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
|
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
|
||||||
if (!screen->window) {
|
if (!screen->window) {
|
||||||
LOGC("Could not create window: %s", SDL_GetError());
|
LOGE("Could not create window: %s", SDL_GetError());
|
||||||
goto error_destroy_fps_counter;
|
goto error_destroy_fps_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->renderer = SDL_CreateRenderer(screen->window, -1,
|
screen->renderer = SDL_CreateRenderer(screen->window, -1,
|
||||||
SDL_RENDERER_ACCELERATED);
|
SDL_RENDERER_ACCELERATED);
|
||||||
if (!screen->renderer) {
|
if (!screen->renderer) {
|
||||||
LOGC("Could not create renderer: %s", SDL_GetError());
|
LOGE("Could not create renderer: %s", SDL_GetError());
|
||||||
goto error_destroy_window;
|
goto error_destroy_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,7 +479,7 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
params->frame_size.height);
|
params->frame_size.height);
|
||||||
screen->texture = create_texture(screen);
|
screen->texture = create_texture(screen);
|
||||||
if (!screen->texture) {
|
if (!screen->texture) {
|
||||||
LOGC("Could not create texture: %s", SDL_GetError());
|
LOGE("Could not create texture: %s", SDL_GetError());
|
||||||
goto error_destroy_renderer;
|
goto error_destroy_renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,7 +666,7 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
|||||||
screen->frame_size.width, screen->frame_size.height);
|
screen->frame_size.width, screen->frame_size.height);
|
||||||
screen->texture = create_texture(screen);
|
screen->texture = create_texture(screen);
|
||||||
if (!screen->texture) {
|
if (!screen->texture) {
|
||||||
LOGC("Could not create texture: %s", SDL_GetError());
|
LOGE("Could not create texture: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
150
app/src/server.c
150
app/src/server.c
@ -503,22 +503,6 @@ sc_server_on_terminated(void *userdata) {
|
|||||||
LOGD("Server terminated");
|
LOGD("Server terminated");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
|
||||||
sc_server_read_serial(struct sc_server *server) {
|
|
||||||
char *serial;
|
|
||||||
if (server->params.req_serial) {
|
|
||||||
// The serial is already known
|
|
||||||
serial = strdup(server->params.req_serial);
|
|
||||||
if (!serial) {
|
|
||||||
LOG_OOM();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
serial = sc_adb_get_serialno(&server->intr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_tcpip_mode_enabled(struct sc_server *server, const char *serial) {
|
is_tcpip_mode_enabled(struct sc_server *server, const char *serial) {
|
||||||
struct sc_intr *intr = &server->intr;
|
struct sc_intr *intr = &server->intr;
|
||||||
@ -641,51 +625,34 @@ sc_server_connect_to_tcpip(struct sc_server *server, const char *ip_port) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_server_configure_tcpip_known_address(struct sc_server *server,
|
||||||
|
const char *addr) {
|
||||||
|
// Append ":5555" if no port is present
|
||||||
|
bool contains_port = strchr(addr, ':');
|
||||||
|
char *ip_port = contains_port ? strdup(addr) : append_port_5555(addr);
|
||||||
|
if (!ip_port) {
|
||||||
|
LOG_OOM();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
server->serial = ip_port;
|
||||||
|
return sc_server_connect_to_tcpip(server, ip_port);
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
sc_server_configure_tcpip(struct sc_server *server) {
|
sc_server_configure_tcpip_unknown_address(struct sc_server *server,
|
||||||
char *ip_port;
|
const char *serial) {
|
||||||
|
bool is_already_tcpip = sc_adb_is_serial_tcpip(serial);
|
||||||
|
if (is_already_tcpip) {
|
||||||
|
// Nothing to do
|
||||||
|
LOGI("Device already connected via TCP/IP: %s", serial);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const struct sc_server_params *params = &server->params;
|
char *ip_port = sc_server_switch_to_tcpip(server, serial);
|
||||||
|
if (!ip_port) {
|
||||||
// If tcpip parameter is given, then it must connect to this address.
|
return false;
|
||||||
// Therefore, the device is unknown, so serial is meaningless at this point.
|
|
||||||
assert(!params->req_serial || !params->tcpip_dst);
|
|
||||||
|
|
||||||
if (params->tcpip_dst) {
|
|
||||||
// Append ":5555" if no port is present
|
|
||||||
bool contains_port = strchr(params->tcpip_dst, ':');
|
|
||||||
ip_port = contains_port ? strdup(params->tcpip_dst)
|
|
||||||
: append_port_5555(params->tcpip_dst);
|
|
||||||
if (!ip_port) {
|
|
||||||
LOG_OOM();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The device IP address must be retrieved from the current
|
|
||||||
// connected device
|
|
||||||
char *serial = sc_server_read_serial(server);
|
|
||||||
if (!serial) {
|
|
||||||
LOGE("Could not get device serial");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The serial is either the real serial when connected via USB, or
|
|
||||||
// the IP:PORT when connected over TCP/IP. Only the latter contains
|
|
||||||
// a colon.
|
|
||||||
bool is_already_tcpip = strchr(serial, ':');
|
|
||||||
if (is_already_tcpip) {
|
|
||||||
// Nothing to do
|
|
||||||
LOGI("Device already connected via TCP/IP: %s", serial);
|
|
||||||
free(serial);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ip_port = sc_server_switch_to_tcpip(server, serial);
|
|
||||||
free(serial);
|
|
||||||
if (!ip_port) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server->serial = ip_port;
|
server->serial = ip_port;
|
||||||
@ -698,16 +665,69 @@ run_server(void *data) {
|
|||||||
|
|
||||||
const struct sc_server_params *params = &server->params;
|
const struct sc_server_params *params = &server->params;
|
||||||
|
|
||||||
if (params->tcpip) {
|
// Execute "adb start-server" before "adb devices" so that daemon starting
|
||||||
bool ok = sc_server_configure_tcpip(server);
|
// output/errors is correctly printed in the console ("adb devices" output
|
||||||
|
// is parsed, so it is not output)
|
||||||
|
bool ok = sc_adb_start_server(&server->intr, 0);
|
||||||
|
if (!ok) {
|
||||||
|
LOGE("Could not start adb daemon");
|
||||||
|
goto error_connection_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// params->tcpip_dst implies params->tcpip
|
||||||
|
assert(!params->tcpip_dst || params->tcpip);
|
||||||
|
|
||||||
|
// If tcpip_dst parameter is given, then it must connect to this address.
|
||||||
|
// Therefore, the device is unknown, so serial is meaningless at this point.
|
||||||
|
assert(!params->req_serial || !params->tcpip_dst);
|
||||||
|
|
||||||
|
// A device must be selected via a serial in all cases except when --tcpip=
|
||||||
|
// is called with a parameter (in that case, the device may initially not
|
||||||
|
// exist, and scrcpy will execute "adb connect").
|
||||||
|
bool need_initial_serial = !params->tcpip_dst;
|
||||||
|
|
||||||
|
if (need_initial_serial) {
|
||||||
|
// At most one of the 3 following parameters may be set
|
||||||
|
assert(!!params->req_serial
|
||||||
|
+ params->select_usb
|
||||||
|
+ params->select_tcpip <= 1);
|
||||||
|
|
||||||
|
struct sc_adb_device_selector selector;
|
||||||
|
if (params->req_serial) {
|
||||||
|
selector.type = SC_ADB_DEVICE_SELECT_SERIAL;
|
||||||
|
selector.serial = params->req_serial;
|
||||||
|
} else if (params->select_usb) {
|
||||||
|
selector.type = SC_ADB_DEVICE_SELECT_USB;
|
||||||
|
} else if (params->select_tcpip) {
|
||||||
|
selector.type = SC_ADB_DEVICE_SELECT_TCPIP;
|
||||||
|
} else {
|
||||||
|
selector.type = SC_ADB_DEVICE_SELECT_ALL;
|
||||||
|
}
|
||||||
|
struct sc_adb_device device;
|
||||||
|
ok = sc_adb_select_device(&server->intr, &selector, 0, &device);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
assert(server->serial);
|
|
||||||
|
if (params->tcpip) {
|
||||||
|
assert(!params->tcpip_dst);
|
||||||
|
ok = sc_server_configure_tcpip_unknown_address(server,
|
||||||
|
device.serial);
|
||||||
|
sc_adb_device_destroy(&device);
|
||||||
|
if (!ok) {
|
||||||
|
goto error_connection_failed;
|
||||||
|
}
|
||||||
|
assert(server->serial);
|
||||||
|
} else {
|
||||||
|
// "move" the device.serial without copy
|
||||||
|
server->serial = device.serial;
|
||||||
|
// the serial must not be freed by the destructor
|
||||||
|
device.serial = NULL;
|
||||||
|
sc_adb_device_destroy(&device);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
server->serial = sc_server_read_serial(server);
|
ok = sc_server_configure_tcpip_known_address(server, params->tcpip_dst);
|
||||||
if (!server->serial) {
|
if (!ok) {
|
||||||
LOGD("Could not get device serial");
|
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -716,7 +736,7 @@ run_server(void *data) {
|
|||||||
assert(serial);
|
assert(serial);
|
||||||
LOGD("Device serial: %s", serial);
|
LOGD("Device serial: %s", serial);
|
||||||
|
|
||||||
bool ok = push_server(&server->intr, serial);
|
ok = push_server(&server->intr, serial);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ struct sc_server_params {
|
|||||||
bool downsize_on_error;
|
bool downsize_on_error;
|
||||||
bool tcpip;
|
bool tcpip;
|
||||||
const char *tcpip_dst;
|
const char *tcpip_dst;
|
||||||
|
bool select_usb;
|
||||||
|
bool select_tcpip;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_server {
|
struct sc_server {
|
||||||
|
@ -176,7 +176,7 @@ sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned flags,
|
|||||||
bool
|
bool
|
||||||
sc_process_terminate(pid_t pid) {
|
sc_process_terminate(pid_t pid) {
|
||||||
if (pid <= 0) {
|
if (pid <= 0) {
|
||||||
LOGC("Requested to kill %d, this is an error. Please report the bug.\n",
|
LOGE("Requested to kill %d, this is an error. Please report the bug.\n",
|
||||||
(int) pid);
|
(int) pid);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ sc_aoa_start(struct sc_aoa *aoa) {
|
|||||||
|
|
||||||
bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "scrcpy-aoa", aoa);
|
bool ok = sc_thread_create(&aoa->thread, run_aoa_thread, "scrcpy-aoa", aoa);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start AOA thread");
|
LOGE("Could not start AOA thread");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ scrcpy_otg(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
// Minimal SDL initialization
|
// Minimal SDL initialization
|
||||||
if (SDL_Init(SDL_INIT_EVENTS)) {
|
if (SDL_Init(SDL_INIT_EVENTS)) {
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
LOGE("Could not initialize SDL: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,50 +83,17 @@ scrcpy_otg(struct scrcpy_options *options) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_usb_device usb_devices[16];
|
struct sc_usb_device usb_device;
|
||||||
ssize_t count = sc_usb_find_devices(&s->usb, serial, usb_devices,
|
ok = sc_usb_select_device(&s->usb, serial, &usb_device);
|
||||||
ARRAY_LEN(usb_devices));
|
if (!ok) {
|
||||||
if (count < 0) {
|
|
||||||
LOGE("Could not list USB devices");
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == 0) {
|
LOGI("USB device: %s (%04x:%04x) %s %s", usb_device.serial,
|
||||||
if (serial) {
|
(unsigned) usb_device.vid, (unsigned) usb_device.pid,
|
||||||
LOGE("Could not find USB device %s", serial);
|
usb_device.manufacturer, usb_device.product);
|
||||||
} else {
|
|
||||||
LOGE("Could not find any USB device");
|
|
||||||
}
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 1) {
|
ok = sc_usb_connect(&s->usb, usb_device.device, &cbs, NULL);
|
||||||
if (serial) {
|
|
||||||
LOGE("Multiple (%d) USB devices with serial %s:", (int) count,
|
|
||||||
serial);
|
|
||||||
} else {
|
|
||||||
LOGE("Multiple (%d) USB devices:", (int) count);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < (size_t) count; ++i) {
|
|
||||||
struct sc_usb_device *d = &usb_devices[i];
|
|
||||||
LOGE(" %-18s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
|
||||||
d->serial, d->vid, d->pid, d->manufacturer, d->product);
|
|
||||||
}
|
|
||||||
if (!serial) {
|
|
||||||
LOGE("Specify the device via -s or --serial");
|
|
||||||
}
|
|
||||||
sc_usb_device_destroy_all(usb_devices, count);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
usb_device_initialized = true;
|
|
||||||
|
|
||||||
struct sc_usb_device *usb_device = &usb_devices[0];
|
|
||||||
|
|
||||||
LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
|
||||||
usb_device->serial, usb_device->vid, usb_device->pid,
|
|
||||||
usb_device->manufacturer, usb_device->product);
|
|
||||||
|
|
||||||
ok = sc_usb_connect(&s->usb, usb_device->device, &cbs, NULL);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -173,7 +140,7 @@ scrcpy_otg(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
const char *window_title = options->window_title;
|
const char *window_title = options->window_title;
|
||||||
if (!window_title) {
|
if (!window_title) {
|
||||||
window_title = usb_device->product ? usb_device->product : "scrcpy";
|
window_title = usb_device.product ? usb_device.product : "scrcpy";
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_screen_otg_params params = {
|
struct sc_screen_otg_params params = {
|
||||||
@ -192,7 +159,7 @@ scrcpy_otg(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// usb_device not needed anymore
|
// usb_device not needed anymore
|
||||||
sc_usb_device_destroy(usb_device);
|
sc_usb_device_destroy(&usb_device);
|
||||||
usb_device_initialized = false;
|
usb_device_initialized = false;
|
||||||
|
|
||||||
ret = event_loop(s);
|
ret = event_loop(s);
|
||||||
@ -223,7 +190,7 @@ end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (usb_device_initialized) {
|
if (usb_device_initialized) {
|
||||||
sc_usb_device_destroy(usb_device);
|
sc_usb_device_destroy(&usb_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_usb_destroy(&s->usb);
|
sc_usb_destroy(&s->usb);
|
||||||
|
@ -25,8 +25,7 @@ read_string(libusb_device_handle *handle, uint8_t desc_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
accept_device(libusb_device *device, const char *serial,
|
sc_usb_read_device(libusb_device *device, struct sc_usb_device *out) {
|
||||||
struct sc_usb_device *out) {
|
|
||||||
// Do not log any USB error in this function, it is expected that many USB
|
// Do not log any USB error in this function, it is expected that many USB
|
||||||
// devices available on the computer have permission restrictions
|
// devices available on the computer have permission restrictions
|
||||||
|
|
||||||
@ -39,6 +38,11 @@ accept_device(libusb_device *device, const char *serial,
|
|||||||
libusb_device_handle *handle;
|
libusb_device_handle *handle;
|
||||||
result = libusb_open(device, &handle);
|
result = libusb_open(device, &handle);
|
||||||
if (result < 0) {
|
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 %04x:%04x: libusb error: %s",
|
||||||
|
(unsigned) desc.idVendor, (unsigned) desc.idProduct,
|
||||||
|
libusb_strerror(result));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,22 +52,13 @@ accept_device(libusb_device *device, const char *serial,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serial) {
|
|
||||||
// Filter by serial
|
|
||||||
bool matches = !strcmp(serial, device_serial);
|
|
||||||
if (!matches) {
|
|
||||||
free(device_serial);
|
|
||||||
libusb_close(handle);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out->device = libusb_ref_device(device);
|
out->device = libusb_ref_device(device);
|
||||||
out->serial = device_serial;
|
out->serial = device_serial;
|
||||||
out->vid = desc.idVendor;
|
out->vid = desc.idVendor;
|
||||||
out->pid = desc.idProduct;
|
out->pid = desc.idProduct;
|
||||||
out->manufacturer = read_string(handle, desc.iManufacturer);
|
out->manufacturer = read_string(handle, desc.iManufacturer);
|
||||||
out->product = read_string(handle, desc.iProduct);
|
out->product = read_string(handle, desc.iProduct);
|
||||||
|
out->selected = false;
|
||||||
|
|
||||||
libusb_close(handle);
|
libusb_close(handle);
|
||||||
|
|
||||||
@ -72,22 +67,33 @@ accept_device(libusb_device *device, const char *serial,
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_usb_device_destroy(struct sc_usb_device *usb_device) {
|
sc_usb_device_destroy(struct sc_usb_device *usb_device) {
|
||||||
libusb_unref_device(usb_device->device);
|
if (usb_device->device) {
|
||||||
|
libusb_unref_device(usb_device->device);
|
||||||
|
}
|
||||||
free(usb_device->serial);
|
free(usb_device->serial);
|
||||||
free(usb_device->manufacturer);
|
free(usb_device->manufacturer);
|
||||||
free(usb_device->product);
|
free(usb_device->product);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_usb_device_destroy_all(struct sc_usb_device *usb_devices, size_t count) {
|
sc_usb_device_move(struct sc_usb_device *dst, struct sc_usb_device *src) {
|
||||||
|
*dst = *src;
|
||||||
|
src->device = NULL;
|
||||||
|
src->serial = NULL;
|
||||||
|
src->manufacturer = NULL;
|
||||||
|
src->product = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_usb_devices_destroy_all(struct sc_usb_device *usb_devices, size_t count) {
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
sc_usb_device_destroy(&usb_devices[i]);
|
sc_usb_device_destroy(&usb_devices[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
static ssize_t
|
||||||
sc_usb_find_devices(struct sc_usb *usb, const char *serial,
|
sc_usb_list_devices(struct sc_usb *usb, struct sc_usb_device *devices,
|
||||||
struct sc_usb_device *devices, size_t len) {
|
size_t len) {
|
||||||
libusb_device **list;
|
libusb_device **list;
|
||||||
ssize_t count = libusb_get_device_list(usb->context, &list);
|
ssize_t count = libusb_get_device_list(usb->context, &list);
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
@ -99,7 +105,7 @@ sc_usb_find_devices(struct sc_usb *usb, const char *serial,
|
|||||||
for (size_t i = 0; i < (size_t) count && idx < len; ++i) {
|
for (size_t i = 0; i < (size_t) count && idx < len; ++i) {
|
||||||
libusb_device *device = list[i];
|
libusb_device *device = list[i];
|
||||||
|
|
||||||
if (accept_device(device, serial, &devices[idx])) {
|
if (sc_usb_read_device(device, &devices[idx])) {
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,6 +114,102 @@ sc_usb_find_devices(struct sc_usb *usb, const char *serial,
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_usb_accept_device(const struct sc_usb_device *device, const char *serial) {
|
||||||
|
if (!serial) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !strcmp(serial, device->serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
sc_usb_devices_select(struct sc_usb_device *devices, size_t len,
|
||||||
|
const char *serial, size_t *idx_out) {
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
struct sc_usb_device *device = &devices[i];
|
||||||
|
device->selected = sc_usb_accept_device(device, serial);
|
||||||
|
if (device->selected) {
|
||||||
|
if (idx_out && !count) {
|
||||||
|
*idx_out = i;
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_usb_devices_log(enum sc_log_level level, struct sc_usb_device *devices,
|
||||||
|
size_t count) {
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
struct sc_usb_device *d = &devices[i];
|
||||||
|
const char *selection = d->selected ? "-->" : " ";
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_usb_select_device(struct sc_usb *usb, const char *serial,
|
||||||
|
struct sc_usb_device *out_device) {
|
||||||
|
struct sc_usb_device usb_devices[16];
|
||||||
|
ssize_t count =
|
||||||
|
sc_usb_list_devices(usb, usb_devices, ARRAY_LEN(usb_devices));
|
||||||
|
if (count == -1) {
|
||||||
|
LOGE("Could not list USB devices");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
LOGE("Could not find any USB device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sel_idx; // index of the single matching device if sel_count == 1
|
||||||
|
size_t sel_count =
|
||||||
|
sc_usb_devices_select(usb_devices, count, serial, &sel_idx);
|
||||||
|
|
||||||
|
if (sel_count == 0) {
|
||||||
|
// if count > 0 && sel_count == 0, then necessarily a serial is provided
|
||||||
|
assert(serial);
|
||||||
|
LOGE("Could not find USB device %s", serial);
|
||||||
|
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, usb_devices, count);
|
||||||
|
sc_usb_devices_destroy_all(usb_devices, count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sel_count > 1) {
|
||||||
|
if (serial) {
|
||||||
|
LOGE("Multiple (%" SC_PRIsizet ") USB devices with serial %s:",
|
||||||
|
sel_count, serial);
|
||||||
|
} else {
|
||||||
|
LOGE("Multiple (%" SC_PRIsizet ") USB devices:", sel_count);
|
||||||
|
}
|
||||||
|
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, usb_devices, count);
|
||||||
|
if (!serial) {
|
||||||
|
LOGE("Specify the device via -s or --serial");
|
||||||
|
}
|
||||||
|
sc_usb_devices_destroy_all(usb_devices, count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
|
||||||
|
struct sc_usb_device *device = &usb_devices[sel_idx];
|
||||||
|
|
||||||
|
LOGD("USB device found:");
|
||||||
|
sc_usb_devices_log(SC_LOG_LEVEL_DEBUG, usb_devices, count);
|
||||||
|
|
||||||
|
// Move device into out_device (do not destroy device)
|
||||||
|
sc_usb_device_move(out_device, device);
|
||||||
|
sc_usb_devices_destroy_all(usb_devices, count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_usb_init(struct sc_usb *usb) {
|
sc_usb_init(struct sc_usb *usb) {
|
||||||
usb->handle = NULL;
|
usb->handle = NULL;
|
||||||
@ -119,7 +221,7 @@ sc_usb_destroy(struct sc_usb *usb) {
|
|||||||
libusb_exit(usb->context);
|
libusb_exit(usb->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static LIBUSB_CALL int
|
||||||
sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
|
sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
|
||||||
libusb_hotplug_event event, void *userdata) {
|
libusb_hotplug_event event, void *userdata) {
|
||||||
(void) ctx;
|
(void) ctx;
|
||||||
|
@ -35,13 +35,26 @@ struct sc_usb_device {
|
|||||||
char *product;
|
char *product;
|
||||||
uint16_t vid;
|
uint16_t vid;
|
||||||
uint16_t pid;
|
uint16_t pid;
|
||||||
|
bool selected;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_usb_device_destroy(struct sc_usb_device *usb_device);
|
sc_usb_device_destroy(struct sc_usb_device *usb_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move src to dest
|
||||||
|
*
|
||||||
|
* After this call, the content of src is undefined, except that
|
||||||
|
* sc_usb_device_destroy() can be called.
|
||||||
|
*
|
||||||
|
* This is useful to take a device from a list that will be destroyed, without
|
||||||
|
* making unnecessary copies.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
sc_usb_device_destroy_all(struct sc_usb_device *usb_devices, size_t count);
|
sc_usb_device_move(struct sc_usb_device *dst, struct sc_usb_device *src);
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_usb_devices_destroy_all(struct sc_usb_device *usb_devices, size_t count);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_usb_init(struct sc_usb *usb);
|
sc_usb_init(struct sc_usb *usb);
|
||||||
@ -49,9 +62,9 @@ sc_usb_init(struct sc_usb *usb);
|
|||||||
void
|
void
|
||||||
sc_usb_destroy(struct sc_usb *usb);
|
sc_usb_destroy(struct sc_usb *usb);
|
||||||
|
|
||||||
ssize_t
|
bool
|
||||||
sc_usb_find_devices(struct sc_usb *usb, const char *serial,
|
sc_usb_select_device(struct sc_usb *usb, const char *serial,
|
||||||
struct sc_usb_device *devices, size_t len);
|
struct sc_usb_device *out_device);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
||||||
|
@ -55,6 +55,16 @@ sc_get_log_level(void) {
|
|||||||
return log_level_sdl_to_sc(sdl_log);
|
return log_level_sdl_to_sc(sdl_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_log(enum sc_log_level level, const char *fmt, ...) {
|
||||||
|
SDL_LogPriority sdl_level = log_level_sc_to_sdl(level);
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, sdl_level, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
bool
|
bool
|
||||||
sc_log_windows_error(const char *prefix, int error) {
|
sc_log_windows_error(const char *prefix, int error) {
|
||||||
|
@ -15,10 +15,9 @@
|
|||||||
#define LOGI(...) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
#define LOGI(...) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
||||||
#define LOGW(...) SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
#define LOGW(...) SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
||||||
#define LOGE(...) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
#define LOGE(...) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
||||||
#define LOGC(...) SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
|
|
||||||
|
|
||||||
#define LOG_OOM() \
|
#define LOG_OOM() \
|
||||||
LOGC("OOM: %s:%d %s()", __FILE__, __LINE__, __func__)
|
LOGE("OOM: %s:%d %s()", __FILE__, __LINE__, __func__)
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_set_log_level(enum sc_log_level level);
|
sc_set_log_level(enum sc_log_level level);
|
||||||
@ -26,6 +25,10 @@ sc_set_log_level(enum sc_log_level level);
|
|||||||
enum sc_log_level
|
enum sc_log_level
|
||||||
sc_get_log_level(void);
|
sc_get_log_level(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_log(enum sc_log_level level, const char *fmt, ...);
|
||||||
|
#define LOG(LEVEL, ...) sc_log((LEVEL), __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Log system error (typically returned by GetLastError() or similar)
|
// Log system error (typically returned by GetLastError() or similar)
|
||||||
bool
|
bool
|
||||||
|
@ -33,7 +33,7 @@ net_init(void) {
|
|||||||
WSADATA wsa;
|
WSADATA wsa;
|
||||||
int res = WSAStartup(MAKEWORD(2, 2), &wsa) < 0;
|
int res = WSAStartup(MAKEWORD(2, 2), &wsa) < 0;
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
LOGC("WSAStartup failed with error %d", res);
|
LOGE("WSAStartup failed with error %d", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
# include <winsock2.h>
|
# include <winsock2.h>
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
# define SC_PRIexitcode "lu"
|
# define SC_PRIexitcode "lu"
|
||||||
// <https://stackoverflow.com/a/44383330/1987178>
|
|
||||||
# define SC_PRIsizet "Iu"
|
|
||||||
# define SC_PROCESS_NONE NULL
|
# define SC_PROCESS_NONE NULL
|
||||||
# define SC_EXIT_CODE_NONE -1UL // max value as unsigned long
|
# define SC_EXIT_CODE_NONE -1UL // max value as unsigned long
|
||||||
typedef HANDLE sc_pid;
|
typedef HANDLE sc_pid;
|
||||||
@ -23,7 +21,6 @@
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# define SC_PRIsizet "zu"
|
|
||||||
# define SC_PRIexitcode "d"
|
# define SC_PRIexitcode "d"
|
||||||
# define SC_PROCESS_NONE -1
|
# define SC_PROCESS_NONE -1
|
||||||
# define SC_EXIT_CODE_NONE -1
|
# define SC_EXIT_CODE_NONE -1
|
||||||
|
@ -54,7 +54,7 @@ sc_mutex_lock(sc_mutex *mutex) {
|
|||||||
int r = SDL_LockMutex(mutex->mutex);
|
int r = SDL_LockMutex(mutex->mutex);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (r) {
|
if (r) {
|
||||||
LOGC("Could not lock mutex: %s", SDL_GetError());
|
LOGE("Could not lock mutex: %s", SDL_GetError());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ sc_mutex_unlock(sc_mutex *mutex) {
|
|||||||
int r = SDL_UnlockMutex(mutex->mutex);
|
int r = SDL_UnlockMutex(mutex->mutex);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (r) {
|
if (r) {
|
||||||
LOGC("Could not lock mutex: %s", SDL_GetError());
|
LOGE("Could not lock mutex: %s", SDL_GetError());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -118,7 +118,7 @@ sc_cond_wait(sc_cond *cond, sc_mutex *mutex) {
|
|||||||
int r = SDL_CondWait(cond->cond, mutex->mutex);
|
int r = SDL_CondWait(cond->cond, mutex->mutex);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (r) {
|
if (r) {
|
||||||
LOGC("Could not wait on condition: %s", SDL_GetError());
|
LOGE("Could not wait on condition: %s", SDL_GetError());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline) {
|
|||||||
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms);
|
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
LOGC("Could not wait on condition with timeout: %s", SDL_GetError());
|
LOGE("Could not wait on condition with timeout: %s", SDL_GetError());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ sc_cond_signal(sc_cond *cond) {
|
|||||||
int r = SDL_CondSignal(cond->cond);
|
int r = SDL_CondSignal(cond->cond);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (r) {
|
if (r) {
|
||||||
LOGC("Could not signal a condition: %s", SDL_GetError());
|
LOGE("Could not signal a condition: %s", SDL_GetError());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -169,7 +169,7 @@ sc_cond_broadcast(sc_cond *cond) {
|
|||||||
int r = SDL_CondBroadcast(cond->cond);
|
int r = SDL_CondBroadcast(cond->cond);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (r) {
|
if (r) {
|
||||||
LOGC("Could not broadcast a condition: %s", SDL_GetError());
|
LOGE("Could not broadcast a condition: %s", SDL_GetError());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -274,7 +274,7 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
|
|||||||
LOGD("Starting v4l2 thread");
|
LOGD("Starting v4l2 thread");
|
||||||
ok = sc_thread_create(&vs->thread, run_v4l2_sink, "scrcpy-v4l2", vs);
|
ok = sc_thread_create(&vs->thread, run_v4l2_sink, "scrcpy-v4l2", vs);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not start v4l2 thread");
|
LOGE("Could not start v4l2 thread");
|
||||||
goto error_av_packet_free;
|
goto error_av_packet_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,158 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "adb/adb_device.h"
|
||||||
#include "adb/adb_parser.h"
|
#include "adb/adb_parser.h"
|
||||||
|
|
||||||
|
static void test_adb_devices() {
|
||||||
|
char output[] =
|
||||||
|
"List of devices attached\n"
|
||||||
|
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
"device:MyDevice transport_id:1\n"
|
||||||
|
"192.168.1.1:5555 device product:MyWifiProduct model:MyWifiModel "
|
||||||
|
"device:MyWifiDevice trandport_id:2\n";
|
||||||
|
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 2);
|
||||||
|
|
||||||
|
struct sc_adb_device *device = &devices[0];
|
||||||
|
assert(!strcmp("0123456789abcdef", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyModel", device->model));
|
||||||
|
|
||||||
|
device = &devices[1];
|
||||||
|
assert(!strcmp("192.168.1.1:5555", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyWifiModel", device->model));
|
||||||
|
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_cr() {
|
||||||
|
char output[] =
|
||||||
|
"List of devices attached\r\n"
|
||||||
|
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
"device:MyDevice transport_id:1\r\n"
|
||||||
|
"192.168.1.1:5555 device product:MyWifiProduct model:MyWifiModel "
|
||||||
|
"device:MyWifiDevice trandport_id:2\r\n";
|
||||||
|
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 2);
|
||||||
|
|
||||||
|
struct sc_adb_device *device = &devices[0];
|
||||||
|
assert(!strcmp("0123456789abcdef", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyModel", device->model));
|
||||||
|
|
||||||
|
device = &devices[1];
|
||||||
|
assert(!strcmp("192.168.1.1:5555", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyWifiModel", device->model));
|
||||||
|
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_daemon_start() {
|
||||||
|
char output[] =
|
||||||
|
"* daemon not running; starting now at tcp:5037\n"
|
||||||
|
"* daemon started successfully\n"
|
||||||
|
"List of devices attached\n"
|
||||||
|
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
"device:MyDevice transport_id:1\n";
|
||||||
|
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 1);
|
||||||
|
|
||||||
|
struct sc_adb_device *device = &devices[0];
|
||||||
|
assert(!strcmp("0123456789abcdef", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyModel", device->model));
|
||||||
|
|
||||||
|
sc_adb_device_destroy(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_daemon_start_mixed() {
|
||||||
|
char output[] =
|
||||||
|
"List of devices attached\n"
|
||||||
|
"adb server version (41) doesn't match this client (39); killing...\n"
|
||||||
|
"* daemon started successfully *\n"
|
||||||
|
"0123456789abcdef unauthorized usb:1-1\n"
|
||||||
|
"87654321 device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
"device:MyDevice\n";
|
||||||
|
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 2);
|
||||||
|
|
||||||
|
struct sc_adb_device *device = &devices[0];
|
||||||
|
assert(!strcmp("0123456789abcdef", device->serial));
|
||||||
|
assert(!strcmp("unauthorized", device->state));
|
||||||
|
fprintf(stderr, "==== [%s]\n", device->model);
|
||||||
|
assert(!device->model);
|
||||||
|
|
||||||
|
device = &devices[1];
|
||||||
|
assert(!strcmp("87654321", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyModel", device->model));
|
||||||
|
|
||||||
|
sc_adb_devices_destroy_all(devices, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_without_eol() {
|
||||||
|
char output[] =
|
||||||
|
"List of devices attached\n"
|
||||||
|
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
"device:MyDevice transport_id:1";
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 1);
|
||||||
|
|
||||||
|
struct sc_adb_device *device = &devices[0];
|
||||||
|
assert(!strcmp("0123456789abcdef", device->serial));
|
||||||
|
assert(!strcmp("device", device->state));
|
||||||
|
assert(!strcmp("MyModel", device->model));
|
||||||
|
|
||||||
|
sc_adb_device_destroy(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_without_header() {
|
||||||
|
char output[] =
|
||||||
|
"0123456789abcdef device usb:2-1 product:MyProduct model:MyModel "
|
||||||
|
"device:MyDevice transport_id:1\n";
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_corrupted() {
|
||||||
|
char output[] =
|
||||||
|
"List of devices attached\n"
|
||||||
|
"corrupted_garbage\n";
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adb_devices_spaces() {
|
||||||
|
char output[] =
|
||||||
|
"List of devices attached\n"
|
||||||
|
"0123456789abcdef unauthorized usb:1-4 transport_id:3\n";
|
||||||
|
|
||||||
|
struct sc_adb_device devices[16];
|
||||||
|
ssize_t count = sc_adb_parse_devices(output, devices, ARRAY_LEN(devices));
|
||||||
|
assert(count == 1);
|
||||||
|
|
||||||
|
struct sc_adb_device *device = &devices[0];
|
||||||
|
assert(!strcmp("0123456789abcdef", device->serial));
|
||||||
|
assert(!strcmp("unauthorized", device->state));
|
||||||
|
assert(!device->model);
|
||||||
|
|
||||||
|
sc_adb_device_destroy(device);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_get_ip_single_line() {
|
static void test_get_ip_single_line() {
|
||||||
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
||||||
"192.168.12.34\r\r\n";
|
"192.168.12.34\r\r\n";
|
||||||
@ -86,6 +236,15 @@ int main(int argc, char *argv[]) {
|
|||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
|
|
||||||
|
test_adb_devices();
|
||||||
|
test_adb_devices_cr();
|
||||||
|
test_adb_devices_daemon_start();
|
||||||
|
test_adb_devices_daemon_start_mixed();
|
||||||
|
test_adb_devices_without_eol();
|
||||||
|
test_adb_devices_without_header();
|
||||||
|
test_adb_devices_corrupted();
|
||||||
|
test_adb_devices_spaces();
|
||||||
|
|
||||||
test_get_ip_single_line();
|
test_get_ip_single_line();
|
||||||
test_get_ip_single_line_without_eol();
|
test_get_ip_single_line_without_eol();
|
||||||
test_get_ip_single_line_with_trailing_space();
|
test_get_ip_single_line_with_trailing_space();
|
||||||
|
@ -21,3 +21,5 @@ ffmpeg_avformat = 'avformat-58'
|
|||||||
ffmpeg_avutil = 'avutil-56'
|
ffmpeg_avutil = 'avutil-56'
|
||||||
prebuilt_ffmpeg = 'ffmpeg-win32-4.3.1'
|
prebuilt_ffmpeg = 'ffmpeg-win32-4.3.1'
|
||||||
prebuilt_sdl2 = 'SDL2-2.0.20/i686-w64-mingw32'
|
prebuilt_sdl2 = 'SDL2-2.0.20/i686-w64-mingw32'
|
||||||
|
prebuilt_libusb_root = 'libusb-1.0.25'
|
||||||
|
prebuilt_libusb = prebuilt_libusb_root + '/MinGW32'
|
||||||
|
@ -21,3 +21,5 @@ ffmpeg_avformat = 'avformat-59'
|
|||||||
ffmpeg_avutil = 'avutil-57'
|
ffmpeg_avutil = 'avutil-57'
|
||||||
prebuilt_ffmpeg = 'ffmpeg-win64-5.0'
|
prebuilt_ffmpeg = 'ffmpeg-win64-5.0'
|
||||||
prebuilt_sdl2 = 'SDL2-2.0.20/x86_64-w64-mingw32'
|
prebuilt_sdl2 = 'SDL2-2.0.20/x86_64-w64-mingw32'
|
||||||
|
prebuilt_libusb_root = 'libusb-1.0.25'
|
||||||
|
prebuilt_libusb = prebuilt_libusb_root + '/MinGW64'
|
||||||
|
28
prebuilt-deps/prepare-libusb.sh
Executable file
28
prebuilt-deps/prepare-libusb.sh
Executable 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 /
|
@ -66,6 +66,7 @@ prepare-deps-win32:
|
|||||||
@prebuilt-deps/prepare-adb.sh
|
@prebuilt-deps/prepare-adb.sh
|
||||||
@prebuilt-deps/prepare-sdl.sh
|
@prebuilt-deps/prepare-sdl.sh
|
||||||
@prebuilt-deps/prepare-ffmpeg-win32.sh
|
@prebuilt-deps/prepare-ffmpeg-win32.sh
|
||||||
|
@prebuilt-deps/prepare-libusb.sh
|
||||||
|
|
||||||
build-win32: prepare-deps-win32
|
build-win32: prepare-deps-win32
|
||||||
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
|
[ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \
|
||||||
@ -107,6 +108,7 @@ dist-win32: build-server build-win32
|
|||||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/data/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp prebuilt-deps/data/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
|
cp prebuilt-deps/data/libusb-1.0.25/MinGW32/dll/libusb-1.0.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
|
|
||||||
dist-win64: build-server build-win64
|
dist-win64: build-server build-win64
|
||||||
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
|
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||||
@ -125,6 +127,7 @@ dist-win64: build-server build-win64
|
|||||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/data/platform-tools-31.0.3/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/data/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/data/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
|
cp prebuilt-deps/data/libusb-1.0.25/MinGW64/dll/libusb-1.0.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
|
|
||||||
zip-win32: dist-win32
|
zip-win32: dist-win32
|
||||||
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
||||||
|
Reference in New Issue
Block a user