Compare commits

...

5 Commits

Author SHA1 Message Date
e2e76c5d48 Increase adb devices -l max output size
For simplicity, the parsing of `adb devices -l` output is performed in a
single pass on the whole output.

This output was limited to 4096 bytes. Since there are about 100 chars
per device line, this limited the number of connected devices to ~40.

Increase to 65536 bytes to avoid a limitation in practice.

PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>
2022-02-21 00:03:45 +01:00
4b8cb042c4 Use vector for listing ADB devices
This avoids the hardcoded maximum number of ADB devices detected (16).

Refs #3029 <https://github.com/Genymobile/scrcpy/pull/3029>
PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>

Co-authored-by: Daniel Ansorregui <d.ansorregui@samsung.com>
2022-02-20 23:59:35 +01:00
1790e88278 Use vector for listing USB devices
This avoids the hardcoded maximum number of USB devices detected (16).

Refs #3029 <https://github.com/Genymobile/scrcpy/pull/3029>
PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>
2022-02-20 23:59:35 +01:00
c070723bc8 Add sc_vector
Adapt vlc_vector [1], that I initially wrote while implementing the VLC
playlist [2].

Change the implementation to use "statement expressions" [3], which are
forbidden in VLC because "non-standard", but:
 - they are supported by gcc and clang;
 - they are already used in the scrcpy codebase;
 - they avoid implementation hacks (VLC_VECTOR_FAILFLAG_);
 - they allow a better API (sc_vector_index_of() may return the result
   without an output parameter).

PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>

[1]: 0857947aba/include/vlc_vector.h
[2]: https://blog.rom1v.com/2019/05/a-new-core-playlist-for-vlc-4
[3]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
2022-02-20 23:59:35 +01:00
36c75e15b8 Move data/ to app/
The files in data/ are specific to the client app (not the server).

This also avoids to reference the parent directory (../) from
app/meson.build.

Refs 8d583d36e2
2022-02-20 17:56:50 +01:00
18 changed files with 1116 additions and 125 deletions

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

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

View File

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

View File

@ -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"
@ -392,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
@ -529,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
@ -567,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;
}
@ -594,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;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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) {
@ -85,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
@ -157,29 +166,28 @@ sc_usb_devices_log(enum sc_log_level level, struct sc_usb_device *devices,
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;
}
@ -190,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;
}

539
app/src/util/vector.h Normal file
View 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

View File

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

View File

@ -98,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)/"
@ -117,10 +117,10 @@ 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)/"