Compare commits
2 Commits
refactor-e
...
feature
Author | SHA1 | Date | |
---|---|---|---|
2b1fd581fd | |||
ebf8c4dbf9 |
17
FAQ.md
17
FAQ.md
@ -7,7 +7,7 @@ Here are the common reported problems and their status.
|
|||||||
If you encounter any error, the first step is to upgrade to the latest version.
|
If you encounter any error, the first step is to upgrade to the latest version.
|
||||||
|
|
||||||
|
|
||||||
## `adb` issues
|
## `adb` and USB issues
|
||||||
|
|
||||||
`scrcpy` execute `adb` commands to initialize the connection with the device. If
|
`scrcpy` execute `adb` commands to initialize the connection with the device. If
|
||||||
`adb` fails, then scrcpy will not work.
|
`adb` fails, then scrcpy will not work.
|
||||||
@ -133,6 +133,21 @@ Try with another USB cable or plug it into another USB port. See [#281] and
|
|||||||
[#283]: https://github.com/Genymobile/scrcpy/issues/283
|
[#283]: https://github.com/Genymobile/scrcpy/issues/283
|
||||||
|
|
||||||
|
|
||||||
|
## HID/OTG issues on Windows
|
||||||
|
|
||||||
|
On Windows, if `scrcpy --otg` (or `--hid-keyboard`/`--hid-mouse`) results in:
|
||||||
|
|
||||||
|
> ERROR: Could not find any USB device
|
||||||
|
|
||||||
|
(or if only unrelated USB devices are detected), there might be drivers issues.
|
||||||
|
|
||||||
|
Please read [#3654], in particular [this comment][#3654-comment1] and [the next
|
||||||
|
one][#3654-comment2].
|
||||||
|
|
||||||
|
[#3654]: https://github.com/Genymobile/scrcpy/issues/3654
|
||||||
|
[#3654-comment1]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-1369278232
|
||||||
|
[#3654-comment2]: https://github.com/Genymobile/scrcpy/issues/3654#issuecomment-1369295011
|
||||||
|
|
||||||
|
|
||||||
## Control issues
|
## Control issues
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ src = [
|
|||||||
'src/util/net_intr.c',
|
'src/util/net_intr.c',
|
||||||
'src/util/process.c',
|
'src/util/process.c',
|
||||||
'src/util/process_intr.c',
|
'src/util/process_intr.c',
|
||||||
'src/util/rand.c',
|
|
||||||
'src/util/strbuf.c',
|
'src/util/strbuf.c',
|
||||||
'src/util/str.c',
|
'src/util/str.c',
|
||||||
'src/util/term.c',
|
'src/util/term.c',
|
||||||
@ -70,12 +69,12 @@ else
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
v4l2_support = get_option('v4l2') and host_machine.system() == 'linux'
|
v4l2_support = get_option('v4l2').enabled() and host_machine.system() == 'linux'
|
||||||
if v4l2_support
|
if v4l2_support
|
||||||
src += [ 'src/v4l2_sink.c' ]
|
src += [ 'src/v4l2_sink.c' ]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
usb_support = get_option('usb')
|
usb_support = get_option('usb').enabled()
|
||||||
if usb_support
|
if usb_support
|
||||||
src += [
|
src += [
|
||||||
'src/usb/aoa_hid.c',
|
'src/usb/aoa_hid.c',
|
||||||
@ -171,8 +170,6 @@ check_functions = [
|
|||||||
'strdup',
|
'strdup',
|
||||||
'asprintf',
|
'asprintf',
|
||||||
'vasprintf',
|
'vasprintf',
|
||||||
'nrand48',
|
|
||||||
'jrand48',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach f : check_functions
|
foreach f : check_functions
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "util/net_intr.h"
|
#include "util/net_intr.h"
|
||||||
#include "util/process_intr.h"
|
#include "util/process_intr.h"
|
||||||
|
|
||||||
|
#define SC_SOCKET_NAME "scrcpy"
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) {
|
listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) {
|
||||||
return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1);
|
return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1);
|
||||||
@ -15,11 +17,10 @@ listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) {
|
|||||||
static bool
|
static bool
|
||||||
enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
|
enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
|
||||||
struct sc_intr *intr, const char *serial,
|
struct sc_intr *intr, const char *serial,
|
||||||
const char *device_socket_name,
|
|
||||||
struct sc_port_range port_range) {
|
struct sc_port_range port_range) {
|
||||||
uint16_t port = port_range.first;
|
uint16_t port = port_range.first;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!sc_adb_reverse(intr, serial, device_socket_name, port,
|
if (!sc_adb_reverse(intr, serial, SC_SOCKET_NAME, port,
|
||||||
SC_ADB_NO_STDOUT)) {
|
SC_ADB_NO_STDOUT)) {
|
||||||
// the command itself failed, it will fail on any port
|
// the command itself failed, it will fail on any port
|
||||||
return false;
|
return false;
|
||||||
@ -51,7 +52,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// failure, disable tunnel and try another port
|
// failure, disable tunnel and try another port
|
||||||
if (!sc_adb_reverse_remove(intr, serial, device_socket_name,
|
if (!sc_adb_reverse_remove(intr, serial, SC_SOCKET_NAME,
|
||||||
SC_ADB_NO_STDOUT)) {
|
SC_ADB_NO_STDOUT)) {
|
||||||
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
|
||||||
}
|
}
|
||||||
@ -77,13 +78,12 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
|
|||||||
static bool
|
static bool
|
||||||
enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel,
|
enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel,
|
||||||
struct sc_intr *intr, const char *serial,
|
struct sc_intr *intr, const char *serial,
|
||||||
const char *device_socket_name,
|
|
||||||
struct sc_port_range port_range) {
|
struct sc_port_range port_range) {
|
||||||
tunnel->forward = true;
|
tunnel->forward = true;
|
||||||
|
|
||||||
uint16_t port = port_range.first;
|
uint16_t port = port_range.first;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (sc_adb_forward(intr, serial, port, device_socket_name,
|
if (sc_adb_forward(intr, serial, port, SC_SOCKET_NAME,
|
||||||
SC_ADB_NO_STDOUT)) {
|
SC_ADB_NO_STDOUT)) {
|
||||||
// success
|
// success
|
||||||
tunnel->local_port = port;
|
tunnel->local_port = port;
|
||||||
@ -123,14 +123,13 @@ sc_adb_tunnel_init(struct sc_adb_tunnel *tunnel) {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
||||||
const char *serial, const char *device_socket_name,
|
const char *serial, struct sc_port_range port_range,
|
||||||
struct sc_port_range port_range, bool force_adb_forward) {
|
bool force_adb_forward) {
|
||||||
assert(!tunnel->enabled);
|
assert(!tunnel->enabled);
|
||||||
|
|
||||||
if (!force_adb_forward) {
|
if (!force_adb_forward) {
|
||||||
// Attempt to use "adb reverse"
|
// Attempt to use "adb reverse"
|
||||||
if (enable_tunnel_reverse_any_port(tunnel, intr, serial,
|
if (enable_tunnel_reverse_any_port(tunnel, intr, serial, port_range)) {
|
||||||
device_socket_name, port_range)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,13 +139,12 @@ sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
|||||||
LOGW("'adb reverse' failed, fallback to 'adb forward'");
|
LOGW("'adb reverse' failed, fallback to 'adb forward'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return enable_tunnel_forward_any_port(tunnel, intr, serial,
|
return enable_tunnel_forward_any_port(tunnel, intr, serial, port_range);
|
||||||
device_socket_name, port_range);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
||||||
const char *serial, const char *device_socket_name) {
|
const char *serial) {
|
||||||
assert(tunnel->enabled);
|
assert(tunnel->enabled);
|
||||||
|
|
||||||
bool ret;
|
bool ret;
|
||||||
@ -154,7 +152,7 @@ sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
|||||||
ret = sc_adb_forward_remove(intr, serial, tunnel->local_port,
|
ret = sc_adb_forward_remove(intr, serial, tunnel->local_port,
|
||||||
SC_ADB_NO_STDOUT);
|
SC_ADB_NO_STDOUT);
|
||||||
} else {
|
} else {
|
||||||
ret = sc_adb_reverse_remove(intr, serial, device_socket_name,
|
ret = sc_adb_reverse_remove(intr, serial, SC_SOCKET_NAME,
|
||||||
SC_ADB_NO_STDOUT);
|
SC_ADB_NO_STDOUT);
|
||||||
|
|
||||||
assert(tunnel->server_socket != SC_SOCKET_NONE);
|
assert(tunnel->server_socket != SC_SOCKET_NONE);
|
||||||
|
@ -34,14 +34,14 @@ sc_adb_tunnel_init(struct sc_adb_tunnel *tunnel);
|
|||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
||||||
const char *serial, const char *device_socket_name,
|
const char *serial, struct sc_port_range port_range,
|
||||||
struct sc_port_range port_range, bool force_adb_forward);
|
bool force_adb_forward);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the tunnel
|
* Close the tunnel
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
|
||||||
const char *serial, const char *device_socket_name);
|
const char *serial);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -51,47 +51,3 @@ int vasprintf(char **strp, const char *fmt, va_list ap) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(HAVE_NRAND48) || !defined(HAVE_JRAND48)
|
|
||||||
#define SC_RAND48_MASK UINT64_C(0xFFFFFFFFFFFF) // 48 bits
|
|
||||||
#define SC_RAND48_A UINT64_C(0x5DEECE66D)
|
|
||||||
#define SC_RAND48_C 0xB
|
|
||||||
static inline uint64_t rand_iter48(uint64_t x) {
|
|
||||||
assert((x & ~SC_RAND48_MASK) == 0);
|
|
||||||
return (x * SC_RAND48_A + SC_RAND48_C) & SC_RAND48_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t rand_iter48_xsubi(unsigned short xsubi[3]) {
|
|
||||||
uint64_t x = ((uint64_t) xsubi[0] << 32)
|
|
||||||
| ((uint64_t) xsubi[1] << 16)
|
|
||||||
| xsubi[2];
|
|
||||||
|
|
||||||
x = rand_iter48(x);
|
|
||||||
|
|
||||||
xsubi[0] = (x >> 32) & 0XFFFF;
|
|
||||||
xsubi[1] = (x >> 16) & 0XFFFF;
|
|
||||||
xsubi[2] = x & 0XFFFF;
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef HAVE_NRAND48
|
|
||||||
long nrand48(unsigned short xsubi[3]) {
|
|
||||||
// range [0, 2^31)
|
|
||||||
return rand_iter48_xsubi(xsubi) >> 17;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HAVE_JRAND48
|
|
||||||
long jrand48(unsigned short xsubi[3]) {
|
|
||||||
// range [-2^31, 2^31)
|
|
||||||
union {
|
|
||||||
uint32_t u;
|
|
||||||
int32_t i;
|
|
||||||
} v;
|
|
||||||
v.u = rand_iter48_xsubi(xsubi) >> 16;
|
|
||||||
return v.i;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -59,12 +59,4 @@ int asprintf(char **strp, const char *fmt, ...);
|
|||||||
int vasprintf(char **strp, const char *fmt, va_list ap);
|
int vasprintf(char **strp, const char *fmt, va_list ap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_NRAND48
|
|
||||||
long nrand48(unsigned short xsubi[3]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HAVE_JRAND48
|
|
||||||
long jrand48(unsigned short xsubi[3]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#include "util/acksync.h"
|
#include "util/acksync.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/rand.h"
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
# include "v4l2_sink.h"
|
# include "v4l2_sink.h"
|
||||||
#endif
|
#endif
|
||||||
@ -266,14 +265,6 @@ sc_server_on_disconnected(struct sc_server *server, void *userdata) {
|
|||||||
// event
|
// event
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
|
||||||
scrcpy_generate_uid() {
|
|
||||||
struct sc_rand rand;
|
|
||||||
sc_rand_init(&rand);
|
|
||||||
// Only use 31 bits to avoid issues with signed values on the Java-side
|
|
||||||
return sc_rand_u32(&rand) & 0x7FFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum scrcpy_exit_code
|
enum scrcpy_exit_code
|
||||||
scrcpy(struct scrcpy_options *options) {
|
scrcpy(struct scrcpy_options *options) {
|
||||||
static struct scrcpy scrcpy;
|
static struct scrcpy scrcpy;
|
||||||
@ -307,10 +298,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
|
|
||||||
struct sc_acksync *acksync = NULL;
|
struct sc_acksync *acksync = NULL;
|
||||||
|
|
||||||
uint32_t uid = scrcpy_generate_uid();
|
|
||||||
|
|
||||||
struct sc_server_params params = {
|
struct sc_server_params params = {
|
||||||
.uid = uid,
|
|
||||||
.req_serial = options->serial,
|
.req_serial = options->serial,
|
||||||
.select_usb = options->select_usb,
|
.select_usb = options->select_usb,
|
||||||
.select_tcpip = options->select_tcpip,
|
.select_tcpip = options->select_tcpip,
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
#define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
#define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
||||||
|
|
||||||
#define SC_ADB_PORT_DEFAULT 5555
|
#define SC_ADB_PORT_DEFAULT 5555
|
||||||
#define SC_SOCKET_NAME_PREFIX "scrcpy_"
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
get_server_path(void) {
|
get_server_path(void) {
|
||||||
@ -198,7 +197,6 @@ execute_server(struct sc_server *server,
|
|||||||
cmd[count++] = p; \
|
cmd[count++] = p; \
|
||||||
}
|
}
|
||||||
|
|
||||||
ADD_PARAM("uid=%08x", params->uid);
|
|
||||||
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
|
ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level));
|
||||||
ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate);
|
ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate);
|
||||||
|
|
||||||
@ -366,7 +364,6 @@ sc_server_init(struct sc_server *server, const struct sc_server_params *params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
server->serial = NULL;
|
server->serial = NULL;
|
||||||
server->device_socket_name = NULL;
|
|
||||||
server->stopped = false;
|
server->stopped = false;
|
||||||
|
|
||||||
server->video_socket = SC_SOCKET_NONE;
|
server->video_socket = SC_SOCKET_NONE;
|
||||||
@ -466,8 +463,7 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we don't need the adb tunnel anymore
|
// we don't need the adb tunnel anymore
|
||||||
sc_adb_tunnel_close(tunnel, &server->intr, serial,
|
sc_adb_tunnel_close(tunnel, &server->intr, serial);
|
||||||
server->device_socket_name);
|
|
||||||
|
|
||||||
// The sockets will be closed on stop if device_read_info() fails
|
// The sockets will be closed on stop if device_read_info() fails
|
||||||
bool ok = device_read_info(&server->intr, video_socket, info);
|
bool ok = device_read_info(&server->intr, video_socket, info);
|
||||||
@ -498,8 +494,7 @@ fail:
|
|||||||
|
|
||||||
if (tunnel->enabled) {
|
if (tunnel->enabled) {
|
||||||
// Always leave this function with tunnel disabled
|
// Always leave this function with tunnel disabled
|
||||||
sc_adb_tunnel_close(tunnel, &server->intr, serial,
|
sc_adb_tunnel_close(tunnel, &server->intr, serial);
|
||||||
server->device_socket_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -769,23 +764,13 @@ run_server(void *data) {
|
|||||||
assert(serial);
|
assert(serial);
|
||||||
LOGD("Device serial: %s", serial);
|
LOGD("Device serial: %s", serial);
|
||||||
|
|
||||||
int r = asprintf(&server->device_socket_name, SC_SOCKET_NAME_PREFIX "%08x",
|
|
||||||
params->uid);
|
|
||||||
if (r == -1) {
|
|
||||||
LOG_OOM();
|
|
||||||
goto error_connection_failed;
|
|
||||||
}
|
|
||||||
assert(r == sizeof(SC_SOCKET_NAME_PREFIX) - 1 + 8);
|
|
||||||
assert(server->device_socket_name);
|
|
||||||
|
|
||||||
ok = push_server(&server->intr, serial);
|
ok = push_server(&server->intr, serial);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, serial,
|
ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, serial,
|
||||||
server->device_socket_name, params->port_range,
|
params->port_range, params->force_adb_forward);
|
||||||
params->force_adb_forward);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
@ -793,8 +778,7 @@ run_server(void *data) {
|
|||||||
// server will connect to our server socket
|
// server will connect to our server socket
|
||||||
sc_pid pid = execute_server(server, params);
|
sc_pid pid = execute_server(server, params);
|
||||||
if (pid == SC_PROCESS_NONE) {
|
if (pid == SC_PROCESS_NONE) {
|
||||||
sc_adb_tunnel_close(&server->tunnel, &server->intr, serial,
|
sc_adb_tunnel_close(&server->tunnel, &server->intr, serial);
|
||||||
server->device_socket_name);
|
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,8 +790,7 @@ run_server(void *data) {
|
|||||||
if (!ok) {
|
if (!ok) {
|
||||||
sc_process_terminate(pid);
|
sc_process_terminate(pid);
|
||||||
sc_process_wait(pid, true); // ignore exit code
|
sc_process_wait(pid, true); // ignore exit code
|
||||||
sc_adb_tunnel_close(&server->tunnel, &server->intr, serial,
|
sc_adb_tunnel_close(&server->tunnel, &server->intr, serial);
|
||||||
server->device_socket_name);
|
|
||||||
goto error_connection_failed;
|
goto error_connection_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,7 +884,6 @@ sc_server_destroy(struct sc_server *server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
free(server->serial);
|
free(server->serial);
|
||||||
free(server->device_socket_name);
|
|
||||||
sc_server_params_destroy(&server->params);
|
sc_server_params_destroy(&server->params);
|
||||||
sc_intr_destroy(&server->intr);
|
sc_intr_destroy(&server->intr);
|
||||||
sc_cond_destroy(&server->cond_stopped);
|
sc_cond_destroy(&server->cond_stopped);
|
||||||
|
@ -22,7 +22,6 @@ struct sc_server_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sc_server_params {
|
struct sc_server_params {
|
||||||
uint32_t uid;
|
|
||||||
const char *req_serial;
|
const char *req_serial;
|
||||||
enum sc_log_level log_level;
|
enum sc_log_level log_level;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
@ -55,7 +54,6 @@ struct sc_server {
|
|||||||
// The internal allocated strings are copies owned by the server
|
// The internal allocated strings are copies owned by the server
|
||||||
struct sc_server_params params;
|
struct sc_server_params params;
|
||||||
char *serial;
|
char *serial;
|
||||||
char *device_socket_name;
|
|
||||||
|
|
||||||
sc_thread thread;
|
sc_thread thread;
|
||||||
struct sc_server_info info; // initialized once connected
|
struct sc_server_info info; // initialized once connected
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
#include "rand.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "tick.h"
|
|
||||||
|
|
||||||
void sc_rand_init(struct sc_rand *rand) {
|
|
||||||
sc_tick seed = sc_tick_now(); // microsecond precision
|
|
||||||
rand->xsubi[0] = (seed >> 32) & 0xFFFF;
|
|
||||||
rand->xsubi[1] = (seed >> 16) & 0xFFFF;
|
|
||||||
rand->xsubi[2] = seed & 0xFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t sc_rand_u32(struct sc_rand *rand) {
|
|
||||||
// jrand returns a value in range [-2^31, 2^31]
|
|
||||||
// conversion from signed to unsigned is well-defined to wrap-around
|
|
||||||
return jrand48(rand->xsubi);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t sc_rand_u64(struct sc_rand *rand) {
|
|
||||||
uint32_t msb = sc_rand_u32(rand);
|
|
||||||
uint32_t lsb = sc_rand_u32(rand);
|
|
||||||
return ((uint64_t) msb << 32) | lsb;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef SC_RAND_H
|
|
||||||
#define SC_RAND_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
struct sc_rand {
|
|
||||||
unsigned short xsubi[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
void sc_rand_init(struct sc_rand *rand);
|
|
||||||
uint32_t sc_rand_u32(struct sc_rand *rand);
|
|
||||||
uint64_t sc_rand_u64(struct sc_rand *rand);
|
|
||||||
|
|
||||||
#endif
|
|
@ -4,5 +4,5 @@ option('prebuilt_server', type: 'string', description: 'Path of the prebuilt ser
|
|||||||
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
|
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
|
||||||
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
|
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
|
||||||
option('server_debugger_method', type: 'combo', choices: ['old', 'new'], value: 'new', description: 'Select the debugger method (Android < 9: "old", Android >= 9: "new")')
|
option('server_debugger_method', type: 'combo', choices: ['old', 'new'], value: 'new', description: 'Select the debugger method (Android < 9: "old", Android >= 9: "new")')
|
||||||
option('v4l2', type: 'boolean', value: true, description: 'Enable V4L2 feature when supported')
|
option('v4l2', type: 'feature', value: 'enabled', description: 'Enable V4L2 feature when supported')
|
||||||
option('usb', type: 'boolean', value: true, description: 'Enable HID/OTG features when supported')
|
option('usb', type: 'feature', value: 'enabled', description: 'Enable HID/OTG features when supported')
|
||||||
|
@ -20,21 +20,18 @@ BUILD_TOOLS_DIR="$ANDROID_HOME/build-tools/$BUILD_TOOLS"
|
|||||||
|
|
||||||
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
|
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
|
||||||
CLASSES_DIR="$BUILD_DIR/classes"
|
CLASSES_DIR="$BUILD_DIR/classes"
|
||||||
GEN_DIR="$BUILD_DIR/gen"
|
|
||||||
SERVER_DIR=$(dirname "$0")
|
SERVER_DIR=$(dirname "$0")
|
||||||
SERVER_BINARY=scrcpy-server
|
SERVER_BINARY=scrcpy-server
|
||||||
ANDROID_JAR="$ANDROID_HOME/platforms/android-$PLATFORM/android.jar"
|
ANDROID_JAR="$ANDROID_HOME/platforms/android-$PLATFORM/android.jar"
|
||||||
LAMBDA_JAR="$BUILD_TOOLS_DIR/core-lambda-stubs.jar"
|
|
||||||
|
|
||||||
echo "Platform: android-$PLATFORM"
|
echo "Platform: android-$PLATFORM"
|
||||||
echo "Build-tools: $BUILD_TOOLS"
|
echo "Build-tools: $BUILD_TOOLS"
|
||||||
echo "Build dir: $BUILD_DIR"
|
echo "Build dir: $BUILD_DIR"
|
||||||
|
|
||||||
rm -rf "$CLASSES_DIR" "$GEN_DIR" "$BUILD_DIR/$SERVER_BINARY" classes.dex
|
rm -rf "$CLASSES_DIR" "$BUILD_DIR/$SERVER_BINARY" classes.dex
|
||||||
mkdir -p "$CLASSES_DIR"
|
mkdir -p "$CLASSES_DIR/com/genymobile/scrcpy"
|
||||||
mkdir -p "$GEN_DIR/com/genymobile/scrcpy"
|
|
||||||
|
|
||||||
<< EOF cat > "$GEN_DIR/com/genymobile/scrcpy/BuildConfig.java"
|
<< EOF cat > "$CLASSES_DIR/com/genymobile/scrcpy/BuildConfig.java"
|
||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
public final class BuildConfig {
|
public final class BuildConfig {
|
||||||
@ -45,15 +42,13 @@ EOF
|
|||||||
|
|
||||||
echo "Generating java from aidl..."
|
echo "Generating java from aidl..."
|
||||||
cd "$SERVER_DIR/src/main/aidl"
|
cd "$SERVER_DIR/src/main/aidl"
|
||||||
"$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" android/view/IRotationWatcher.aidl
|
"$BUILD_TOOLS_DIR/aidl" -o"$CLASSES_DIR" android/view/IRotationWatcher.aidl
|
||||||
"$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" \
|
"$BUILD_TOOLS_DIR/aidl" -o"$CLASSES_DIR" \
|
||||||
android/content/IOnPrimaryClipChangedListener.aidl
|
android/content/IOnPrimaryClipChangedListener.aidl
|
||||||
|
|
||||||
echo "Compiling java sources..."
|
echo "Compiling java sources..."
|
||||||
cd ../java
|
cd ../java
|
||||||
javac -bootclasspath "$ANDROID_JAR" \
|
javac -bootclasspath "$ANDROID_JAR" -cp "$CLASSES_DIR" -d "$CLASSES_DIR" \
|
||||||
-cp "$LAMBDA_JAR:$GEN_DIR" \
|
|
||||||
-d "$CLASSES_DIR" \
|
|
||||||
-source 1.8 -target 1.8 \
|
-source 1.8 -target 1.8 \
|
||||||
com/genymobile/scrcpy/*.java \
|
com/genymobile/scrcpy/*.java \
|
||||||
com/genymobile/scrcpy/wrappers/*.java
|
com/genymobile/scrcpy/wrappers/*.java
|
||||||
@ -73,7 +68,7 @@ then
|
|||||||
echo "Archiving..."
|
echo "Archiving..."
|
||||||
cd "$BUILD_DIR"
|
cd "$BUILD_DIR"
|
||||||
jar cvf "$SERVER_BINARY" classes.dex
|
jar cvf "$SERVER_BINARY" classes.dex
|
||||||
rm -rf classes.dex
|
rm -rf classes.dex classes
|
||||||
else
|
else
|
||||||
# use d8
|
# use d8
|
||||||
"$BUILD_TOOLS_DIR/d8" --classpath "$ANDROID_JAR" \
|
"$BUILD_TOOLS_DIR/d8" --classpath "$ANDROID_JAR" \
|
||||||
@ -85,8 +80,7 @@ else
|
|||||||
|
|
||||||
cd "$BUILD_DIR"
|
cd "$BUILD_DIR"
|
||||||
mv classes.zip "$SERVER_BINARY"
|
mv classes.zip "$SERVER_BINARY"
|
||||||
|
rm -rf classes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf "$GEN_DIR" "$CLASSES_DIR"
|
|
||||||
|
|
||||||
echo "Server generated in $BUILD_DIR/$SERVER_BINARY"
|
echo "Server generated in $BUILD_DIR/$SERVER_BINARY"
|
||||||
|
@ -4,8 +4,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CodecOption {
|
public class CodecOption {
|
||||||
private final String key;
|
private String key;
|
||||||
private final Object value;
|
private Object value;
|
||||||
|
|
||||||
public CodecOption(String key, Object value) {
|
public CodecOption(String key, Object value) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
@ -75,7 +75,7 @@ public class Controller {
|
|||||||
SystemClock.sleep(500);
|
SystemClock.sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (true) {
|
||||||
handleEvent();
|
handleEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,9 +258,12 @@ public class Controller {
|
|||||||
* Schedule a call to set power mode to off after a small delay.
|
* Schedule a call to set power mode to off after a small delay.
|
||||||
*/
|
*/
|
||||||
private static void schedulePowerModeOff() {
|
private static void schedulePowerModeOff() {
|
||||||
EXECUTOR.schedule(() -> {
|
EXECUTOR.schedule(new Runnable() {
|
||||||
Ln.i("Forcing screen off");
|
@Override
|
||||||
Device.setScreenPowerMode(Device.POWER_MODE_OFF);
|
public void run() {
|
||||||
|
Ln.i("Forcing screen off");
|
||||||
|
Device.setScreenPowerMode(Device.POWER_MODE_OFF);
|
||||||
|
}
|
||||||
}, 200, TimeUnit.MILLISECONDS);
|
}, 200, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ public final class DesktopConnection implements Closeable {
|
|||||||
|
|
||||||
private static final int DEVICE_NAME_FIELD_LENGTH = 64;
|
private static final int DEVICE_NAME_FIELD_LENGTH = 64;
|
||||||
|
|
||||||
private static final String SOCKET_NAME_PREFIX = "scrcpy";
|
private static final String SOCKET_NAME = "scrcpy";
|
||||||
|
|
||||||
private final LocalSocket videoSocket;
|
private final LocalSocket videoSocket;
|
||||||
private final FileDescriptor videoFd;
|
private final FileDescriptor videoFd;
|
||||||
@ -46,22 +46,12 @@ public final class DesktopConnection implements Closeable {
|
|||||||
return localSocket;
|
return localSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getSocketName(int uid) {
|
public static DesktopConnection open(boolean tunnelForward, boolean control, boolean sendDummyByte) throws IOException {
|
||||||
if (uid == -1) {
|
|
||||||
// If no UID is set, use "scrcpy" to simplify using scrcpy-server alone
|
|
||||||
return SOCKET_NAME_PREFIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SOCKET_NAME_PREFIX + String.format("_%08x", uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DesktopConnection open(int uid, boolean tunnelForward, boolean control, boolean sendDummyByte) throws IOException {
|
|
||||||
String socketName = getSocketName(uid);
|
|
||||||
|
|
||||||
LocalSocket videoSocket;
|
LocalSocket videoSocket;
|
||||||
LocalSocket controlSocket = null;
|
LocalSocket controlSocket = null;
|
||||||
if (tunnelForward) {
|
if (tunnelForward) {
|
||||||
try (LocalServerSocket localServerSocket = new LocalServerSocket(socketName)) {
|
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
|
||||||
|
try {
|
||||||
videoSocket = localServerSocket.accept();
|
videoSocket = localServerSocket.accept();
|
||||||
if (sendDummyByte) {
|
if (sendDummyByte) {
|
||||||
// send one byte so the client may read() to detect a connection error
|
// send one byte so the client may read() to detect a connection error
|
||||||
@ -75,12 +65,14 @@ public final class DesktopConnection implements Closeable {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
localServerSocket.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
videoSocket = connect(socketName);
|
videoSocket = connect(SOCKET_NAME);
|
||||||
if (control) {
|
if (control) {
|
||||||
try {
|
try {
|
||||||
controlSocket = connect(socketName);
|
controlSocket = connect(SOCKET_NAME);
|
||||||
} catch (IOException | RuntimeException e) {
|
} catch (IOException | RuntimeException e) {
|
||||||
videoSocket.close();
|
videoSocket.close();
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -25,7 +25,7 @@ public final class DeviceMessageSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loop() throws IOException, InterruptedException {
|
public void loop() throws IOException, InterruptedException {
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (true) {
|
||||||
String text;
|
String text;
|
||||||
long sequence;
|
long sequence;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
@ -6,7 +6,6 @@ import java.util.List;
|
|||||||
|
|
||||||
public class Options {
|
public class Options {
|
||||||
private Ln.Level logLevel = Ln.Level.DEBUG;
|
private Ln.Level logLevel = Ln.Level.DEBUG;
|
||||||
private int uid = -1; // 31-bit non-negative value, or -1
|
|
||||||
private int maxSize;
|
private int maxSize;
|
||||||
private int bitRate = 8000000;
|
private int bitRate = 8000000;
|
||||||
private int maxFps;
|
private int maxFps;
|
||||||
@ -38,14 +37,6 @@ public class Options {
|
|||||||
this.logLevel = logLevel;
|
this.logLevel = logLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getUid() {
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUid(int uid) {
|
|
||||||
this.uid = uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSize() {
|
public int getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ package com.genymobile.scrcpy;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Position {
|
public class Position {
|
||||||
private final Point point;
|
private Point point;
|
||||||
private final Size screenSize;
|
private Size screenSize;
|
||||||
|
|
||||||
public Position(Point point, Size screenSize) {
|
public Position(Point point, Size screenSize) {
|
||||||
this.point = point;
|
this.point = point;
|
||||||
|
@ -9,7 +9,6 @@ import android.media.MediaCodecList;
|
|||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
@ -76,88 +75,63 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void internalStreamScreen(Device device, FileDescriptor fd) throws IOException {
|
private void internalStreamScreen(Device device, FileDescriptor fd) throws IOException {
|
||||||
MediaCodec codec = createCodec(encoderName);
|
|
||||||
MediaFormat format = createFormat(bitRate, maxFps, codecOptions);
|
MediaFormat format = createFormat(bitRate, maxFps, codecOptions);
|
||||||
IBinder display = createDisplay();
|
|
||||||
device.setRotationListener(this);
|
device.setRotationListener(this);
|
||||||
boolean alive;
|
boolean alive;
|
||||||
try {
|
try {
|
||||||
do {
|
do {
|
||||||
|
MediaCodec codec = createCodec(encoderName);
|
||||||
|
IBinder display = createDisplay();
|
||||||
ScreenInfo screenInfo = device.getScreenInfo();
|
ScreenInfo screenInfo = device.getScreenInfo();
|
||||||
Rect contentRect = screenInfo.getContentRect();
|
Rect contentRect = screenInfo.getContentRect();
|
||||||
|
|
||||||
// include the locked video orientation
|
// include the locked video orientation
|
||||||
Rect videoRect = screenInfo.getVideoSize().toRect();
|
Rect videoRect = screenInfo.getVideoSize().toRect();
|
||||||
format.setInteger(MediaFormat.KEY_WIDTH, videoRect.width());
|
// does not include the locked video orientation
|
||||||
format.setInteger(MediaFormat.KEY_HEIGHT, videoRect.height());
|
Rect unlockedVideoRect = screenInfo.getUnlockedVideoSize().toRect();
|
||||||
|
int videoRotation = screenInfo.getVideoRotation();
|
||||||
|
int layerStack = device.getLayerStack();
|
||||||
|
setSize(format, videoRect.width(), videoRect.height());
|
||||||
|
|
||||||
Surface surface = null;
|
Surface surface = null;
|
||||||
try {
|
try {
|
||||||
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
configure(codec, format);
|
||||||
surface = codec.createInputSurface();
|
surface = codec.createInputSurface();
|
||||||
|
|
||||||
// does not include the locked video orientation
|
|
||||||
Rect unlockedVideoRect = screenInfo.getUnlockedVideoSize().toRect();
|
|
||||||
int videoRotation = screenInfo.getVideoRotation();
|
|
||||||
int layerStack = device.getLayerStack();
|
|
||||||
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
|
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
|
||||||
|
|
||||||
codec.start();
|
codec.start();
|
||||||
|
|
||||||
alive = encode(codec, fd);
|
alive = encode(codec, fd);
|
||||||
// do not call stop() on exception, it would trigger an IllegalStateException
|
// do not call stop() on exception, it would trigger an IllegalStateException
|
||||||
codec.stop();
|
codec.stop();
|
||||||
} catch (MediaCodec.CodecException e) {
|
|
||||||
Ln.e("Codec error: " + e.getMessage());
|
|
||||||
// <https://developer.android.com/reference/android/media/MediaCodec#error-handling>
|
|
||||||
// For simplicity, handle isTransient() like isRecoverable()
|
|
||||||
if (e.isRecoverable() || e.isTransient()) {
|
|
||||||
// Avoid busy-loop if too many errors are generated
|
|
||||||
SystemClock.sleep(50);
|
|
||||||
} else if (!prepareDownsizeRetry(device, screenInfo)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
alive = true;
|
|
||||||
} catch (IllegalStateException | IllegalArgumentException e) {
|
} catch (IllegalStateException | IllegalArgumentException e) {
|
||||||
Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage());
|
Ln.e("Encoding error: " + e.getClass().getName() + ": " + e.getMessage());
|
||||||
if (!prepareDownsizeRetry(device, screenInfo)) {
|
if (!downsizeOnError || firstFrameSent) {
|
||||||
|
// Fail immediately
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int newMaxSize = chooseMaxSizeFallback(screenInfo.getVideoSize());
|
||||||
|
if (newMaxSize == 0) {
|
||||||
|
// Definitively fail
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry with a smaller device size
|
||||||
|
Ln.i("Retrying with -m" + newMaxSize + "...");
|
||||||
|
device.setMaxSize(newMaxSize);
|
||||||
alive = true;
|
alive = true;
|
||||||
} finally {
|
} finally {
|
||||||
codec.reset();
|
destroyDisplay(display);
|
||||||
|
codec.release();
|
||||||
if (surface != null) {
|
if (surface != null) {
|
||||||
surface.release();
|
surface.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (alive);
|
} while (alive);
|
||||||
} finally {
|
} finally {
|
||||||
codec.release();
|
|
||||||
device.setRotationListener(null);
|
device.setRotationListener(null);
|
||||||
SurfaceControl.destroyDisplay(display);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean prepareDownsizeRetry(Device device, ScreenInfo screenInfo) {
|
|
||||||
if (!downsizeOnError || firstFrameSent) {
|
|
||||||
Ln.i("#1 " + downsizeOnError + " " + firstFrameSent);
|
|
||||||
// Must fail immediately
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newMaxSize = chooseMaxSizeFallback(screenInfo.getVideoSize());
|
|
||||||
Ln.i("newMaxSize = " + newMaxSize);
|
|
||||||
if (newMaxSize == 0) {
|
|
||||||
// Must definitively fail
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry with a smaller device size
|
|
||||||
Ln.i("Retrying with -m" + newMaxSize + "...");
|
|
||||||
device.setMaxSize(newMaxSize);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int chooseMaxSizeFallback(Size failedSize) {
|
private static int chooseMaxSizeFallback(Size failedSize) {
|
||||||
int currentMaxSize = Math.max(failedSize.getWidth(), failedSize.getHeight());
|
int currentMaxSize = Math.max(failedSize.getWidth(), failedSize.getHeight());
|
||||||
for (int value : MAX_SIZE_FALLBACK) {
|
for (int value : MAX_SIZE_FALLBACK) {
|
||||||
@ -304,6 +278,15 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
return SurfaceControl.createDisplay("scrcpy", secure);
|
return SurfaceControl.createDisplay("scrcpy", secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void configure(MediaCodec codec, MediaFormat format) {
|
||||||
|
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setSize(MediaFormat format, int width, int height) {
|
||||||
|
format.setInteger(MediaFormat.KEY_WIDTH, width);
|
||||||
|
format.setInteger(MediaFormat.KEY_HEIGHT, height);
|
||||||
|
}
|
||||||
|
|
||||||
private static void setDisplaySurface(IBinder display, Surface surface, int orientation, Rect deviceRect, Rect displayRect, int layerStack) {
|
private static void setDisplaySurface(IBinder display, Surface surface, int orientation, Rect deviceRect, Rect displayRect, int layerStack) {
|
||||||
SurfaceControl.openTransaction();
|
SurfaceControl.openTransaction();
|
||||||
try {
|
try {
|
||||||
@ -314,4 +297,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
SurfaceControl.closeTransaction();
|
SurfaceControl.closeTransaction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void destroyDisplay(IBinder display) {
|
||||||
|
SurfaceControl.destroyDisplay(display);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,12 +66,11 @@ public final class Server {
|
|||||||
|
|
||||||
Thread initThread = startInitThread(options);
|
Thread initThread = startInitThread(options);
|
||||||
|
|
||||||
int uid = options.getUid();
|
|
||||||
boolean tunnelForward = options.isTunnelForward();
|
boolean tunnelForward = options.isTunnelForward();
|
||||||
boolean control = options.getControl();
|
boolean control = options.getControl();
|
||||||
boolean sendDummyByte = options.getSendDummyByte();
|
boolean sendDummyByte = options.getSendDummyByte();
|
||||||
|
|
||||||
try (DesktopConnection connection = DesktopConnection.open(uid, tunnelForward, control, sendDummyByte)) {
|
try (DesktopConnection connection = DesktopConnection.open(tunnelForward, control, sendDummyByte)) {
|
||||||
if (options.getSendDeviceMeta()) {
|
if (options.getSendDeviceMeta()) {
|
||||||
Size videoSize = device.getScreenInfo().getVideoSize();
|
Size videoSize = device.getScreenInfo().getVideoSize();
|
||||||
connection.sendDeviceMeta(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
connection.sendDeviceMeta(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
||||||
@ -88,7 +87,12 @@ public final class Server {
|
|||||||
controllerThread = startController(controller);
|
controllerThread = startController(controller);
|
||||||
deviceMessageSenderThread = startDeviceMessageSender(controller.getSender());
|
deviceMessageSenderThread = startDeviceMessageSender(controller.getSender());
|
||||||
|
|
||||||
device.setClipboardListener(text -> controller.getSender().pushClipboardText(text));
|
device.setClipboardListener(new Device.ClipboardListener() {
|
||||||
|
@Override
|
||||||
|
public void onClipboardTextChanged(String text) {
|
||||||
|
controller.getSender().pushClipboardText(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -110,18 +114,26 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Thread startInitThread(final Options options) {
|
private static Thread startInitThread(final Options options) {
|
||||||
Thread thread = new Thread(() -> initAndCleanUp(options));
|
Thread thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
initAndCleanUp(options);
|
||||||
|
}
|
||||||
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Thread startController(final Controller controller) {
|
private static Thread startController(final Controller controller) {
|
||||||
Thread thread = new Thread(() -> {
|
Thread thread = new Thread(new Runnable() {
|
||||||
try {
|
@Override
|
||||||
controller.control();
|
public void run() {
|
||||||
} catch (IOException e) {
|
try {
|
||||||
// this is expected on close
|
controller.control();
|
||||||
Ln.d("Controller stopped");
|
} catch (IOException e) {
|
||||||
|
// this is expected on close
|
||||||
|
Ln.d("Controller stopped");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
@ -129,12 +141,15 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Thread startDeviceMessageSender(final DeviceMessageSender sender) {
|
private static Thread startDeviceMessageSender(final DeviceMessageSender sender) {
|
||||||
Thread thread = new Thread(() -> {
|
Thread thread = new Thread(new Runnable() {
|
||||||
try {
|
@Override
|
||||||
sender.loop();
|
public void run() {
|
||||||
} catch (IOException | InterruptedException e) {
|
try {
|
||||||
// this is expected on close
|
sender.loop();
|
||||||
Ln.d("Device message sender stopped");
|
} catch (IOException | InterruptedException e) {
|
||||||
|
// this is expected on close
|
||||||
|
Ln.d("Device message sender stopped");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
@ -163,13 +178,6 @@ public final class Server {
|
|||||||
String key = arg.substring(0, equalIndex);
|
String key = arg.substring(0, equalIndex);
|
||||||
String value = arg.substring(equalIndex + 1);
|
String value = arg.substring(equalIndex + 1);
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "uid":
|
|
||||||
int uid = Integer.parseInt(value, 0x10);
|
|
||||||
if (uid < -1) {
|
|
||||||
throw new IllegalArgumentException("uid may not be negative (except -1 for 'none'): " + uid);
|
|
||||||
}
|
|
||||||
options.setUid(uid);
|
|
||||||
break;
|
|
||||||
case "log_level":
|
case "log_level":
|
||||||
Ln.Level level = Ln.Level.valueOf(value.toUpperCase(Locale.ENGLISH));
|
Ln.Level level = Ln.Level.valueOf(value.toUpperCase(Locale.ENGLISH));
|
||||||
options.setLogLevel(level);
|
options.setLogLevel(level);
|
||||||
@ -311,9 +319,12 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
public static void main(String... args) throws Exception {
|
||||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||||
Ln.e("Exception on thread " + t, e);
|
@Override
|
||||||
suggestFix(e);
|
public void uncaughtException(Thread t, Throwable e) {
|
||||||
|
Ln.e("Exception on thread " + t, e);
|
||||||
|
suggestFix(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Options options = createOptions(args);
|
Options options = createOptions(args);
|
||||||
|
Reference in New Issue
Block a user