Compare commits

..

1 Commits

Author SHA1 Message Date
da8be4c8d5 Fix compilation errors with old SDL versions
SDL_PixelFormatEnum has been introduced in SDL 2.0.10:
<cc6a8ac87e>

SDL_PIXELFORMAT_BGR444 has been introduced in SDL 2.0.12:
<a1c11854f2>

Fixes #2777 <https://github.com/Genymobile/scrcpy/issues/2777>
2021-11-14 15:18:15 +01:00
27 changed files with 276 additions and 675 deletions

View File

@ -416,27 +416,17 @@ autoadb scrcpy -s '{}'
To connect to a remote device, it is possible to connect a local `adb` client to To connect to a remote device, it is possible to connect a local `adb` client to
a remote `adb` server (provided they use the same version of the _adb_ a remote `adb` server (provided they use the same version of the _adb_
protocol). protocol):
First, make sure the ADB server is running on the remote computer:
```bash ```bash
adb start-server adb kill-server # kill the local adb server on 5037
``` ssh -CN -L5037:localhost:5037 -R27183:localhost:27183 your_remote_computer
Then, establish a SSH tunnel:
```bash
# local 5038 --> remote 5037
# local 27183 <-- remote 27183
ssh -CN -L5038:localhost:5037 -R27183:localhost:27183 your_remote_computer
# keep this open # keep this open
``` ```
From another terminal, run scrcpy: From another terminal:
```bash ```bash
export ADB_SERVER_SOCKET=tcp:localhost:5038
scrcpy scrcpy
``` ```
@ -444,16 +434,14 @@ To avoid enabling remote port forwarding, you could force a forward connection
instead (notice the `-L` instead of `-R`): instead (notice the `-L` instead of `-R`):
```bash ```bash
# local 5038 --> remote 5037 adb kill-server # kill the local adb server on 5037
# local 27183 --> remote 27183 ssh -CN -L5037:localhost:5037 -L27183:localhost:27183 your_remote_computer
ssh -CN -L5038:localhost:5037 -L27183:localhost:27183 your_remote_computer
# keep this open # keep this open
``` ```
From another terminal, run scrcpy: From another terminal:
```bash ```bash
export ADB_SERVER_SOCKET=tcp:localhost:5038
scrcpy --force-adb-forward scrcpy --force-adb-forward
``` ```

View File

@ -7,7 +7,6 @@
#include "util/file.h" #include "util/file.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#include "util/str.h" #include "util/str.h"
static const char *adb_command; static const char *adb_command;
@ -110,9 +109,9 @@ show_adb_err_msg(enum sc_process_result err, const char *const argv[]) {
free(buf); free(buf);
} }
static sc_pid sc_pid
adb_execute_p(const char *serial, const char *const adb_cmd[], adb_execute_p(const char *serial, const char *const adb_cmd[],
size_t len, unsigned inherit, sc_pipe *pout) { size_t len, sc_pipe *pin, sc_pipe *pout, sc_pipe *perr) {
int i; int i;
sc_pid pid; sc_pid pid;
@ -133,7 +132,7 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
memcpy(&argv[i], adb_cmd, len * sizeof(const char *)); memcpy(&argv[i], adb_cmd, len * sizeof(const char *));
argv[len + i] = NULL; argv[len + i] = NULL;
enum sc_process_result r = enum sc_process_result r =
sc_process_execute_p(argv, &pid, inherit, NULL, pout, NULL); sc_process_execute_p(argv, &pid, pin, pout, perr);
if (r != SC_PROCESS_SUCCESS) { if (r != SC_PROCESS_SUCCESS) {
show_adb_err_msg(r, argv); show_adb_err_msg(r, argv);
pid = SC_PROCESS_NONE; pid = SC_PROCESS_NONE;
@ -144,54 +143,50 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
} }
sc_pid sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len, adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {
unsigned inherit) { return adb_execute_p(serial, adb_cmd, len, NULL, NULL, NULL);
return adb_execute_p(serial, adb_cmd, len, inherit, NULL);
} }
static sc_pid sc_pid
adb_exec_forward(const char *serial, uint16_t local_port, adb_forward(const char *serial, uint16_t local_port,
const char *device_socket_name, unsigned inherit) { const char *device_socket_name) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"forward", local, remote}; const char *const adb_cmd[] = {"forward", local, remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_forward_remove(const char *serial, uint16_t local_port, adb_forward_remove(const char *serial, uint16_t local_port) {
unsigned inherit) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
const char *const adb_cmd[] = {"forward", "--remove", local}; const char *const adb_cmd[] = {"forward", "--remove", local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_reverse(const char *serial, const char *device_socket_name, adb_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port, unsigned inherit) { uint16_t local_port) {
char local[4 + 5 + 1]; // tcp:PORT char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
sprintf(local, "tcp:%" PRIu16, local_port); sprintf(local, "tcp:%" PRIu16, local_port);
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", remote, local}; const char *const adb_cmd[] = {"reverse", remote, local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_reverse_remove(const char *serial, const char *device_socket_name, adb_reverse_remove(const char *serial, const char *device_socket_name) {
unsigned inherit) {
char remote[108 + 14 + 1]; // localabstract:NAME char remote[108 + 14 + 1]; // localabstract:NAME
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name); snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", "--remove", remote}; const char *const adb_cmd[] = {"reverse", "--remove", remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
} }
static sc_pid sc_pid
adb_exec_push(const char *serial, const char *local, const char *remote, adb_push(const char *serial, const char *local, const char *remote) {
unsigned inherit) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the paths must be quoted // Windows will parse the string, so the paths must be quoted
// (see sys/win/command.c) // (see sys/win/command.c)
@ -207,7 +202,7 @@ adb_exec_push(const char *serial, const char *local, const char *remote,
#endif #endif
const char *const adb_cmd[] = {"push", local, remote}; const char *const adb_cmd[] = {"push", local, remote};
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__ #ifdef __WINDOWS__
free((void *) remote); free((void *) remote);
@ -217,8 +212,8 @@ adb_exec_push(const char *serial, const char *local, const char *remote,
return pid; return pid;
} }
static sc_pid sc_pid
adb_exec_install(const char *serial, const char *local, unsigned inherit) { adb_install(const char *serial, const char *local) {
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Windows will parse the string, so the local name must be quoted // Windows will parse the string, so the local name must be quoted
// (see sys/win/command.c) // (see sys/win/command.c)
@ -229,7 +224,7 @@ adb_exec_install(const char *serial, const char *local, unsigned inherit) {
#endif #endif
const char *const adb_cmd[] = {"install", "-r", local}; const char *const adb_cmd[] = {"install", "-r", local};
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit); sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
#ifdef __WINDOWS__ #ifdef __WINDOWS__
free((void *) local); free((void *) local);
@ -238,79 +233,45 @@ adb_exec_install(const char *serial, const char *local, unsigned inherit) {
return pid; return pid;
} }
static sc_pid static ssize_t
adb_exec_get_serialno(unsigned inherit, sc_pipe *pout) { adb_execute_for_output(const char *serial, const char *const adb_cmd[],
const char *const adb_cmd[] = {"get-serialno"}; size_t adb_cmd_len, char *buf, size_t buf_len,
return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), inherit, pout); const char *name) {
sc_pipe pout;
sc_pid pid = adb_execute_p(serial, adb_cmd, adb_cmd_len, NULL, &pout, NULL);
ssize_t r = sc_pipe_read_all(pout, buf, buf_len);
sc_pipe_close(pout);
if (!sc_process_check_success(pid, name, true)) {
return -1;
}
return r;
} }
bool static size_t
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port, truncate_first_line(char *data, size_t len) {
const char *device_socket_name, unsigned inherit) { data[len - 1] = '\0';
sc_pid pid = char *eol = strpbrk(data, "\r\n");
adb_exec_forward(serial, local_port, device_socket_name, inherit); if (eol) {
return sc_process_check_success_intr(intr, pid, "adb forward", true); *eol = '\0';
} len = eol - data;
}
bool return len;
adb_forward_remove(struct sc_intr *intr, const char *serial,
uint16_t local_port, unsigned inherit) {
sc_pid pid = adb_exec_forward_remove(serial, local_port, inherit);
return sc_process_check_success_intr(intr, pid, "adb forward --remove",
true);
}
bool
adb_reverse(struct sc_intr *intr, const char *serial,
const char *device_socket_name, uint16_t local_port,
unsigned inherit) {
sc_pid pid =
adb_exec_reverse(serial, device_socket_name, local_port, inherit);
return sc_process_check_success_intr(intr, pid, "adb reverse", true);
}
bool
adb_reverse_remove(struct sc_intr *intr, const char *serial,
const char *device_socket_name, unsigned inherit) {
sc_pid pid = adb_exec_reverse_remove(serial, device_socket_name, inherit);
return sc_process_check_success_intr(intr, pid, "adb reverse --remove",
true);
}
bool
adb_push(struct sc_intr *intr, const char *serial, const char *local,
const char *remote, unsigned inherit) {
sc_pid pid = adb_exec_push(serial, local, remote, inherit);
return sc_process_check_success_intr(intr, pid, "adb push", true);
}
bool
adb_install(struct sc_intr *intr, const char *serial, const char *local,
unsigned inherit) {
sc_pid pid = adb_exec_install(serial, local, inherit);
return sc_process_check_success_intr(intr, pid, "adb install", true);
} }
char * char *
adb_get_serialno(struct sc_intr *intr, unsigned inherit) { adb_get_serialno(void) {
sc_pipe pout;
sc_pid pid = adb_exec_get_serialno(inherit, &pout);
if (pid == SC_PROCESS_NONE) {
LOGE("Could not execute \"adb get-serialno\"");
return NULL;
}
char buf[128]; char buf[128];
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf));
sc_pipe_close(pout);
bool ok = const char *const adb_cmd[] = {"get-serialno"};
sc_process_check_success_intr(intr, pid, "adb get-serialno", true); ssize_t r = adb_execute_for_output(NULL, adb_cmd, ARRAY_LEN(adb_cmd),
if (!ok) { buf, sizeof(buf), "get-serialno");
if (r <= 0) {
return NULL; return NULL;
} }
sc_str_truncate(buf, r, " \r\n"); truncate_first_line(buf, r);
return strdup(buf); return strdup(buf);
} }

