Compare commits
16 Commits
libusb-mac
...
illegalarg
Author | SHA1 | Date | |
---|---|---|---|
71b41d846f | |||
e2e76c5d48 | |||
4b8cb042c4 | |||
1790e88278 | |||
c070723bc8 | |||
36c75e15b8 | |||
d9bc5082ab | |||
73a5311ac6 | |||
25296ae167 | |||
3bb24b3926 | |||
6ee75c0cff | |||
6b65cd405a | |||
ff3cb31cb4 | |||
06243e7c3c | |||
b9b2879789 | |||
be1936bb85 |
6
BUILD.md
6
BUILD.md
@ -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 \
|
||||
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
@ -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')
|
||||
]
|
||||
|
||||
@ -211,7 +224,7 @@ executable('scrcpy', src,
|
||||
c_args: [])
|
||||
|
||||
install_man('scrcpy.1')
|
||||
install_data('../data/icon.png',
|
||||
install_data('data/icon.png',
|
||||
rename: 'scrcpy.png',
|
||||
install_dir: 'share/icons/hicolor/256x256/apps')
|
||||
|
||||
@ -269,6 +282,9 @@ if get_option('buildtype') == 'debug'
|
||||
'src/util/str.c',
|
||||
'src/util/strbuf.c',
|
||||
]],
|
||||
['test_vector', [
|
||||
'tests/test_vector.c',
|
||||
]],
|
||||
]
|
||||
|
||||
foreach t : tests
|
||||
|
28
app/prebuilt-deps/prepare-libusb.sh
Executable file
28
app/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 /
|
@ -1,6 +1,6 @@
|
||||
#include <winuser.h>
|
||||
|
||||
0 ICON "../data/icon.ico"
|
||||
0 ICON "data/icon.ico"
|
||||
1 RT_MANIFEST "scrcpy-windows.manifest"
|
||||
2 VERSIONINFO
|
||||
BEGIN
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adb_device.h"
|
||||
#include "adb_parser.h"
|
||||
#include "util/file.h"
|
||||
#include "util/log.h"
|
||||
@ -150,7 +151,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 +159,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 +205,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) {
|
||||
@ -382,45 +393,55 @@ sc_adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags) {
|
||||
return process_check_success_intr(intr, pid, "adb disconnect", flags);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
static bool
|
||||
sc_adb_list_devices(struct sc_intr *intr, unsigned flags,
|
||||
struct sc_adb_device *devices, size_t len) {
|
||||
struct sc_vec_adb_devices *out_vec) {
|
||||
const char *const argv[] = SC_ADB_COMMAND("devices", "-l");
|
||||
|
||||
#define BUFSIZE 65536
|
||||
char *buf = malloc(BUFSIZE);
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf) - 1);
|
||||
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, BUFSIZE - 1);
|
||||
sc_pipe_close(pout);
|
||||
|
||||
bool ok = process_check_success_intr(intr, pid, "adb devices -l", flags);
|
||||
if (!ok) {
|
||||
return -1;
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r == -1) {
|
||||
return -1;
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert((size_t) r < sizeof(buf));
|
||||
if (r == sizeof(buf) - 1) {
|
||||
assert((size_t) r < BUFSIZE);
|
||||
if (r == BUFSIZE - 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. "
|
||||
LOGW("Result of \"adb devices -l\" does not fit in 64Kb. "
|
||||
"Please report an issue.");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
ok = sc_adb_parse_devices(buf, out_vec);
|
||||
free(buf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -519,22 +540,21 @@ 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) {
|
||||
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_list_devices(intr, flags, &vec);
|
||||
if (!ok) {
|
||||
LOGE("Could not list ADB devices");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
if (vec.size == 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);
|
||||
sc_adb_devices_select(vec.data, vec.size, selector, &sel_idx);
|
||||
|
||||
if (sel_count == 0) {
|
||||
// if count > 0 && sel_count == 0, then necessarily a selection is
|
||||
@ -557,8 +577,8 @@ sc_adb_select_device(struct sc_intr *intr,
|
||||
break;
|
||||
}
|
||||
|
||||
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
||||
sc_adb_devices_destroy_all(devices, count);
|
||||
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -584,28 +604,28 @@ sc_adb_select_device(struct sc_intr *intr,
|
||||
assert(!"Unexpected selector type");
|
||||
break;
|
||||
}
|
||||
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, devices, count);
|
||||
sc_adb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
|
||||
LOGE("Select a device via -s (--serial), -d (--select-usb) or -e "
|
||||
"(--select-tcpip)");
|
||||
sc_adb_devices_destroy_all(devices, count);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
|
||||
struct sc_adb_device *device = &devices[sel_idx];
|
||||
struct sc_adb_device *device = &vec.data[sel_idx];
|
||||
|
||||
bool ok = sc_adb_device_check_state(device, devices, count);
|
||||
ok = sc_adb_device_check_state(device, vec.data, vec.size);
|
||||
if (!ok) {
|
||||
sc_adb_devices_destroy_all(devices, count);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("ADB device found:");
|
||||
sc_adb_devices_log(SC_LOG_LEVEL_DEBUG, devices, count);
|
||||
sc_adb_devices_log(SC_LOG_LEVEL_DEBUG, vec.data, vec.size);
|
||||
|
||||
// Move devics into out_device (do not destroy device)
|
||||
sc_adb_device_move(out_device, device);
|
||||
sc_adb_devices_destroy_all(devices, count);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -18,9 +18,10 @@ 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) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
sc_adb_device_destroy(&devices[i]);
|
||||
sc_adb_devices_destroy(struct sc_vec_adb_devices *devices) {
|
||||
for (size_t i = 0; i < devices->size; ++i) {
|
||||
sc_adb_device_destroy(&devices->data[i]);
|
||||
}
|
||||
sc_vector_destroy(devices);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/vector.h"
|
||||
|
||||
struct sc_adb_device {
|
||||
char *serial;
|
||||
char *state;
|
||||
@ -13,6 +15,8 @@ struct sc_adb_device {
|
||||
bool selected;
|
||||
};
|
||||
|
||||
struct sc_vec_adb_devices SC_VECTOR(struct sc_adb_device);
|
||||
|
||||
void
|
||||
sc_adb_device_destroy(struct sc_adb_device *device);
|
||||
|
||||
@ -29,6 +33,6 @@ 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);
|
||||
sc_adb_devices_destroy(struct sc_vec_adb_devices *devices);
|
||||
|
||||
#endif
|
||||
|
@ -109,11 +109,8 @@ sc_adb_parse_device(char *line, struct sc_adb_device *device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
|
||||
size_t devices_len) {
|
||||
size_t dev_count = 0;
|
||||
|
||||
bool
|
||||
sc_adb_parse_devices(char *str, struct sc_vec_adb_devices *out_vec) {
|
||||
#define HEADER "List of devices attached"
|
||||
#define HEADER_LEN (sizeof(HEADER) - 1)
|
||||
bool header_found = false;
|
||||
@ -144,25 +141,24 @@ sc_adb_parse_devices(char *str, struct sc_adb_device *devices,
|
||||
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]);
|
||||
struct sc_adb_device device;
|
||||
bool ok = sc_adb_parse_device(line, &device);
|
||||
if (!ok) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++dev_count;
|
||||
|
||||
assert(dev_count <= devices_len);
|
||||
if (dev_count == devices_len) {
|
||||
// Max number of devices reached
|
||||
break;
|
||||
ok = sc_vector_push(out_vec, device);
|
||||
if (!ok) {
|
||||
LOG_OOM();
|
||||
LOGE("Could not push adb_device to vector");
|
||||
sc_adb_device_destroy(&device);
|
||||
// continue anyway
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!header_found) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dev_count;
|
||||
assert(header_found || out_vec->size == 0);
|
||||
return header_found;
|
||||
}
|
||||
|
||||
static char *
|
||||
|
@ -14,9 +14,8 @@
|
||||
*
|
||||
* 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);
|
||||
bool
|
||||
sc_adb_parse_devices(char *str, struct sc_vec_adb_devices *out_vec);
|
||||
|
||||
/**
|
||||
* Parse the ip from the output of `adb shell ip route`
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -3,6 +3,9 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
struct sc_vec_usb_devices SC_VECTOR(struct sc_usb_device);
|
||||
|
||||
static char *
|
||||
read_string(libusb_device_handle *handle, uint8_t desc_index) {
|
||||
@ -40,8 +43,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;
|
||||
}
|
||||
|
||||
@ -84,33 +88,39 @@ 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) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
sc_usb_device_destroy(&usb_devices[i]);
|
||||
sc_usb_devices_destroy(struct sc_vec_usb_devices *usb_devices) {
|
||||
for (size_t i = 0; i < usb_devices->size; ++i) {
|
||||
sc_usb_device_destroy(&usb_devices->data[i]);
|
||||
}
|
||||
sc_vector_destroy(usb_devices);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sc_usb_list_devices(struct sc_usb *usb, struct sc_usb_device *devices,
|
||||
size_t len) {
|
||||
static bool
|
||||
sc_usb_list_devices(struct sc_usb *usb, struct sc_vec_usb_devices *out_vec) {
|
||||
libusb_device **list;
|
||||
ssize_t count = libusb_get_device_list(usb->context, &list);
|
||||
if (count < 0) {
|
||||
LOGE("List USB devices: libusb error: %s", libusb_strerror(count));
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < (size_t) count && idx < len; ++i) {
|
||||
for (size_t i = 0; i < (size_t) count; ++i) {
|
||||
libusb_device *device = list[i];
|
||||
|
||||
if (sc_usb_read_device(device, &devices[idx])) {
|
||||
++idx;
|
||||
struct sc_usb_device usb_device;
|
||||
if (sc_usb_read_device(device, &usb_device)) {
|
||||
bool ok = sc_vector_push(out_vec, usb_device);
|
||||
if (!ok) {
|
||||
LOG_OOM();
|
||||
LOGE("Could not push usb_device to vector");
|
||||
sc_usb_device_destroy(&usb_device);
|
||||
// continue anyway
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
return idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -146,37 +156,38 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
struct sc_vec_usb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_usb_list_devices(usb, &vec);
|
||||
if (!ok) {
|
||||
LOGE("Could not list USB devices");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
if (vec.size == 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);
|
||||
sc_usb_devices_select(vec.data, vec.size, 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);
|
||||
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
|
||||
sc_usb_devices_destroy(&vec);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -187,21 +198,21 @@ sc_usb_select_device(struct sc_usb *usb, const char *serial,
|
||||
} else {
|
||||
LOGE("Multiple (%" SC_PRIsizet ") USB devices:", sel_count);
|
||||
}
|
||||
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, usb_devices, count);
|
||||
sc_usb_devices_log(SC_LOG_LEVEL_ERROR, vec.data, vec.size);
|
||||
LOGE("Select a device via -s (--serial)");
|
||||
sc_usb_devices_destroy_all(usb_devices, count);
|
||||
sc_usb_devices_destroy(&vec);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(sel_count == 1); // sel_idx is valid only if sel_count == 1
|
||||
struct sc_usb_device *device = &usb_devices[sel_idx];
|
||||
struct sc_usb_device *device = &vec.data[sel_idx];
|
||||
|
||||
LOGD("USB device found:");
|
||||
sc_usb_devices_log(SC_LOG_LEVEL_DEBUG, usb_devices, count);
|
||||
sc_usb_devices_log(SC_LOG_LEVEL_DEBUG, vec.data, vec.size);
|
||||
|
||||
// Move device into out_device (do not destroy device)
|
||||
sc_usb_device_move(out_device, device);
|
||||
sc_usb_devices_destroy_all(usb_devices, count);
|
||||
sc_usb_devices_destroy(&vec);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -216,7 +227,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 +261,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 +335,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 +346,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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
539
app/src/util/vector.h
Normal file
539
app/src/util/vector.h
Normal file
@ -0,0 +1,539 @@
|
||||
#ifndef SC_VECTOR_H
|
||||
#define SC_VECTOR_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Adapted from vlc_vector:
|
||||
// <https://code.videolan.org/videolan/vlc/-/blob/0857947abaed9c89810cd96353aaa1b7e6ba3b0d/include/vlc_vector.h>
|
||||
|
||||
/**
|
||||
* Vector struct body
|
||||
*
|
||||
* A vector is a dynamic array, managed by the sc_vector_* helpers.
|
||||
*
|
||||
* It is generic over the type of its items, so it is implemented as macros.
|
||||
*
|
||||
* To use a vector, a new type must be defined:
|
||||
*
|
||||
* struct vec_int SC_VECTOR(int);
|
||||
*
|
||||
* The struct may be anonymous:
|
||||
*
|
||||
* struct SC_VECTOR(const char *) names;
|
||||
*
|
||||
* Vector size is accessible via `vec.size`, and items are intended to be
|
||||
* accessed directly, via `vec.data[i]`.
|
||||
*
|
||||
* Functions and macros having name ending with '_' are private.
|
||||
*/
|
||||
#define SC_VECTOR(type) \
|
||||
{ \
|
||||
size_t cap; \
|
||||
size_t size; \
|
||||
type *data; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Static initializer for a vector
|
||||
*/
|
||||
#define SC_VECTOR_INITIALIZER { 0, 0, NULL }
|
||||
|
||||
/**
|
||||
* Initialize an empty vector
|
||||
*/
|
||||
#define sc_vector_init(pv) \
|
||||
({ \
|
||||
(pv)->cap = 0; \
|
||||
(pv)->size = 0; \
|
||||
(pv)->data = NULL; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Destroy a vector
|
||||
*
|
||||
* The vector may not be used anymore unless sc_vector_init() is called.
|
||||
*/
|
||||
#define sc_vector_destroy(pv) \
|
||||
free((pv)->data)
|
||||
|
||||
/**
|
||||
* Clear a vector
|
||||
*
|
||||
* Remove all items from the vector.
|
||||
*/
|
||||
#define sc_vector_clear(pv) \
|
||||
({ \
|
||||
sc_vector_destroy(pv); \
|
||||
sc_vector_init(pv);\
|
||||
})
|
||||
|
||||
/**
|
||||
* The minimal allocation size, in number of items
|
||||
*
|
||||
* Private.
|
||||
*/
|
||||
#define SC_VECTOR_MINCAP_ ((size_t) 10)
|
||||
|
||||
static inline size_t
|
||||
sc_vector_min_(size_t a, size_t b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
sc_vector_max_(size_t a, size_t b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
sc_vector_clamp_(size_t x, size_t min, size_t max)
|
||||
{
|
||||
return sc_vector_max_(min, sc_vector_min_(max, x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Realloc data and update vector fields
|
||||
*
|
||||
* On reallocation success, update the vector capacity (*pcap) and size
|
||||
* (*psize), and return the reallocated data.
|
||||
*
|
||||
* On reallocation failure, return NULL without any change.
|
||||
*
|
||||
* Private.
|
||||
*
|
||||
* \param ptr the current `data` field of the vector to realloc
|
||||
* \param count the requested capacity, in number of items
|
||||
* \param size the size of one item
|
||||
* \param pcap a pointer to the `cap` field of the vector [IN/OUT]
|
||||
* \param psize a pointer to the `size` field of the vector [IN/OUT]
|
||||
* \return the new ptr on success, NULL on error
|
||||
*/
|
||||
static inline void *
|
||||
sc_vector_reallocdata_(void *ptr, size_t count, size_t size,
|
||||
size_t *restrict pcap, size_t *restrict psize)
|
||||
{
|
||||
void *p = realloc(ptr, count * size);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*pcap = count;
|
||||
*psize = sc_vector_min_(*psize, count);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define sc_vector_realloc_(pv, newcap) \
|
||||
({ \
|
||||
void *p = sc_vector_reallocdata_((pv)->data, newcap, sizeof(*(pv)->data), \
|
||||
&(pv)->cap, &(pv)->size); \
|
||||
if (p) { \
|
||||
(pv)->data = p; \
|
||||
} \
|
||||
(bool) p; \
|
||||
});
|
||||
|
||||
#define sc_vector_resize_(pv, newcap) \
|
||||
({ \
|
||||
bool ok; \
|
||||
if ((pv)->cap == (newcap)) { \
|
||||
ok = true; \
|
||||
} else if ((newcap) > 0) { \
|
||||
ok = sc_vector_realloc_(pv, (newcap)); \
|
||||
} else { \
|
||||
sc_vector_clear(pv); \
|
||||
ok = true; \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
static inline size_t
|
||||
sc_vector_growsize_(size_t value)
|
||||
{
|
||||
/* integer multiplication by 1.5 */
|
||||
return value + (value >> 1);
|
||||
}
|
||||
|
||||
/* SIZE_MAX/2 to fit in ssize_t, and so that cap*1.5 does not overflow. */
|
||||
#define sc_vector_max_cap_(pv) (SIZE_MAX / 2 / sizeof(*(pv)->data))
|
||||
|
||||
/**
|
||||
* Increase the capacity of the vector to at least `mincap`
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param mincap (size_t) the requested capacity
|
||||
* \retval true if no allocation failed
|
||||
* \retval false on allocation failure (the vector is left untouched)
|
||||
*/
|
||||
#define sc_vector_reserve(pv, mincap) \
|
||||
({ \
|
||||
bool ok; \
|
||||
/* avoid to allocate tiny arrays (< SC_VECTOR_MINCAP_) */ \
|
||||
size_t mincap_ = sc_vector_max_(mincap, SC_VECTOR_MINCAP_); \
|
||||
if (mincap_ <= (pv)->cap) { \
|
||||
/* nothing to do */ \
|
||||
ok = true; \
|
||||
} else if (mincap_ <= sc_vector_max_cap_(pv)) { \
|
||||
/* not too big */ \
|
||||
size_t newsize = sc_vector_growsize_((pv)->cap); \
|
||||
newsize = sc_vector_clamp_(newsize, mincap_, sc_vector_max_cap_(pv)); \
|
||||
ok = sc_vector_realloc_(pv, newsize); \
|
||||
} else { \
|
||||
ok = false; \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
#define sc_vector_shrink_to_fit(pv) \
|
||||
/* decreasing the size may not fail */ \
|
||||
(void) sc_vector_resize_(pv, (pv)->size)
|
||||
|
||||
/**
|
||||
* Resize the vector down automatically
|
||||
*
|
||||
* Shrink only when necessary (in practice when cap > (size+5)*1.5)
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
*/
|
||||
#define sc_vector_autoshrink(pv) \
|
||||
({ \
|
||||
bool must_shrink = \
|
||||
/* do not shrink to tiny size */ \
|
||||
(pv)->cap > SC_VECTOR_MINCAP_ && \
|
||||
/* no need to shrink */ \
|
||||
(pv)->cap >= sc_vector_growsize_((pv)->size + 5); \
|
||||
if (must_shrink) { \
|
||||
size_t newsize = sc_vector_max_((pv)->size + 5, SC_VECTOR_MINCAP_); \
|
||||
sc_vector_resize_(pv, newsize); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define sc_vector_check_same_ptr_type_(a, b) \
|
||||
(void) ((a) == (b)) /* warn on type mismatch */
|
||||
|
||||
/**
|
||||
* Push an item at the end of the vector
|
||||
*
|
||||
* The amortized complexity is O(1).
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param item the item to append
|
||||
* \retval true if no allocation failed
|
||||
* \retval false on allocation failure (the vector is left untouched)
|
||||
*/
|
||||
#define sc_vector_push(pv, item) \
|
||||
({ \
|
||||
bool ok = sc_vector_reserve(pv, (pv)->size + 1); \
|
||||
if (ok) { \
|
||||
(pv)->data[(pv)->size++] = (item); \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Append `count` items at the end of the vector
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param items the items array to append
|
||||
* \param count the number of items in the array
|
||||
* \retval true if no allocation failed
|
||||
* \retval false on allocation failure (the vector is left untouched)
|
||||
*/
|
||||
#define sc_vector_push_all(pv, items, count) \
|
||||
sc_vector_push_all_(pv, items, (size_t) count)
|
||||
|
||||
#define sc_vector_push_all_(pv, items, count) \
|
||||
({ \
|
||||
sc_vector_check_same_ptr_type_((pv)->data, items); \
|
||||
bool ok = sc_vector_reserve(pv, (pv)->size + (count)); \
|
||||
if (ok) { \
|
||||
memcpy(&(pv)->data[(pv)->size], items, (count) * sizeof(*(pv)->data)); \
|
||||
(pv)->size += count; \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Insert an hole of size `count` to the given index
|
||||
*
|
||||
* The items in range [index; size-1] will be moved. The items in the hole are
|
||||
* left uninitialized.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index where the hole is to be inserted
|
||||
* \param count the number of items in the hole
|
||||
* \retval true if no allocation failed
|
||||
* \retval false on allocation failure (the vector is left untouched)
|
||||
*/
|
||||
#define sc_vector_insert_hole(pv, index, count) \
|
||||
sc_vector_insert_hole_(pv, (size_t) index, (size_t) count);
|
||||
|
||||
#define sc_vector_insert_hole_(pv, index, count) \
|
||||
({ \
|
||||
bool ok = sc_vector_reserve(pv, (pv)->size + (count)); \
|
||||
if (ok) { \
|
||||
if ((index) < (pv)->size) { \
|
||||
memmove(&(pv)->data[(index) + (count)], \
|
||||
&(pv)->data[(index)], \
|
||||
((pv)->size - (index)) * sizeof(*(pv)->data)); \
|
||||
} \
|
||||
(pv)->size += count; \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Insert an item at the given index
|
||||
*
|
||||
* The items in range [index; size-1] will be moved.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index where the item is to be inserted
|
||||
* \param item the item to append
|
||||
* \retval true if no allocation failed
|
||||
* \retval false on allocation failure (the vector is left untouched)
|
||||
*/
|
||||
#define sc_vector_insert(pv, index, item) \
|
||||
sc_vector_insert_(pv, (size_t) index, (size_t) item);
|
||||
|
||||
#define sc_vector_insert_(pv, index, item) \
|
||||
({ \
|
||||
bool ok = sc_vector_insert_hole_(pv, index, 1); \
|
||||
if (ok) { \
|
||||
(pv)->data[index] = (item); \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Insert `count` items at the given index
|
||||
*
|
||||
* The items in range [index; size-1] will be moved.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index where the items are to be inserted
|
||||
* \param items the items array to append
|
||||
* \param count the number of items in the array
|
||||
* \retval true if no allocation failed
|
||||
* \retval false on allocation failure (the vector is left untouched)
|
||||
*/
|
||||
#define sc_vector_insert_all(pv, index, items, count) \
|
||||
sc_vector_insert_all_(pv, (size_t) index, items, (size_t) count)
|
||||
|
||||
#define sc_vector_insert_all_(pv, index, items, count) \
|
||||
({ \
|
||||
sc_vector_check_same_ptr_type_((pv)->data, items); \
|
||||
bool ok = sc_vector_insert_hole_(pv, index, count); \
|
||||
if (ok) { \
|
||||
memcpy(&(pv)->data[index], items, count * sizeof(*(pv)->data)); \
|
||||
} \
|
||||
ok; \
|
||||
})
|
||||
|
||||
/** Reverse a char array in place */
|
||||
static inline void
|
||||
sc_char_array_reverse(char *array, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len / 2; ++i)
|
||||
{
|
||||
char c = array[i];
|
||||
array[i] = array[len - i - 1];
|
||||
array[len - i - 1] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Right-rotate a (char) array in place
|
||||
*
|
||||
* For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
|
||||
* distance 4 will result in {5, 6, 1, 2, 3, 4}.
|
||||
*
|
||||
* Private.
|
||||
*/
|
||||
static inline void
|
||||
sc_char_array_rotate_left(char *array, size_t len, size_t distance)
|
||||
{
|
||||
sc_char_array_reverse(array, distance);
|
||||
sc_char_array_reverse(&array[distance], len - distance);
|
||||
sc_char_array_reverse(array, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Right-rotate a (char) array in place
|
||||
*
|
||||
* For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
|
||||
* distance 2 will result in {5, 6, 1, 2, 3, 4}.
|
||||
*
|
||||
* Private.
|
||||
*/
|
||||
static inline void
|
||||
sc_char_array_rotate_right(char *array, size_t len, size_t distance)
|
||||
{
|
||||
sc_char_array_rotate_left(array, len, len - distance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move items in a (char) array in place
|
||||
*
|
||||
* Move slice [index, count] to target.
|
||||
*/
|
||||
static inline void
|
||||
sc_char_array_move(char *array, size_t idx, size_t count, size_t target)
|
||||
{
|
||||
if (idx < target) {
|
||||
sc_char_array_rotate_left(&array[idx], target - idx + count, count);
|
||||
} else {
|
||||
sc_char_array_rotate_right(&array[target], idx - target + count, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a slice of items to a given target index
|
||||
*
|
||||
* The items in range [index; count] will be moved so that the *new* position
|
||||
* of the first item is `target`.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of the first item to move
|
||||
* \param count the number of items to move
|
||||
* \param target the new index of the moved slice
|
||||
*/
|
||||
#define sc_vector_move_slice(pv, index, count, target) \
|
||||
sc_vector_move_slice_(pv, (size_t) index, count, (size_t) target);
|
||||
|
||||
#define sc_vector_move_slice_(pv, index, count, target) \
|
||||
({ \
|
||||
sc_char_array_move((char *) (pv)->data, \
|
||||
(index) * sizeof(*(pv)->data), \
|
||||
(count) * sizeof(*(pv)->data), \
|
||||
(target) * sizeof(*(pv)->data)); \
|
||||
})
|
||||
|
||||
/**
|
||||
* Move an item to a given target index
|
||||
*
|
||||
* The items will be moved so that its *new* position is `target`.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of the item to move
|
||||
* \param target the new index of the moved item
|
||||
*/
|
||||
#define sc_vector_move(pv, index, target) \
|
||||
sc_vector_move_slice(pv, index, 1, target)
|
||||
|
||||
/**
|
||||
* Remove a slice of items, without shrinking the array
|
||||
*
|
||||
* If you have no good reason to use the _noshrink() version, use
|
||||
* sc_vector_remove_slice() instead.
|
||||
*
|
||||
* The items in range [index+count; size-1] will be moved.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of the first item to remove
|
||||
* \param count the number of items to remove
|
||||
*/
|
||||
#define sc_vector_remove_slice_noshrink(pv, index, count) \
|
||||
sc_vector_remove_slice_noshrink_(pv, (size_t) index, (size_t) count)
|
||||
|
||||
#define sc_vector_remove_slice_noshrink_(pv, index, count) \
|
||||
({ \
|
||||
if ((index) + (count) < (pv)->size) { \
|
||||
memmove(&(pv)->data[index], \
|
||||
&(pv)->data[(index) + (count)], \
|
||||
((pv)->size - (index) - (count)) * sizeof(*(pv)->data)); \
|
||||
} \
|
||||
(pv)->size -= count; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Remove a slice of items
|
||||
*
|
||||
* The items in range [index+count; size-1] will be moved.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of the first item to remove
|
||||
* \param count the number of items to remove
|
||||
*/
|
||||
#define sc_vector_remove_slice(pv, index, count) \
|
||||
({ \
|
||||
sc_vector_remove_slice_noshrink(pv, index, count); \
|
||||
sc_vector_autoshrink(pv); \
|
||||
})
|
||||
|
||||
/**
|
||||
* Remove an item, without shrinking the array
|
||||
*
|
||||
* If you have no good reason to use the _noshrink() version, use
|
||||
* sc_vector_remove() instead.
|
||||
*
|
||||
* The items in range [index+1; size-1] will be moved.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of item to remove
|
||||
*/
|
||||
#define sc_vector_remove_noshrink(pv, index) \
|
||||
sc_vector_remove_slice_noshrink(pv, index, 1)
|
||||
|
||||
/**
|
||||
* Remove an item
|
||||
*
|
||||
* The items in range [index+1; size-1] will be moved.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of item to remove
|
||||
*/
|
||||
#define sc_vector_remove(pv, index) \
|
||||
({ \
|
||||
sc_vector_remove_noshrink(pv, index); \
|
||||
sc_vector_autoshrink(pv); \
|
||||
})
|
||||
|
||||
/**
|
||||
* Remove an item
|
||||
*
|
||||
* The removed item is replaced by the last item of the vector.
|
||||
*
|
||||
* This does not preserve ordering, but is O(1). This is useful when the order
|
||||
* of items is not meaningful.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param index the index of item to remove
|
||||
*/
|
||||
#define sc_vector_swap_remove(pv, index) \
|
||||
sc_vector_swap_remove_(pv, (size_t) index);
|
||||
|
||||
#define sc_vector_swap_remove_(pv, index) \
|
||||
({ \
|
||||
(pv)->data[index] = (pv)->data[(pv)->size-1]; \
|
||||
(pv)->size--; \
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the index of an item
|
||||
*
|
||||
* Iterate over all items to find a given item.
|
||||
*
|
||||
* Use only for vectors of primitive types or pointers.
|
||||
*
|
||||
* Return the index, or -1 if not found.
|
||||
*
|
||||
* \param pv a pointer to the vector
|
||||
* \param item the item to find (compared with ==)
|
||||
*/
|
||||
#define sc_vector_index_of(pv, item) \
|
||||
({ \
|
||||
ssize_t idx = -1; \
|
||||
for (size_t i = 0; i < (pv)->size; ++i) { \
|
||||
if ((pv)->data[i] == (item)) { \
|
||||
idx = (ssize_t) i; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
idx; \
|
||||
})
|
||||
|
||||
#endif
|
@ -13,21 +13,22 @@ static void test_adb_devices() {
|
||||
"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_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 2);
|
||||
|
||||
struct sc_adb_device *device = &devices[0];
|
||||
struct sc_adb_device *device = &vec.data[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("device", device->state));
|
||||
assert(!strcmp("MyModel", device->model));
|
||||
|
||||
device = &devices[1];
|
||||
device = &vec.data[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);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_adb_devices_cr() {
|
||||
@ -38,21 +39,22 @@ static void test_adb_devices_cr() {
|
||||
"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_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 2);
|
||||
|
||||
struct sc_adb_device *device = &devices[0];
|
||||
struct sc_adb_device *device = &vec.data[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("device", device->state));
|
||||
assert(!strcmp("MyModel", device->model));
|
||||
|
||||
device = &devices[1];
|
||||
device = &vec.data[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);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_adb_devices_daemon_start() {
|
||||
@ -63,16 +65,17 @@ static void test_adb_devices_daemon_start() {
|
||||
"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_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 1);
|
||||
|
||||
struct sc_adb_device *device = &devices[0];
|
||||
struct sc_adb_device *device = &vec.data[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("device", device->state));
|
||||
assert(!strcmp("MyModel", device->model));
|
||||
|
||||
sc_adb_device_destroy(device);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_adb_devices_daemon_start_mixed() {
|
||||
@ -84,21 +87,22 @@ static void test_adb_devices_daemon_start_mixed() {
|
||||
"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_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 2);
|
||||
|
||||
struct sc_adb_device *device = &devices[0];
|
||||
struct sc_adb_device *device = &vec.data[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("unauthorized", device->state));
|
||||
assert(!device->model);
|
||||
|
||||
device = &devices[1];
|
||||
device = &vec.data[1];
|
||||
assert(!strcmp("87654321", device->serial));
|
||||
assert(!strcmp("device", device->state));
|
||||
assert(!strcmp("MyModel", device->model));
|
||||
|
||||
sc_adb_devices_destroy_all(devices, count);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_adb_devices_without_eol() {
|
||||
@ -106,34 +110,39 @@ static void test_adb_devices_without_eol() {
|
||||
"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];
|
||||
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 1);
|
||||
|
||||
struct sc_adb_device *device = &vec.data[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("device", device->state));
|
||||
assert(!strcmp("MyModel", device->model));
|
||||
|
||||
sc_adb_device_destroy(device);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(!ok);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
struct sc_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 0);
|
||||
}
|
||||
|
||||
static void test_adb_devices_spaces() {
|
||||
@ -141,16 +150,17 @@ static void test_adb_devices_spaces() {
|
||||
"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_vec_adb_devices vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok = sc_adb_parse_devices(output, &vec);
|
||||
assert(ok);
|
||||
assert(vec.size == 1);
|
||||
|
||||
struct sc_adb_device *device = &devices[0];
|
||||
struct sc_adb_device *device = &vec.data[0];
|
||||
assert(!strcmp("0123456789abcdef", device->serial));
|
||||
assert(!strcmp("unauthorized", device->state));
|
||||
assert(!device->model);
|
||||
|
||||
sc_adb_device_destroy(device);
|
||||
sc_adb_devices_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_get_ip_single_line() {
|
||||
|
421
app/tests/test_vector.c
Normal file
421
app/tests/test_vector.c
Normal file
@ -0,0 +1,421 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "util/vector.h"
|
||||
|
||||
static void test_vector_insert_remove(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
bool ok;
|
||||
|
||||
ok = sc_vector_push(&vec, 42);
|
||||
assert(ok);
|
||||
assert(vec.data[0] == 42);
|
||||
assert(vec.size == 1);
|
||||
|
||||
ok = sc_vector_push(&vec, 37);
|
||||
assert(ok);
|
||||
assert(vec.size == 2);
|
||||
assert(vec.data[0] == 42);
|
||||
assert(vec.data[1] == 37);
|
||||
|
||||
ok = sc_vector_insert(&vec, 1, 100);
|
||||
assert(ok);
|
||||
assert(vec.size == 3);
|
||||
assert(vec.data[0] == 42);
|
||||
assert(vec.data[1] == 100);
|
||||
assert(vec.data[2] == 37);
|
||||
|
||||
ok = sc_vector_push(&vec, 77);
|
||||
assert(ok);
|
||||
assert(vec.size == 4);
|
||||
assert(vec.data[0] == 42);
|
||||
assert(vec.data[1] == 100);
|
||||
assert(vec.data[2] == 37);
|
||||
assert(vec.data[3] == 77);
|
||||
|
||||
sc_vector_remove(&vec, 1);
|
||||
assert(vec.size == 3);
|
||||
assert(vec.data[0] == 42);
|
||||
assert(vec.data[1] == 37);
|
||||
assert(vec.data[2] == 77);
|
||||
|
||||
sc_vector_clear(&vec);
|
||||
assert(vec.size == 0);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_push_array(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok;
|
||||
|
||||
ok = sc_vector_push(&vec, 3); assert(ok);
|
||||
ok = sc_vector_push(&vec, 14); assert(ok);
|
||||
ok = sc_vector_push(&vec, 15); assert(ok);
|
||||
ok = sc_vector_push(&vec, 92); assert(ok);
|
||||
ok = sc_vector_push(&vec, 65); assert(ok);
|
||||
assert(vec.size == 5);
|
||||
|
||||
int items[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
ok = sc_vector_push_all(&vec, items, 8);
|
||||
|
||||
assert(ok);
|
||||
assert(vec.size == 13);
|
||||
assert(vec.data[0] == 3);
|
||||
assert(vec.data[1] == 14);
|
||||
assert(vec.data[2] == 15);
|
||||
assert(vec.data[3] == 92);
|
||||
assert(vec.data[4] == 65);
|
||||
assert(vec.data[5] == 1);
|
||||
assert(vec.data[6] == 2);
|
||||
assert(vec.data[7] == 3);
|
||||
assert(vec.data[8] == 4);
|
||||
assert(vec.data[9] == 5);
|
||||
assert(vec.data[10] == 6);
|
||||
assert(vec.data[11] == 7);
|
||||
assert(vec.data[12] == 8);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_insert_array(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
bool ok;
|
||||
|
||||
ok = sc_vector_push(&vec, 3); assert(ok);
|
||||
ok = sc_vector_push(&vec, 14); assert(ok);
|
||||
ok = sc_vector_push(&vec, 15); assert(ok);
|
||||
ok = sc_vector_push(&vec, 92); assert(ok);
|
||||
ok = sc_vector_push(&vec, 65); assert(ok);
|
||||
assert(vec.size == 5);
|
||||
|
||||
int items[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
ok = sc_vector_insert_all(&vec, 3, items, 8);
|
||||
assert(ok);
|
||||
assert(vec.size == 13);
|
||||
assert(vec.data[0] == 3);
|
||||
assert(vec.data[1] == 14);
|
||||
assert(vec.data[2] == 15);
|
||||
assert(vec.data[3] == 1);
|
||||
assert(vec.data[4] == 2);
|
||||
assert(vec.data[5] == 3);
|
||||
assert(vec.data[6] == 4);
|
||||
assert(vec.data[7] == 5);
|
||||
assert(vec.data[8] == 6);
|
||||
assert(vec.data[9] == 7);
|
||||
assert(vec.data[10] == 8);
|
||||
assert(vec.data[11] == 92);
|
||||
assert(vec.data[12] == 65);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_remove_slice(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
bool ok;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
assert(vec.size == 100);
|
||||
|
||||
sc_vector_remove_slice(&vec, 32, 60);
|
||||
assert(vec.size == 40);
|
||||
assert(vec.data[31] == 31);
|
||||
assert(vec.data[32] == 92);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_swap_remove(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
bool ok;
|
||||
|
||||
ok = sc_vector_push(&vec, 3); assert(ok);
|
||||
ok = sc_vector_push(&vec, 14); assert(ok);
|
||||
ok = sc_vector_push(&vec, 15); assert(ok);
|
||||
ok = sc_vector_push(&vec, 92); assert(ok);
|
||||
ok = sc_vector_push(&vec, 65); assert(ok);
|
||||
assert(vec.size == 5);
|
||||
|
||||
sc_vector_swap_remove(&vec, 1);
|
||||
assert(vec.size == 4);
|
||||
assert(vec.data[0] == 3);
|
||||
assert(vec.data[1] == 65);
|
||||
assert(vec.data[2] == 15);
|
||||
assert(vec.data[3] == 92);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_index_of(void) {
|
||||
struct SC_VECTOR(int) vec;
|
||||
sc_vector_init(&vec);
|
||||
|
||||
bool ok;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
ssize_t idx;
|
||||
|
||||
idx = sc_vector_index_of(&vec, 0);
|
||||
assert(idx == 0);
|
||||
|
||||
idx = sc_vector_index_of(&vec, 1);
|
||||
assert(idx == 1);
|
||||
|
||||
idx = sc_vector_index_of(&vec, 4);
|
||||
assert(idx == 4);
|
||||
|
||||
idx = sc_vector_index_of(&vec, 9);
|
||||
assert(idx == 9);
|
||||
|
||||
idx = sc_vector_index_of(&vec, 12);
|
||||
assert(idx == -1);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_grow() {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
bool ok;
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
ok = sc_vector_push(&vec, i); /* append */
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
assert(vec.cap >= 50);
|
||||
assert(vec.size == 50);
|
||||
|
||||
for (int i = 0; i < 25; ++i)
|
||||
{
|
||||
ok = sc_vector_insert(&vec, 20, i); /* insert in the middle */
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
assert(vec.cap >= 75);
|
||||
assert(vec.size == 75);
|
||||
|
||||
for (int i = 0; i < 25; ++i)
|
||||
{
|
||||
ok = sc_vector_insert(&vec, 0, i); /* prepend */
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
assert(vec.cap >= 100);
|
||||
assert(vec.size == 100);
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
sc_vector_remove(&vec, 20); /* remove from the middle */
|
||||
|
||||
assert(vec.cap >= 50);
|
||||
assert(vec.size == 50);
|
||||
|
||||
for (int i = 0; i < 25; ++i)
|
||||
sc_vector_remove(&vec, 0); /* remove from the head */
|
||||
|
||||
assert(vec.cap >= 25);
|
||||
assert(vec.size == 25);
|
||||
|
||||
for (int i = 24; i >=0; --i)
|
||||
sc_vector_remove(&vec, i); /* remove from the tail */
|
||||
|
||||
assert(vec.size == 0);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_exp_growth(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
size_t oldcap = vec.cap;
|
||||
int realloc_count = 0;
|
||||
bool ok;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
if (vec.cap != oldcap)
|
||||
{
|
||||
realloc_count++;
|
||||
oldcap = vec.cap;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test speciically for an expected growth factor of 1.5. In practice, the
|
||||
* result is even lower (19) due to the first alloc of size 10 */
|
||||
assert(realloc_count <= 23); /* ln(10000) / ln(1.5) ~= 23 */
|
||||
|
||||
realloc_count = 0;
|
||||
for (int i = 9999; i >= 0; --i)
|
||||
{
|
||||
sc_vector_remove(&vec, i);
|
||||
if (vec.cap != oldcap)
|
||||
{
|
||||
realloc_count++;
|
||||
oldcap = vec.cap;
|
||||
}
|
||||
}
|
||||
|
||||
assert(realloc_count <= 23); /* same expectations for removals */
|
||||
assert(realloc_count > 0); /* sc_vector_remove() must autoshrink */
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_reserve(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
bool ok;
|
||||
|
||||
ok = sc_vector_reserve(&vec, 800);
|
||||
assert(ok);
|
||||
assert(vec.cap >= 800);
|
||||
assert(vec.size == 0);
|
||||
|
||||
size_t initial_cap = vec.cap;
|
||||
|
||||
for (int i = 0; i < 800; ++i)
|
||||
{
|
||||
ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
assert(vec.cap == initial_cap); /* no realloc */
|
||||
}
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_shrink_to_fit(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
bool ok;
|
||||
|
||||
ok = sc_vector_reserve(&vec, 800);
|
||||
assert(ok);
|
||||
for (int i = 0; i < 250; ++i)
|
||||
{
|
||||
ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
assert(vec.cap >= 800);
|
||||
assert(vec.size == 250);
|
||||
|
||||
sc_vector_shrink_to_fit(&vec);
|
||||
assert(vec.cap == 250);
|
||||
assert(vec.size == 250);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_move(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
bool ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
/* move item at 1 so that its new position is 4 */
|
||||
sc_vector_move(&vec, 1, 4);
|
||||
|
||||
assert(vec.size == 7);
|
||||
assert(vec.data[0] == 0);
|
||||
assert(vec.data[1] == 2);
|
||||
assert(vec.data[2] == 3);
|
||||
assert(vec.data[3] == 4);
|
||||
assert(vec.data[4] == 1);
|
||||
assert(vec.data[5] == 5);
|
||||
assert(vec.data[6] == 6);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_move_slice_forward(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
bool ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
/* move slice {2, 3, 4, 5} so that its new position is 5 */
|
||||
sc_vector_move_slice(&vec, 2, 4, 5);
|
||||
|
||||
assert(vec.size == 10);
|
||||
assert(vec.data[0] == 0);
|
||||
assert(vec.data[1] == 1);
|
||||
assert(vec.data[2] == 6);
|
||||
assert(vec.data[3] == 7);
|
||||
assert(vec.data[4] == 8);
|
||||
assert(vec.data[5] == 2);
|
||||
assert(vec.data[6] == 3);
|
||||
assert(vec.data[7] == 4);
|
||||
assert(vec.data[8] == 5);
|
||||
assert(vec.data[9] == 9);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
static void test_vector_move_slice_backward(void) {
|
||||
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
bool ok = sc_vector_push(&vec, i);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
/* move slice {5, 6, 7} so that its new position is 2 */
|
||||
sc_vector_move_slice(&vec, 5, 3, 2);
|
||||
|
||||
assert(vec.size == 10);
|
||||
assert(vec.data[0] == 0);
|
||||
assert(vec.data[1] == 1);
|
||||
assert(vec.data[2] == 5);
|
||||
assert(vec.data[3] == 6);
|
||||
assert(vec.data[4] == 7);
|
||||
assert(vec.data[5] == 2);
|
||||
assert(vec.data[6] == 3);
|
||||
assert(vec.data[7] == 4);
|
||||
assert(vec.data[8] == 8);
|
||||
assert(vec.data[9] == 9);
|
||||
|
||||
sc_vector_destroy(&vec);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
test_vector_insert_remove();
|
||||
test_vector_push_array();
|
||||
test_vector_insert_array();
|
||||
test_vector_remove_slice();
|
||||
test_vector_swap_remove();
|
||||
test_vector_move();
|
||||
test_vector_move_slice_forward();
|
||||
test_vector_move_slice_backward();
|
||||
test_vector_index_of();
|
||||
test_vector_grow();
|
||||
test_vector_exp_growth();
|
||||
test_vector_reserve();
|
||||
test_vector_shrink_to_fit();
|
||||
return 0;
|
||||
}
|
@ -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'
|
||||
|
@ -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'
|
||||
|
22
release.mk
22
release.mk
@ -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)"
|
||||
@ -94,10 +98,10 @@ dist-win32: build-server build-win32
|
||||
mkdir -p "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/open_a_terminal_here.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp app/data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp app/data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp app/data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp app/data/open_a_terminal_here.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win32-4.3.1/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
@ -107,15 +111,16 @@ 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)"
|
||||
cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/open_a_terminal_here.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp app/data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp app/data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp app/data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp app/data/open_a_terminal_here.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avutil-57.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avcodec-59.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp app/prebuilt-deps/data/ffmpeg-win64-5.0/bin/avformat-59.dll "$(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)"; \
|
||||
|
@ -102,7 +102,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
alive = encode(codec, fd);
|
||||
// do not call stop() on exception, it would trigger an IllegalStateException
|
||||
codec.stop();
|
||||
} catch (IllegalStateException e) {
|
||||
} catch (IllegalStateException | IllegalArgumentException e) {
|
||||
Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage());
|
||||
if (!downsizeOnError || firstFrameSent) {
|
||||
// Fail immediately
|
||||
|
Reference in New Issue
Block a user