View File

@ -6,43 +6,37 @@
#include <stdbool.h> #include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#include "util/intr.h" #include "util/process.h"
sc_pid sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len, adb_execute(const char *serial, const char *const adb_cmd[], size_t len);
unsigned inherit);
bool sc_pid
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port, adb_execute_p(const char *serial, const char *const adb_cmd[],
const char *device_socket_name, unsigned inherit); size_t len, sc_pipe *pin, sc_pipe *pout, sc_pipe *perr);
bool sc_pid
adb_forward_remove(struct sc_intr *intr, const char *serial, adb_forward(const char *serial, uint16_t local_port,
uint16_t local_port, unsigned inherit); const char *device_socket_name);
bool sc_pid
adb_reverse(struct sc_intr *intr, const char *serial, adb_forward_remove(const char *serial, uint16_t local_port);
const char *device_socket_name, uint16_t local_port,
unsigned inherit);
bool sc_pid
adb_reverse_remove(struct sc_intr *intr, const char *serial, adb_reverse(const char *serial, const char *device_socket_name,
const char *device_socket_name, unsigned inherit); uint16_t local_port);
bool sc_pid
adb_push(struct sc_intr *intr, const char *serial, const char *local, adb_reverse_remove(const char *serial, const char *device_socket_name);
const char *remote, unsigned inherit);
bool sc_pid
adb_install(struct sc_intr *intr, const char *serial, const char *local, adb_push(const char *serial, const char *local, const char *remote);
unsigned inherit);
/** sc_pid
* Execute `adb get-serialno` adb_install(const char *serial, const char *local);
*
* Return the result, to be freed by the caller, or NULL on error. // Return the result of "adb get-serialno".
*/
char * char *
adb_get_serialno(struct sc_intr *intr, unsigned inherit); adb_get_serialno(void);
#endif #endif

View File

@ -9,6 +9,33 @@
#define SC_SOCKET_NAME "scrcpy" #define SC_SOCKET_NAME "scrcpy"
static bool
enable_tunnel_reverse(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_reverse(serial, SC_SOCKET_NAME, local_port);
return sc_process_check_success_intr(intr, pid, "adb reverse");
}
static bool
disable_tunnel_reverse(struct sc_intr *intr, const char *serial) {
sc_pid pid = adb_reverse_remove(serial, SC_SOCKET_NAME);
return sc_process_check_success_intr(intr, pid, "adb reverse --remove");
}
static bool
enable_tunnel_forward(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_forward(serial, local_port, SC_SOCKET_NAME);
return sc_process_check_success_intr(intr, pid, "adb forward");
}
static bool
disable_tunnel_forward(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_forward_remove(serial, local_port);
return sc_process_check_success_intr(intr, pid, "adb forward --remove");
}
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);
@ -20,7 +47,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
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 (!adb_reverse(intr, serial, SC_SOCKET_NAME, port, SC_STDERR)) { if (!enable_tunnel_reverse(intr, serial, port)) {
// 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 +78,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 (!adb_reverse_remove(intr, serial, SC_SOCKET_NAME, SC_STDERR)) { if (!disable_tunnel_reverse(intr, serial)) {
LOGW("Could not remove reverse tunnel on port %" PRIu16, port); LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
} }
@ -81,7 +108,7 @@ enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel,
uint16_t port = port_range.first; uint16_t port = port_range.first;
for (;;) { for (;;) {
if (adb_forward(intr, serial, port, SC_SOCKET_NAME, SC_STDERR)) { if (enable_tunnel_forward(intr, serial, port)) {
// success // success
tunnel->local_port = port; tunnel->local_port = port;
tunnel->enabled = true; tunnel->enabled = true;
@ -146,9 +173,9 @@ sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
bool ret; bool ret;
if (tunnel->forward) { if (tunnel->forward) {
ret = adb_forward_remove(intr, serial, tunnel->local_port, SC_STDERR); ret = disable_tunnel_forward(intr, serial, tunnel->local_port);
} else { } else {
ret = adb_reverse_remove(intr, serial, SC_SOCKET_NAME, SC_STDERR); ret = disable_tunnel_reverse(intr, serial);
assert(tunnel->server_socket != SC_SOCKET_NONE); assert(tunnel->server_socket != SC_SOCKET_NONE);
if (!net_close(tunnel->server_socket)) { if (!net_close(tunnel->server_socket)) {

View File

@ -569,10 +569,6 @@ sc_getopt_adapter_create_longopts(void) {
size_t out_idx = 0; size_t out_idx = 0;
for (size_t i = 0; i < ARRAY_LEN(options); ++i) { for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
const struct sc_option *in = &options[i]; const struct sc_option *in = &options[i];
// If longopt_id is set, then longopt must be set
assert(!in->longopt_id || in->longopt);
if (!in->longopt) { if (!in->longopt) {
// The longopts array must only contain long options // The longopts array must only contain long options
continue; continue;
@ -675,12 +671,12 @@ print_option_usage_header(const struct sc_option *opt) {
} }
} }
printf("\n %s\n", buf.s); fprintf(stderr, "\n %s\n", buf.s);
free(buf.s); free(buf.s);
return; return;
error: error:
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
} }
static void static void
@ -696,11 +692,11 @@ print_option_usage(const struct sc_option *opt, unsigned cols) {
char *text = sc_str_wrap_lines(opt->text, cols, 8); char *text = sc_str_wrap_lines(opt->text, cols, 8);
if (!text) { if (!text) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", text); fprintf(stderr, "%s\n", text);
free(text); free(text);
} }
@ -711,11 +707,11 @@ print_shortcuts_intro(unsigned cols) {
"(left) Alt or (left) Super, but it can be configured by " "(left) Alt or (left) Super, but it can be configured by "
"--shortcut-mod (see above).", cols, 4); "--shortcut-mod (see above).", cols, 4);
if (!intro) { if (!intro) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", intro); fprintf(stderr, "%s\n", intro);
free(intro); free(intro);
} }
@ -725,21 +721,21 @@ print_shortcut(const struct sc_shortcut *shortcut, unsigned cols) {
assert(shortcut->shortcuts[0]); // At least one shortcut assert(shortcut->shortcuts[0]); // At least one shortcut
assert(shortcut->text); assert(shortcut->text);
printf("\n"); fprintf(stderr, "\n");
unsigned i = 0; unsigned i = 0;
while (shortcut->shortcuts[i]) { while (shortcut->shortcuts[i]) {
printf(" %s\n", shortcut->shortcuts[i]); fprintf(stderr, " %s\n", shortcut->shortcuts[i]);
++i; ++i;
}; };
char *text = sc_str_wrap_lines(shortcut->text, cols, 8); char *text = sc_str_wrap_lines(shortcut->text, cols, 8);
if (!text) { if (!text) {
printf("<ERROR>\n"); fprintf(stderr, "<ERROR>\n");
return; return;
} }
printf("%s\n", text); fprintf(stderr, "%s\n", text);
free(text); free(text);
} }
@ -763,14 +759,14 @@ scrcpy_print_usage(const char *arg0) {
} }
} }
printf("Usage: %s [options]\n\n" fprintf(stderr, "Usage: %s [options]\n\n"
"Options:\n", arg0); "Options:\n", arg0);
for (size_t i = 0; i < ARRAY_LEN(options); ++i) { for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
print_option_usage(&options[i], cols); print_option_usage(&options[i], cols);
} }
// Print shortcuts section // Print shortcuts section
printf("\nShortcuts:\n\n"); fprintf(stderr, "\nShortcuts:\n\n");
print_shortcuts_intro(cols); print_shortcuts_intro(cols);
for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) { for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) {
print_shortcut(&shortcuts[i], cols); print_shortcut(&shortcuts[i], cols);

View File

@ -5,7 +5,6 @@
#include "adb.h" #include "adb.h"
#include "util/log.h" #include "util/log.h"
#include "util/process_intr.h"
#define DEFAULT_PUSH_TARGET "/sdcard/Download/" #define DEFAULT_PUSH_TARGET "/sdcard/Download/"
@ -17,7 +16,6 @@ file_handler_request_destroy(struct file_handler_request *req) {
bool bool
file_handler_init(struct file_handler *file_handler, const char *serial, file_handler_init(struct file_handler *file_handler, const char *serial,
const char *push_target) { const char *push_target) {
assert(serial);
cbuf_init(&file_handler->queue); cbuf_init(&file_handler->queue);
@ -32,26 +30,23 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
return false; return false;
} }
ok = sc_intr_init(&file_handler->intr); if (serial) {
if (!ok) { file_handler->serial = strdup(serial);
LOGE("Could not create intr"); if (!file_handler->serial) {
sc_cond_destroy(&file_handler->event_cond); LOGW("Could not strdup serial");
sc_mutex_destroy(&file_handler->mutex); sc_cond_destroy(&file_handler->event_cond);
} sc_mutex_destroy(&file_handler->mutex);
return false;
file_handler->serial = strdup(serial); }
if (!file_handler->serial) { } else {
LOGE("Could not strdup serial"); file_handler->serial = NULL;
sc_intr_destroy(&file_handler->intr);
sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex);
return false;
} }
// lazy initialization // lazy initialization
file_handler->initialized = false; file_handler->initialized = false;
file_handler->stopped = false; file_handler->stopped = false;
file_handler->current_process = SC_PROCESS_NONE;
file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET; file_handler->push_target = push_target ? push_target : DEFAULT_PUSH_TARGET;
@ -62,7 +57,6 @@ void
file_handler_destroy(struct file_handler *file_handler) { file_handler_destroy(struct file_handler *file_handler) {
sc_cond_destroy(&file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
sc_mutex_destroy(&file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
sc_intr_destroy(&file_handler->intr);
free(file_handler->serial); free(file_handler->serial);
struct file_handler_request req; struct file_handler_request req;
@ -71,6 +65,16 @@ file_handler_destroy(struct file_handler *file_handler) {
} }
} }
static sc_pid
install_apk(const char *serial, const char *file) {
return adb_install(serial, file);
}
static sc_pid
push_file(const char *serial, const char *file, const char *push_target) {
return adb_push(serial, file, push_target);
}
bool bool
file_handler_request(struct file_handler *file_handler, file_handler_request(struct file_handler *file_handler,
file_handler_action_t action, char *file) { file_handler_action_t action, char *file) {
@ -102,16 +106,10 @@ file_handler_request(struct file_handler *file_handler,
static int static int
run_file_handler(void *data) { run_file_handler(void *data) {
struct file_handler *file_handler = data; struct file_handler *file_handler = data;
struct sc_intr *intr = &file_handler->intr;
const char *serial = file_handler->serial;
assert(serial);
const char *push_target = file_handler->push_target;
assert(push_target);
for (;;) { for (;;) {
sc_mutex_lock(&file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->current_process = SC_PROCESS_NONE;
while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) { while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) {
sc_cond_wait(&file_handler->event_cond, &file_handler->mutex); sc_cond_wait(&file_handler->event_cond, &file_handler->mutex);
} }
@ -124,28 +122,43 @@ run_file_handler(void *data) {
bool non_empty = cbuf_take(&file_handler->queue, &req); bool non_empty = cbuf_take(&file_handler->queue, &req);
assert(non_empty); assert(non_empty);
(void) non_empty; (void) non_empty;
sc_pid pid;
if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file);
pid = install_apk(file_handler->serial, req.file);
} else {
LOGI("Pushing %s...", req.file);
pid = push_file(file_handler->serial, req.file,
file_handler->push_target);
}
file_handler->current_process = pid;
sc_mutex_unlock(&file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
if (req.action == ACTION_INSTALL_APK) { if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file); if (sc_process_check_success(pid, "adb install", false)) {
bool ok =
adb_install(intr, serial, req.file, SC_STDOUT | SC_STDERR);
if (ok) {
LOGI("%s successfully installed", req.file); LOGI("%s successfully installed", req.file);
} else { } else {
LOGE("Failed to install %s", req.file); LOGE("Failed to install %s", req.file);
} }
} else { } else {
LOGI("Pushing %s...", req.file); if (sc_process_check_success(pid, "adb push", false)) {
bool ok = adb_push(intr, serial, req.file, push_target, LOGI("%s successfully pushed to %s", req.file,
SC_STDOUT | SC_STDERR); file_handler->push_target);
if (ok) {
LOGI("%s successfully pushed to %s", req.file, push_target);
} else { } else {
LOGE("Failed to push %s to %s", req.file, push_target); LOGE("Failed to push %s to %s", req.file,
file_handler->push_target);
} }
} }
sc_mutex_lock(&file_handler->mutex);
// Close the process (it is necessarily already terminated)
// Execute this call with mutex locked to avoid race conditions with
// file_handler_stop()
sc_process_close(file_handler->current_process);
file_handler->current_process = SC_PROCESS_NONE;
sc_mutex_unlock(&file_handler->mutex);
file_handler_request_destroy(&req); file_handler_request_destroy(&req);
} }
return 0; return 0;
@ -170,7 +183,11 @@ file_handler_stop(struct file_handler *file_handler) {
sc_mutex_lock(&file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->stopped = true; file_handler->stopped = true;
sc_cond_signal(&file_handler->event_cond); sc_cond_signal(&file_handler->event_cond);
sc_intr_interrupt(&file_handler->intr); if (file_handler->current_process != SC_PROCESS_NONE) {
if (!sc_process_terminate(file_handler->current_process)) {
LOGW("Could not terminate push/install process");
}
}
sc_mutex_unlock(&file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
} }

View File

@ -8,7 +8,6 @@
#include "adb.h" #include "adb.h"
#include "util/cbuf.h" #include "util/cbuf.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/intr.h"
typedef enum { typedef enum {
ACTION_INSTALL_APK, ACTION_INSTALL_APK,
@ -30,9 +29,8 @@ struct file_handler {
sc_cond event_cond; sc_cond event_cond;
bool stopped; bool stopped;
bool initialized; bool initialized;
sc_pid current_process;
struct file_handler_request_queue queue; struct file_handler_request_queue queue;
struct sc_intr intr;
}; };
bool bool

View File

@ -47,9 +47,6 @@ main(int argc, char *argv[]) {
setbuf(stderr, NULL); setbuf(stderr, NULL);
#endif #endif
printf("scrcpy " SCRCPY_VERSION
" <https://github.com/Genymobile/scrcpy>\n");
struct scrcpy_cli_args args = { struct scrcpy_cli_args args = {
.opts = scrcpy_options_default, .opts = scrcpy_options_default,
.help = false, .help = false,
@ -76,6 +73,8 @@ main(int argc, char *argv[]) {
return 0; return 0;
} }
LOGI("scrcpy " SCRCPY_VERSION " <https://github.com/Genymobile/scrcpy>");
#ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL #ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL
av_register_all(); av_register_all();
#endif #endif

View File

@ -394,11 +394,8 @@ scrcpy(struct scrcpy_options *options) {
// It is necessarily initialized here, since the device is connected // It is necessarily initialized here, since the device is connected
struct sc_server_info *info = &s->server.info; struct sc_server_info *info = &s->server.info;
const char *serial = s->server.params.serial;
assert(serial);
if (options->display && options->control) { if (options->display && options->control) {
if (!file_handler_init(&s->file_handler, serial, if (!file_handler_init(&s->file_handler, options->serial,
options->push_target)) { options->push_target)) {
goto end; goto end;
} }
@ -519,7 +516,21 @@ scrcpy(struct scrcpy_options *options) {
#ifdef HAVE_AOA_HID #ifdef HAVE_AOA_HID
bool aoa_hid_ok = false; bool aoa_hid_ok = false;
char *serialno = NULL;
const char *serial = options->serial;
if (!serial) {
serialno = adb_get_serialno();
if (!serialno) {
LOGE("Could not get device serial");
goto aoa_hid_end;
}
serial = serialno;
LOGI("Device serial: %s", serial);
}
bool ok = sc_aoa_init(&s->aoa, serial); bool ok = sc_aoa_init(&s->aoa, serial);
free(serialno);
if (!ok) { if (!ok) {
goto aoa_hid_end; goto aoa_hid_end;
} }

View File

@ -112,10 +112,9 @@ push_server(struct sc_intr *intr, const char *serial) {
free(server_path); free(server_path);
return false; return false;
} }
bool ok = adb_push(intr, serial, server_path, SC_DEVICE_SERVER_PATH, sc_pid pid = adb_push(serial, server_path, SC_DEVICE_SERVER_PATH);
SC_STDERR);
free(server_path); free(server_path);
return ok; return sc_process_check_success_intr(intr, pid, "adb push");
} }
static const char * static const char *
@ -199,8 +198,7 @@ execute_server(struct sc_server *server,
// Port: 5005 // Port: 5005
// Then click on "Debug" // Then click on "Debug"
#endif #endif
// Inherit both stdout and stderr (all server logs are printed to stdout) return adb_execute(serial, cmd, ARRAY_LEN(cmd));
return adb_execute(serial, cmd, ARRAY_LEN(cmd), SC_STDOUT | SC_STDERR);
} }
static bool static bool
@ -236,12 +234,6 @@ connect_to_server(struct sc_server *server, uint32_t attempts, sc_tick delay) {
net_close(socket); net_close(socket);
} }
if (sc_intr_is_interrupted(&server->intr)) {
// Stop immediately
break;
}
if (attempts) { if (attempts) {
sc_mutex_lock(&server->mutex); sc_mutex_lock(&server->mutex);
sc_tick deadline = sc_tick_now() + delay; sc_tick deadline = sc_tick_now() + delay;
@ -424,43 +416,17 @@ sc_server_on_terminated(void *userdata) {
LOGD("Server terminated"); LOGD("Server terminated");
} }
static bool
sc_server_fill_serial(struct sc_server *server) {
// Retrieve the actual device immediately if not provided, so that all
// future adb commands are executed for this specific device, even if other
// devices are connected afterwards (without "more than one
// device/emulator" error)
if (!server->params.serial) {
// The serial is owned by sc_server_params, and will be freed on destroy
server->params.serial = adb_get_serialno(&server->intr, SC_STDERR);
if (!server->params.serial) {
LOGE("Could not get device serial");
return false;
}
}
return true;
}
static int static int
run_server(void *data) { run_server(void *data) {
struct sc_server *server = data; struct sc_server *server = data;
if (!sc_server_fill_serial(server)) {
goto error_connection_failed;
}
const struct sc_server_params *params = &server->params; const struct sc_server_params *params = &server->params;
LOGD("Device serial: %s", params->serial);
bool ok = push_server(&server->intr, params->serial); bool ok = push_server(&server->intr, params->serial);
if (!ok) { if (!ok) {
goto error_connection_failed; goto error_connection_failed;
} }
LOGI("Server pushed");
ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, params->serial, ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, params->serial,
params->port_range, params->force_adb_forward); params->port_range, params->force_adb_forward);
if (!ok) { if (!ok) {

View File

@ -11,16 +11,8 @@
#include "util/log.h" #include "util/log.h"
enum sc_process_result enum sc_process_result
sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit, sc_process_execute_p(const char *const argv[], sc_pid *pid,
int *pin, int *pout, int *perr) { int *pin, int *pout, int *perr) {
bool inherit_stdout = inherit & SC_STDOUT;
bool inherit_stderr = inherit & SC_STDERR;
// If pout is defined, then inherit MUST NOT contain SC_STDOUT.
assert(!pout || !inherit_stdout);
// If perr is defined, then inherit MUST NOT contain SC_STDERR.
assert(!perr || !inherit_stderr);
int in[2]; int in[2];
int out[2]; int out[2];
int err[2]; int err[2];
@ -98,31 +90,20 @@ sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit,
} }
close(in[1]); close(in[1]);
} }
// Do not close stdin in the child process, this makes adb fail on
// Linux
if (pout) { if (pout) {
if (out[1] != STDOUT_FILENO) { if (out[1] != STDOUT_FILENO) {
dup2(out[1], STDOUT_FILENO); dup2(out[1], STDOUT_FILENO);
close(out[1]); close(out[1]);
} }
close(out[0]); close(out[0]);
} else if (!inherit_stdout) {
// Close stdout in the child process
close(STDOUT_FILENO);
} }
if (perr) { if (perr) {
if (err[1] != STDERR_FILENO) { if (err[1] != STDERR_FILENO) {
dup2(err[1], STDERR_FILENO); dup2(err[1], STDERR_FILENO);
close(err[1]); close(err[1]);
} }
close(err[0]); close(err[0]);
} else if (!inherit_stderr) {
// Close stderr in the child process
close(STDERR_FILENO);
} }
close(internal[0]); close(internal[0]);
enum sc_process_result err; enum sc_process_result err;
if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) { if (fcntl(internal[1], F_SETFD, FD_CLOEXEC) == 0) {

View File

@ -1,10 +1,5 @@
// <https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873>
#define _WIN32_WINNT 0x0600 // For extended process API
#include "util/process.h" #include "util/process.h"
#include <processthreadsapi.h>
#include <assert.h> #include <assert.h>
#include "util/log.h" #include "util/log.h"
@ -27,21 +22,8 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
} }
enum sc_process_result enum sc_process_result
sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit, sc_process_execute_p(const char *const argv[], HANDLE *handle,
HANDLE *pin, HANDLE *pout, HANDLE *perr) { HANDLE *pin, HANDLE *pout, HANDLE *perr) {
bool inherit_stdout = inherit & SC_STDOUT;
bool inherit_stderr = inherit & SC_STDERR;
// If pout is defined, then inherit MUST NOT contain SC_STDOUT.
assert(!pout || !inherit_stdout);
// If perr is defined, then inherit MUST NOT contain SC_STDERR.
assert(!perr || !inherit_stderr);
// Add 1 per non-NULL pointer
unsigned handle_count = !!pin
+ (pout || inherit_stdout)
+ (perr || inherit_stderr);
enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC; enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
@ -83,92 +65,45 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit,
} }
} }
STARTUPINFOEXW si; STARTUPINFOW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si)); memset(&si, 0, sizeof(si));
si.StartupInfo.cb = sizeof(si); si.cb = sizeof(si);
HANDLE handles[3]; if (pin || pout || perr) {
si.dwFlags = STARTF_USESTDHANDLES;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
// Must be set even if handle_count == 0, so that stdin, stdout and stderr
// are NOT inherited in that case
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (handle_count) {
unsigned i = 0;
if (pin) { if (pin) {
si.StartupInfo.hStdInput = stdin_read_handle; si.hStdInput = stdin_read_handle;
handles[i++] = si.StartupInfo.hStdInput;
} }
if (pout || inherit_stdout) { if (pout) {
si.StartupInfo.hStdOutput = pout ? stdout_write_handle si.hStdOutput = stdout_write_handle;
: GetStdHandle(STD_OUTPUT_HANDLE);
handles[i++] = si.StartupInfo.hStdOutput;
} }
if (perr || inherit_stderr) { if (perr) {
si.StartupInfo.hStdError = perr ? stderr_write_handle si.hStdError = stderr_write_handle;
: GetStdHandle(STD_ERROR_HANDLE);
handles[i++] = si.StartupInfo.hStdError;
} }
SIZE_T size;
// Call it once to know the required buffer size
BOOL ok =
InitializeProcThreadAttributeList(NULL, 1, 0, &size)
|| GetLastError() == ERROR_INSUFFICIENT_BUFFER;
if (!ok) {
goto error_close_stderr;
}
lpAttributeList = malloc(size);
if (!lpAttributeList) {
goto error_close_stderr;
}
ok = InitializeProcThreadAttributeList(lpAttributeList, 1, 0, &size);
if (!ok) {
free(lpAttributeList);
goto error_close_stderr;
}
ok = UpdateProcThreadAttribute(lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
handles, handle_count * sizeof(HANDLE),
NULL, NULL);
if (!ok) {
goto error_free_attribute_list;
}
si.lpAttributeList = lpAttributeList;
} }
char *cmd = malloc(CMD_MAX_LEN); char *cmd = malloc(CMD_MAX_LEN);
if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) { if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) {
goto error_free_attribute_list; *handle = NULL;
goto error_close_stderr;
} }
wchar_t *wide = sc_str_to_wchars(cmd); wchar_t *wide = sc_str_to_wchars(cmd);
free(cmd); free(cmd);
if (!wide) { if (!wide) {
LOGC("Could not allocate wide char string"); LOGC("Could not allocate wide char string");
goto error_free_attribute_list; goto error_close_stderr;
} }
BOOL bInheritHandles = handle_count > 0; if (!CreateProcessW(NULL, wide, NULL, NULL, TRUE, 0, NULL, NULL, &si,
DWORD dwCreationFlags = handle_count > 0 ? EXTENDED_STARTUPINFO_PRESENT : 0; &pi)) {
BOOL ok = CreateProcessW(NULL, wide, NULL, NULL, bInheritHandles, free(wide);
dwCreationFlags, NULL, NULL, &si.StartupInfo, &pi); *handle = NULL;
free(wide);
if (!ok) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) { if (GetLastError() == ERROR_FILE_NOT_FOUND) {
ret = SC_PROCESS_ERROR_MISSING_BINARY; ret = SC_PROCESS_ERROR_MISSING_BINARY;
} }
goto error_free_attribute_list; goto error_close_stderr;
}
if (lpAttributeList) {
DeleteProcThreadAttributeList(lpAttributeList);
free(lpAttributeList);
} }
// These handles are used by the child process, close them for this process // These handles are used by the child process, close them for this process
@ -182,15 +117,11 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit,
CloseHandle(stderr_write_handle); CloseHandle(stderr_write_handle);
} }
free(wide);
*handle = pi.hProcess; *handle = pi.hProcess;
return SC_PROCESS_SUCCESS; return SC_PROCESS_SUCCESS;
error_free_attribute_list:
if (lpAttributeList) {
DeleteProcThreadAttributeList(lpAttributeList);
free(lpAttributeList);
}
error_close_stderr: error_close_stderr:
if (perr) { if (perr) {
CloseHandle(*perr); CloseHandle(*perr);
@ -237,7 +168,7 @@ sc_process_close(HANDLE handle) {
} }
ssize_t ssize_t
sc_pipe_read(HANDLE pipe, char *data, size_t len) { sc_read_pipe(HANDLE pipe, char *data, size_t len) {
DWORD r; DWORD r;
if (!ReadFile(pipe, data, len, &r, NULL)) { if (!ReadFile(pipe, data, len, &r, NULL)) {
return -1; return -1;
@ -246,7 +177,7 @@ sc_pipe_read(HANDLE pipe, char *data, size_t len) {
} }
void void
sc_pipe_close(HANDLE pipe) { sc_close_pipe(HANDLE pipe) {
if (!CloseHandle(pipe)) { if (!CloseHandle(pipe)) {
LOGW("Cannot close pipe"); LOGW("Cannot close pipe");
} }

View File

@ -5,8 +5,8 @@
#include "log.h" #include "log.h"
enum sc_process_result enum sc_process_result
sc_process_execute(const char *const argv[], sc_pid *pid, unsigned inherit) { sc_process_execute(const char *const argv[], sc_pid *pid) {
return sc_process_execute_p(argv, pid, inherit, NULL, NULL, NULL); return sc_process_execute_p(argv, pid, NULL, NULL, NULL);
} }
bool bool

View File

@ -67,31 +67,20 @@ enum sc_process_result {
SC_PROCESS_ERROR_MISSING_BINARY, SC_PROCESS_ERROR_MISSING_BINARY,
}; };
#define SC_STDOUT (1 << 0)
#define SC_STDERR (1 << 1)
/** /**
* Execute the command and write the process id to `pid` * Execute the command and write the process id to `pid`
*
* The parameter `inherit` is a OR of any of SC_STDOUT and SC_STDERR. It
* indicates if stdout and stderr must be inherited from the scrcpy process (in
* other words, if the process must output to the scrcpy console).
*/ */
enum sc_process_result enum sc_process_result
sc_process_execute(const char *const argv[], sc_pid *pid, unsigned inherit); sc_process_execute(const char *const argv[], sc_pid *pid);
/** /**
* Execute the command and write the process id to `pid` * Execute the command and write the process id to `pid`
* *
* If not NULL, provide a pipe for stdin (`pin`), stdout (`pout`) and stderr * If not NULL, provide a pipe for stdin (`pin`), stdout (`pout`) and stderr
* (`perr`). * (`perr`).
*
* The parameter `inherit` has the same semantics as in `sc_process_execute()`.
* If `pout` is not NULL, then `inherit` MUST NOT contain SC_STDOUT.
* If `perr` is not NULL, then `inherit` MUST NOT contain SC_STDERR.
*/ */
enum sc_process_result enum sc_process_result
sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit, sc_process_execute_p(const char *const argv[], sc_pid *pid,
sc_pipe *pin, sc_pipe *pout, sc_pipe *perr); sc_pipe *pin, sc_pipe *pout, sc_pipe *perr);
/** /**

View File

@ -2,7 +2,7 @@
bool bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid, sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
const char *name, bool close) { const char *name) {
if (!sc_intr_set_process(intr, pid)) { if (!sc_intr_set_process(intr, pid)) {
// Already interrupted // Already interrupted
return false; return false;
@ -11,40 +11,6 @@ sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
// Always pass close=false, interrupting would be racy otherwise // Always pass close=false, interrupting would be racy otherwise
bool ret = sc_process_check_success(pid, name, false); bool ret = sc_process_check_success(pid, name, false);
sc_intr_set_process(intr, SC_PROCESS_NONE);
if (close) {
// Close separately
sc_process_close(pid);
}
return ret;
}
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)) {
// Already interrupted
return false;
}
ssize_t ret = sc_pipe_read(pipe, data, len);
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)) {
// Already interrupted
return false;
}
ssize_t ret = sc_pipe_read_all(pipe, data, len);
sc_intr_set_process(intr, SC_PROCESS_NONE); sc_intr_set_process(intr, SC_PROCESS_NONE);
return ret; return ret;
} }

View File

@ -8,14 +8,6 @@
bool bool
sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid, sc_process_check_success_intr(struct sc_intr *intr, sc_pid pid,
const char *name, bool close); const char *name);
ssize_t
sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,
size_t len);
ssize_t
sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe,
char *data, size_t len);
#endif #endif

View File

@ -291,11 +291,3 @@ error:
free(buf.s); free(buf.s);
return NULL; return NULL;
} }
size_t
sc_str_truncate(char *data, size_t len, const char *endchars) {
data[len - 1] = '\0';
size_t idx = strcspn(data, endchars);
data[idx] = '\0';
return idx;
}

View File

@ -103,15 +103,4 @@ sc_str_from_wchars(const wchar_t *s);
char * char *
sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent); sc_str_wrap_lines(const char *input, unsigned columns, unsigned indent);
/**
* Truncate the data after any of the characters from `endchars`
*
* An '\0' is always written at the end of the data, even if no newline
* character is encountered.
*
* Return the size of the resulting line.
*/
size_t
sc_str_truncate(char *data, size_t len, const char *endchars);
#endif #endif

View File

@ -337,32 +337,6 @@ static void test_wrap_lines(void) {
free(formatted); free(formatted);
} }
static void test_truncate(void) {
char s[] = "hello\nworld\n!";
size_t len = sc_str_truncate(s, sizeof(s), "\n");
assert(len == 5);
assert(!strcmp("hello", s));
char s2[] = "hello\r\nworkd\r\n!";
len = sc_str_truncate(s2, sizeof(s2), "\n\r");
assert(len == 5);
assert(!strcmp("hello", s));
char s3[] = "hello world\n!";
len = sc_str_truncate(s3, sizeof(s3), " \n\r");
assert(len == 5);
assert(!strcmp("hello", s3));
char s4[] = "hello ";
len = sc_str_truncate(s4, sizeof(s4), " \n\r");
assert(len == 5);
assert(!strcmp("hello", s4));
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
(void) argc; (void) argc;
(void) argv; (void) argv;
@ -382,6 +356,5 @@ int main(int argc, char *argv[]) {
test_parse_integer_with_suffix(); test_parse_integer_with_suffix();
test_strlist_contains(); test_strlist_contains();
test_wrap_lines(); test_wrap_lines();
test_truncate();
return 0; return 0;
} }

View File

@ -1,5 +1,6 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.os.Parcel; import android.os.Parcel;
@ -165,21 +166,14 @@ public final class CleanUp {
if (config.disableShowTouches || config.restoreStayOn != -1) { if (config.disableShowTouches || config.restoreStayOn != -1) {
ServiceManager serviceManager = new ServiceManager(); ServiceManager serviceManager = new ServiceManager();
Settings settings = new Settings(serviceManager); try (ContentProvider settings = serviceManager.getActivityManager().createSettingsProvider()) {
if (config.disableShowTouches) { if (config.disableShowTouches) {
Ln.i("Disabling \"show touches\""); Ln.i("Disabling \"show touches\"");
try { settings.putValue(ContentProvider.TABLE_SYSTEM, "show_touches", "0");
settings.putValue(Settings.TABLE_SYSTEM, "show_touches", "0");
} catch (SettingsException e) {
Ln.e("Could not restore \"show_touches\"", e);
} }
} if (config.restoreStayOn != -1) {
if (config.restoreStayOn != -1) { Ln.i("Restoring \"stay awake\"");
Ln.i("Restoring \"stay awake\""); settings.putValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
try {
settings.putValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(config.restoreStayOn));
} catch (SettingsException e) {
Ln.e("Could not restore \"stay_on_while_plugged_in\"", e);
} }
} }
} }

View File

@ -1,33 +0,0 @@
package com.genymobile.scrcpy;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public final class Command {
private Command() {
// not instantiable
}
public static void exec(String... cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(cmd);
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
}
public static String execReadLine(String... cmd) throws IOException, InterruptedException {
String result = null;
Process process = Runtime.getRuntime().exec(cmd);
Scanner scanner = new Scanner(process.getInputStream());
if (scanner.hasNextLine()) {
result = scanner.nextLine();
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
return result;
}
}

View File

@ -1,6 +1,7 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl; import com.genymobile.scrcpy.wrappers.SurfaceControl;
@ -28,7 +29,6 @@ public final class Device {
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2; public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
private static final ServiceManager SERVICE_MANAGER = new ServiceManager(); private static final ServiceManager SERVICE_MANAGER = new ServiceManager();
private static final Settings SETTINGS = new Settings(SERVICE_MANAGER);
public interface RotationListener { public interface RotationListener {
void onRotationChanged(int rotation); void onRotationChanged(int rotation);
@ -296,7 +296,7 @@ public final class Device {
} }
} }
public static Settings getSettings() { public static ContentProvider createSettingsProvider() {
return SETTINGS; return SERVICE_MANAGER.getActivityManager().createSettingsProvider();
} }
} }

View File

@ -57,18 +57,11 @@ public final class Ln {
} }
} }
public static void w(String message, Throwable throwable) {
if (isEnabled(Level.WARN)) {
Log.w(TAG, message, throwable);
System.out.println(PREFIX + "WARN: " + message);
if (throwable != null) {
throwable.printStackTrace();
}
}
}
public static void w(String message) { public static void w(String message) {
w(message, null); if (isEnabled(Level.WARN)) {
Log.w(TAG, message);
System.out.println(PREFIX + "WARN: " + message);
}
} }
public static void e(String message, Throwable throwable) { public static void e(String message, Throwable throwable) {

View File

@ -1,5 +1,7 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import android.graphics.Rect; import android.graphics.Rect;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
@ -17,25 +19,24 @@ public final class Server {
// not instantiable // not instantiable
} }
private static void initAndCleanUp(Options options) { private static void scrcpy(Options options) throws IOException {
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
final Device device = new Device(options);
List<CodecOption> codecOptions = CodecOption.parse(options.getCodecOptions());
boolean mustDisableShowTouchesOnCleanUp = false; boolean mustDisableShowTouchesOnCleanUp = false;
int restoreStayOn = -1; int restoreStayOn = -1;
if (options.getShowTouches() || options.getStayAwake()) { if (options.getShowTouches() || options.getStayAwake()) {
Settings settings = Device.getSettings(); try (ContentProvider settings = Device.createSettingsProvider()) {
if (options.getShowTouches()) { if (options.getShowTouches()) {
try { String oldValue = settings.getAndPutValue(ContentProvider.TABLE_SYSTEM, "show_touches", "1");
String oldValue = settings.getAndPutValue(Settings.TABLE_SYSTEM, "show_touches", "1");
// If "show touches" was disabled, it must be disabled back on clean up // If "show touches" was disabled, it must be disabled back on clean up
mustDisableShowTouchesOnCleanUp = !"1".equals(oldValue); mustDisableShowTouchesOnCleanUp = !"1".equals(oldValue);
} catch (SettingsException e) {
Ln.e("Could not change \"show_touches\"", e);
} }
}
if (options.getStayAwake()) { if (options.getStayAwake()) {
int stayOn = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS; int stayOn = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS;
try { String oldValue = settings.getAndPutValue(ContentProvider.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(stayOn));
String oldValue = settings.getAndPutValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(stayOn));
try { try {
restoreStayOn = Integer.parseInt(oldValue); restoreStayOn = Integer.parseInt(oldValue);
if (restoreStayOn == stayOn) { if (restoreStayOn == stayOn) {
@ -45,25 +46,11 @@ public final class Server {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
restoreStayOn = 0; restoreStayOn = 0;
} }
} catch (SettingsException e) {
Ln.e("Could not change \"stay_on_while_plugged_in\"", e);
} }
} }
} }
try { CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose());
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, true, options.getPowerOffScreenOnClose());
} catch (IOException e) {
Ln.e("Could not configure cleanup", e);
}
}
private static void scrcpy(Options options) throws IOException {
Ln.i("Device: " + Build.MANUFACTURER + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
final Device device = new Device(options);
List<CodecOption> codecOptions = CodecOption.parse(options.getCodecOptions());
Thread initThread = startInitThread(options);
boolean tunnelForward = options.isTunnelForward(); boolean tunnelForward = options.isTunnelForward();
@ -95,7 +82,6 @@ public final class Server {
// this is expected on close // this is expected on close
Ln.d("Screen streaming stopped"); Ln.d("Screen streaming stopped");
} finally { } finally {
initThread.interrupt();
if (controllerThread != null) { if (controllerThread != null) {
controllerThread.interrupt(); controllerThread.interrupt();
} }
@ -106,17 +92,6 @@ public final class Server {
} }
} }
private static Thread startInitThread(final Options options) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
initAndCleanUp(options);
}
});
thread.start();
return thread;
}
private static Thread startController(final Controller controller) { private static Thread startController(final Controller controller) {
Thread thread = new Thread(new Runnable() { Thread thread = new Thread(new Runnable() {
@Override @Override

View File

@ -1,84 +0,0 @@
package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.os.Build;
import java.io.IOException;
public class Settings {
public static final String TABLE_SYSTEM = ContentProvider.TABLE_SYSTEM;
public static final String TABLE_SECURE = ContentProvider.TABLE_SECURE;
public static final String TABLE_GLOBAL = ContentProvider.TABLE_GLOBAL;
private final ServiceManager serviceManager;
public Settings(ServiceManager serviceManager) {
this.serviceManager = serviceManager;
}
private static void execSettingsPut(String table, String key, String value) throws SettingsException {
try {
Command.exec("settings", "put", table, key, value);
} catch (IOException | InterruptedException e) {
throw new SettingsException("put", table, key, value, e);
}
}
private static String execSettingsGet(String table, String key) throws SettingsException {
try {
return Command.execReadLine("settings", "get", table, key);
} catch (IOException | InterruptedException e) {
throw new SettingsException("get", table, key, null, e);
}
}
public String getValue(String table, String key) throws SettingsException {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = serviceManager.getActivityManager().createSettingsProvider()) {
return provider.getValue(table, key);
} catch (SettingsException e) {
Ln.w("Could not get settings value via ContentProvider, fallback to settings process", e);
}
}
return execSettingsGet(table, key);
}
public void putValue(String table, String key, String value) throws SettingsException {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = serviceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
} catch (SettingsException e) {
Ln.w("Could not put settings value via ContentProvider, fallback to settings process", e);
}
}
execSettingsPut(table, key, value);
}
public String getAndPutValue(String table, String key, String value) throws SettingsException {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
// on Android >= 12, it always fails: <https://github.com/Genymobile/scrcpy/issues/2788>
try (ContentProvider provider = serviceManager.getActivityManager().createSettingsProvider()) {
String oldValue = provider.getValue(table, key);
if (!value.equals(oldValue)) {
provider.putValue(table, key, value);
}
return oldValue;
} catch (SettingsException e) {
Ln.w("Could not get and put settings value via ContentProvider, fallback to settings process", e);
}
}
String oldValue = getValue(table, key);
if (!value.equals(oldValue)) {
putValue(table, key, value);
}
return oldValue;
}
}

View File

@ -1,11 +0,0 @@
package com.genymobile.scrcpy;
public class SettingsException extends Exception {
private static String createMessage(String method, String table, String key, String value) {
return "Could not access settings: " + method + " " + table + " " + key + (value != null ? " " + value : "");
}
public SettingsException(String method, String table, String key, String value, Throwable cause) {
super(createMessage(method, table, key, value), cause);
}
}

View File

@ -1,7 +1,6 @@
package com.genymobile.scrcpy.wrappers; package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.Ln; import com.genymobile.scrcpy.Ln;
import com.genymobile.scrcpy.SettingsException;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
@ -88,8 +87,7 @@ public class ContentProvider implements Closeable {
return attributionSource; return attributionSource;
} }
private Bundle call(String callMethod, String arg, Bundle extras) private Bundle call(String callMethod, String arg, Bundle extras) {
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
try { try {
Method method = getCallMethod(); Method method = getCallMethod();
Object[] args; Object[] args;
@ -110,7 +108,7 @@ public class ContentProvider implements Closeable {
return (Bundle) method.invoke(provider, args); return (Bundle) method.invoke(provider, args);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException e) {
Ln.e("Could not invoke method", e); Ln.e("Could not invoke method", e);
throw e; return null;
} }
} }
@ -144,31 +142,30 @@ public class ContentProvider implements Closeable {
} }
} }
public String getValue(String table, String key) throws SettingsException { public String getValue(String table, String key) {
String method = getGetMethod(table); String method = getGetMethod(table);
Bundle arg = new Bundle(); Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID); arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
try { Bundle bundle = call(method, key, arg);
Bundle bundle = call(method, key, arg); if (bundle == null) {
if (bundle == null) { return null;
return null;
}
return bundle.getString("value");
} catch (Exception e) {
throw new SettingsException(table, "get", key, null, e);
} }
return bundle.getString("value");
} }
public void putValue(String table, String key, String value) throws SettingsException { public void putValue(String table, String key, String value) {
String method = getPutMethod(table); String method = getPutMethod(table);
Bundle arg = new Bundle(); Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID); arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
arg.putString(NAME_VALUE_TABLE_VALUE, value); arg.putString(NAME_VALUE_TABLE_VALUE, value);
try { call(method, key, arg);
call(method, key, arg); }
} catch (Exception e) {
throw new SettingsException(table, "put", key, value, e); public String getAndPutValue(String table, String key, String value) {
String oldValue = getValue(table, key);
if (!value.equals(oldValue)) {
putValue(table, key, value);
} }
return oldValue;
} }
} }