Compare commits

..

4 Commits

Author SHA1 Message Date
Romain Vimont
e48bbb3c2c Log server pushed
Now that "adb push" stdout is disabled, add a log to notify server
pushed.
2021-11-19 22:27:01 +01:00
Romain Vimont
36c0d6864c explicit_inherit 2021-11-19 22:27:01 +01:00
Romain Vimont
88e9c2b4af process_inherit 2021-11-19 22:27:01 +01:00
Romain Vimont
c96dc6d2c4 Simplify Windows process inheritance configuration
Merge if-blocks together.
2021-11-19 22:27:01 +01:00
47 changed files with 259 additions and 698 deletions

View File

@@ -412,47 +412,12 @@ autoadb scrcpy -s '{}'
[AutoAdb]: https://github.com/rom1v/autoadb
#### Tunnels
#### SSH tunnel
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_
protocol).
##### Remote ADB server
To connect to a remote ADB server, make the server listen on all interfaces:
```bash
adb kill-server
adb -a nodaemon server start
# keep this open
```
**Warning: all communications between clients and ADB server are unencrypted.**
Suppose that this server is accessible at 192.168.1.2. Then, from another
terminal, run scrcpy:
```bash
export ADB_SERVER_SOCKET=tcp:192.168.1.2:5037
scrcpy --tunnel-host=192.168.1.2
```
By default, scrcpy uses the local port used for `adb forward` tunnel
establishment (typically `27183`, see `--port`). It is also possible to force a
different tunnel port (it may be useful in more complex situations, when more
redirections are involved):
```
scrcpy --tunnel-port=1234
```
##### SSH tunnel
To communicate with a remote ADB server securely, it is preferable to use a SSH
tunnel.
First, make sure the ADB server is running on the remote computer:
```bash

View File

@@ -25,7 +25,6 @@ src = [
'src/server.c',
'src/stream.c',
'src/video_buffer.c',
'src/util/acksync.c',
'src/util/file.c',
'src/util/intr.c',
'src/util/log.c',
@@ -40,26 +39,16 @@ src = [
'src/util/tick.c',
]
conf = configuration_data()
if host_machine.system() == 'windows'
src += [
'src/sys/win/file.c',
'src/sys/win/process.c',
]
conf.set('_WIN32_WINNT', '0x0600')
conf.set('WINVER', '0x0600')
else
src += [
'src/sys/unix/file.c',
'src/sys/unix/process.c',
]
conf.set('_POSIX_C_SOURCE', '200809L')
conf.set('_XOPEN_SOURCE', '700')
conf.set('_GNU_SOURCE', true)
if host_machine.system() == 'darwin'
conf.set('_DARWIN_C_SOURCE', true)
endif
endif
v4l2_support = host_machine.system() == 'linux'
@@ -139,6 +128,8 @@ if host_machine.system() == 'windows'
dependencies += cc.find_library('ws2_32')
endif
conf = configuration_data()
foreach f : check_functions
if cc.has_function(f)
define = 'HAVE_' + f.underscorify().to_upper()
@@ -208,7 +199,6 @@ if get_option('buildtype') == 'debug'
'tests/test_cli.c',
'src/cli.c',
'src/options.c',
'src/util/net.c',
'src/util/str.c',
'src/util/strbuf.c',
'src/util/term.c',

View File

@@ -203,18 +203,6 @@ Enable "show touches" on start, restore the initial value on exit.
It only shows physical touches (not clicks from scrcpy).
.TP
.BI "\-\-tunnel\-host " ip
Set the IP address of the adb tunnel to reach the scrcpy server. This option automatically enables --force-adb-forward.
Default is localhost.
.TP
.BI "\-\-tunnel\-port " port
Set the TCP port of the adb tunnel to reach the scrcpy server. This option automatically enables --force-adb-forward.
Default is 0 (not forced): the local port used for establishing the tunnel will be used.
.TP
.BI "\-\-v4l2-sink " /dev/videoN
Output to v4l2loopback device.

View File

@@ -112,7 +112,7 @@ show_adb_err_msg(enum sc_process_result err, const char *const argv[]) {
static sc_pid
adb_execute_p(const char *serial, const char *const adb_cmd[],
size_t len, sc_pipe *pout) {
size_t len, unsigned inherit, sc_pipe *pout) {
int i;
sc_pid pid;
@@ -133,7 +133,7 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
memcpy(&argv[i], adb_cmd, len * sizeof(const char *));
argv[len + i] = NULL;
enum sc_process_result r =
sc_process_execute_p(argv, &pid, NULL, pout, NULL);
sc_process_execute_p(argv, &pid, inherit, NULL, pout, NULL);
if (r != SC_PROCESS_SUCCESS) {
show_adb_err_msg(r, argv);
pid = SC_PROCESS_NONE;
@@ -144,50 +144,54 @@ adb_execute_p(const char *serial, const char *const adb_cmd[],
}
sc_pid
adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {
return adb_execute_p(serial, adb_cmd, len, NULL);
adb_execute(const char *serial, const char *const adb_cmd[], size_t len,
unsigned inherit) {
return adb_execute_p(serial, adb_cmd, len, inherit, NULL);
}
static sc_pid
adb_exec_forward(const char *serial, uint16_t local_port,
const char *device_socket_name) {
const char *device_socket_name, unsigned inherit) {
char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME
sprintf(local, "tcp:%" PRIu16, local_port);
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"forward", local, remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit);
}
static sc_pid
adb_exec_forward_remove(const char *serial, uint16_t local_port) {
adb_exec_forward_remove(const char *serial, uint16_t local_port,
unsigned inherit) {
char local[4 + 5 + 1]; // tcp:PORT
sprintf(local, "tcp:%" PRIu16, local_port);
const char *const adb_cmd[] = {"forward", "--remove", local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit);
}
static sc_pid
adb_exec_reverse(const char *serial, const char *device_socket_name,
uint16_t local_port) {
uint16_t local_port, unsigned inherit) {
char local[4 + 5 + 1]; // tcp:PORT
char remote[108 + 14 + 1]; // localabstract:NAME
sprintf(local, "tcp:%" PRIu16, local_port);
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", remote, local};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit);
}
static sc_pid
adb_exec_reverse_remove(const char *serial, const char *device_socket_name) {
adb_exec_reverse_remove(const char *serial, const char *device_socket_name,
unsigned inherit) {
char remote[108 + 14 + 1]; // localabstract:NAME
snprintf(remote, sizeof(remote), "localabstract:%s", device_socket_name);
const char *const adb_cmd[] = {"reverse", "--remove", remote};
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit);
}
static sc_pid
adb_exec_push(const char *serial, const char *local, const char *remote) {
adb_exec_push(const char *serial, const char *local, const char *remote,
unsigned inherit) {
#ifdef __WINDOWS__
// Windows will parse the string, so the paths must be quoted
// (see sys/win/command.c)
@@ -203,7 +207,7 @@ adb_exec_push(const char *serial, const char *local, const char *remote) {
#endif
const char *const adb_cmd[] = {"push", local, remote};
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit);
#ifdef __WINDOWS__
free((void *) remote);
@@ -214,7 +218,7 @@ adb_exec_push(const char *serial, const char *local, const char *remote) {
}
static sc_pid
adb_exec_install(const char *serial, const char *local) {
adb_exec_install(const char *serial, const char *local, unsigned inherit) {
#ifdef __WINDOWS__
// Windows will parse the string, so the local name must be quoted
// (see sys/win/command.c)
@@ -225,7 +229,7 @@ adb_exec_install(const char *serial, const char *local) {
#endif
const char *const adb_cmd[] = {"install", "-r", local};
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
sc_pid pid = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd), inherit);
#ifdef __WINDOWS__
free((void *) local);
@@ -235,58 +239,62 @@ adb_exec_install(const char *serial, const char *local) {
}
static sc_pid
adb_exec_get_serialno(sc_pipe *pout) {
adb_exec_get_serialno(unsigned inherit, sc_pipe *pout) {
const char *const adb_cmd[] = {"get-serialno"};
return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), pout);
return adb_execute_p(NULL, adb_cmd, ARRAY_LEN(adb_cmd), inherit, pout);
}
bool
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
const char *device_socket_name) {
sc_pid pid = adb_exec_forward(serial, local_port, device_socket_name);
const char *device_socket_name, unsigned inherit) {
sc_pid pid =
adb_exec_forward(serial, local_port, device_socket_name, inherit);
return sc_process_check_success_intr(intr, pid, "adb forward", true);
}
bool
adb_forward_remove(struct sc_intr *intr, const char *serial,
uint16_t local_port) {
sc_pid pid = adb_exec_forward_remove(serial, local_port);
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) {
sc_pid pid = adb_exec_reverse(serial, device_socket_name, local_port);
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) {
sc_pid pid = adb_exec_reverse_remove(serial, device_socket_name);
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) {
sc_pid pid = adb_exec_push(serial, local, remote);
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) {
sc_pid pid = adb_exec_install(serial, local);
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 *
adb_get_serialno(struct sc_intr *intr) {
adb_get_serialno(struct sc_intr *intr, unsigned inherit) {
sc_pipe pout;
sc_pid pid = adb_exec_get_serialno(&pout);
sc_pid pid = adb_exec_get_serialno(inherit, &pout);
if (pid == SC_PROCESS_NONE) {
LOGE("Could not execute \"adb get-serialno\"");
return NULL;

View File

@@ -9,30 +9,33 @@
#include "util/intr.h"
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
adb_forward(struct sc_intr *intr, const char *serial, uint16_t local_port,
const char *device_socket_name);
const char *device_socket_name, unsigned inherit);
bool
adb_forward_remove(struct sc_intr *intr, const char *serial,
uint16_t local_port);
uint16_t local_port, unsigned inherit);
bool
adb_reverse(struct sc_intr *intr, const char *serial,
const char *device_socket_name, uint16_t local_port);
const char *device_socket_name, uint16_t local_port,
unsigned inherit);
bool
adb_reverse_remove(struct sc_intr *intr, const char *serial,
const char *device_socket_name);
const char *device_socket_name, unsigned inherit);
bool
adb_push(struct sc_intr *intr, const char *serial, const char *local,
const char *remote);
const char *remote, unsigned inherit);
bool
adb_install(struct sc_intr *intr, const char *serial, const char *local);
adb_install(struct sc_intr *intr, const char *serial, const char *local,
unsigned inherit);
/**
* Execute `adb get-serialno`
@@ -40,6 +43,6 @@ adb_install(struct sc_intr *intr, const char *serial, const char *local);
* Return the result, to be freed by the caller, or NULL on error.
*/
char *
adb_get_serialno(struct sc_intr *intr);
adb_get_serialno(struct sc_intr *intr, unsigned inherit);
#endif

View File

@@ -20,7 +20,8 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
struct sc_port_range port_range) {
uint16_t port = port_range.first;
for (;;) {
if (!adb_reverse(intr, serial, SC_SOCKET_NAME, port)) {
if (!adb_reverse(intr, serial, SC_SOCKET_NAME, port,
SC_INHERIT_STDERR)) {
// the command itself failed, it will fail on any port
return false;
}
@@ -51,7 +52,8 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel,
}
// failure, disable tunnel and try another port
if (!adb_reverse_remove(intr, serial, SC_SOCKET_NAME)) {
if (!adb_reverse_remove(intr, serial, SC_SOCKET_NAME,
SC_INHERIT_STDERR)) {
LOGW("Could not remove reverse tunnel on port %" PRIu16, port);
}
@@ -81,7 +83,8 @@ enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel,
uint16_t port = port_range.first;
for (;;) {
if (adb_forward(intr, serial, port, SC_SOCKET_NAME)) {
if (adb_forward(intr, serial, port, SC_SOCKET_NAME,
SC_INHERIT_STDERR)) {
// success
tunnel->local_port = port;
tunnel->enabled = true;
@@ -146,9 +149,11 @@ sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr,
bool ret;
if (tunnel->forward) {
ret = adb_forward_remove(intr, serial, tunnel->local_port);
ret = adb_forward_remove(intr, serial, tunnel->local_port,
SC_INHERIT_STDERR);
} else {
ret = adb_reverse_remove(intr, serial, SC_SOCKET_NAME);
ret = adb_reverse_remove(intr, serial, SC_SOCKET_NAME,
SC_INHERIT_STDERR);
assert(tunnel->server_socket != SC_SOCKET_NONE);
if (!net_close(tunnel->server_socket)) {

View File

@@ -35,7 +35,7 @@ sc_hid_event_init(struct sc_hid_event *hid_event, uint16_t accessory_id,
hid_event->accessory_id = accessory_id;
hid_event->buffer = buffer;
hid_event->size = buffer_size;
hid_event->ack_to_wait = SC_SEQUENCE_INVALID;
hid_event->delay = 0;
}
void
@@ -118,10 +118,7 @@ sc_aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle) {
}
bool
sc_aoa_init(struct sc_aoa *aoa, const char *serial,
struct sc_acksync *acksync) {
assert(acksync);
sc_aoa_init(struct sc_aoa *aoa, const char *serial) {
cbuf_init(&aoa->queue);
if (!sc_mutex_init(&aoa->mutex)) {
@@ -158,7 +155,6 @@ sc_aoa_init(struct sc_aoa *aoa, const char *serial,
}
aoa->stopped = false;
aoa->acksync = acksync;
return true;
}
@@ -336,28 +332,23 @@ run_aoa_thread(void *data) {
assert(non_empty);
(void) non_empty;
uint64_t ack_to_wait = event.ack_to_wait;
sc_mutex_unlock(&aoa->mutex);
if (ack_to_wait != SC_SEQUENCE_INVALID) {
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
// Do not block the loop indefinitely if the ack never comes (it should
// never happen)
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
enum sc_acksync_wait_result result =
sc_acksync_wait(aoa->acksync, ack_to_wait, deadline);
if (result == SC_ACKSYNC_WAIT_TIMEOUT) {
LOGW("Ack not received after 500ms, discarding HID event");
sc_hid_event_destroy(&event);
continue;
} else if (result == SC_ACKSYNC_WAIT_INTR) {
// stopped
sc_hid_event_destroy(&event);
assert(event.delay >= 0);
if (event.delay) {
// Wait during the specified delay before injecting the HID event
sc_tick deadline = sc_tick_now() + event.delay;
bool timed_out = false;
while (!aoa->stopped && !timed_out) {
timed_out = !sc_cond_timedwait(&aoa->event_cond, &aoa->mutex,
deadline);
}
if (aoa->stopped) {
sc_mutex_unlock(&aoa->mutex);
break;
}
}
sc_mutex_unlock(&aoa->mutex);
bool ok = sc_aoa_send_hid_event(aoa, &event);
sc_hid_event_destroy(&event);
if (!ok) {
@@ -386,8 +377,6 @@ sc_aoa_stop(struct sc_aoa *aoa) {
aoa->stopped = true;
sc_cond_signal(&aoa->event_cond);
sc_mutex_unlock(&aoa->mutex);
sc_acksync_interrupt(aoa->acksync);
}
void

View File

@@ -6,7 +6,6 @@
#include <libusb-1.0/libusb.h>
#include "util/acksync.h"
#include "util/cbuf.h"
#include "util/thread.h"
#include "util/tick.h"
@@ -15,7 +14,7 @@ struct sc_hid_event {
uint16_t accessory_id;
unsigned char *buffer;
uint16_t size;
uint64_t ack_to_wait;
sc_tick delay;
};
// Takes ownership of buffer
@@ -37,12 +36,10 @@ struct sc_aoa {
sc_cond event_cond;
bool stopped;
struct sc_hid_event_queue queue;
struct sc_acksync *acksync;
};
bool
sc_aoa_init(struct sc_aoa *aoa, const char *serial, struct sc_acksync *acksync);
sc_aoa_init(struct sc_aoa *aoa, const char *serial);
void
sc_aoa_destroy(struct sc_aoa *aoa);

View File

@@ -9,7 +9,6 @@
#include "options.h"
#include "util/log.h"
#include "util/net.h"
#include "util/str.h"
#include "util/strbuf.h"
#include "util/term.h"
@@ -47,8 +46,6 @@
#define OPT_V4L2_SINK 1027
#define OPT_DISPLAY_BUFFER 1028
#define OPT_V4L2_BUFFER 1029
#define OPT_TUNNEL_HOST 1030
#define OPT_TUNNEL_PORT 1031
struct sc_option {
char shortopt;
@@ -333,25 +330,6 @@ static const struct sc_option options[] = {
"on exit.\n"
"It only shows physical touches (not clicks from scrcpy).",
},
{
.longopt_id = OPT_TUNNEL_HOST,
.longopt = "tunnel-host",
.argdesc = "ip",
.text = "Set the IP address of the adb tunnel to reach the scrcpy "
"server. This option automatically enables "
"--force-adb-forward.\n"
"Default is localhost.",
},
{
.longopt_id = OPT_TUNNEL_PORT,
.longopt = "tunnel-port",
.argdesc = "port",
.text = "Set the TCP port of the adb tunnel to reach the scrcpy "
"server. This option automatically enables "
"--force-adb-forward.\n"
"Default is 0 (not forced): the local port used for "
"establishing the tunnel will be used.",
},
#ifdef HAVE_V4L2
{
.longopt_id = OPT_V4L2_SINK,
@@ -1149,21 +1127,6 @@ parse_record_format(const char *optarg, enum sc_record_format *format) {
return false;
}
static bool
parse_ip(const char *optarg, uint32_t *ipv4) {
return net_parse_ipv4(optarg, ipv4);
}
static bool
parse_port(const char *optarg, uint16_t *port) {
long value;
if (!parse_integer_arg(optarg, &value, false, 0, 0xFFFF, "port")) {
return false;
}
*port = (uint16_t) value;
return true;
}
static enum sc_record_format
guess_record_format(const char *filename) {
size_t len = strlen(filename);
@@ -1236,16 +1199,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
break;
case OPT_TUNNEL_HOST:
if (!parse_ip(optarg, &opts->tunnel_host)) {
return false;
}
break;
case OPT_TUNNEL_PORT:
if (!parse_port(optarg, &opts->tunnel_port)) {
return false;
}
break;
case 'n':
opts->control = false;
break;
@@ -1405,12 +1358,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
#endif
if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) {
LOGI("Tunnel host/port is set, "
"--force-adb-forward automatically enabled.");
opts->force_adb_forward = true;
}
int index = optind;
if (index < argc) {
LOGE("Unexpected additional argument: %s", argv[index]);

View File

@@ -1,17 +1,16 @@
#ifndef COMPAT_H
#define COMPAT_H
#include "config.h"
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
#define _GNU_SOURCE
#ifdef __APPLE__
# define _DARWIN_C_SOURCE
#endif
#include <libavformat/version.h>
#include <SDL2/SDL_version.h>
#ifndef __WIN32
# define PRIu64_ PRIu64
#else
# define PRIu64_ "I64u" // Windows...
#endif
// In ffmpeg/doc/APIchanges:
// 2018-02-06 - 0694d87024 - lavf 58.9.100 - avformat.h
// Deprecate use of av_register_input_format(), av_register_output_format(),

View File

@@ -118,12 +118,11 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
buf[1] = msg->inject_keycode.action;
return 2;
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
buffer_write64be(&buf[1], msg->set_clipboard.sequence);
buf[9] = !!msg->set_clipboard.paste;
buf[1] = !!msg->set_clipboard.paste;
size_t len = write_string(msg->set_clipboard.text,
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
&buf[10]);
return 10 + len;
&buf[2]);
return 2 + len;
}
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_screen_power_mode.mode;
@@ -171,6 +170,11 @@ control_msg_log(const struct control_msg *msg) {
(long) msg->inject_touch_event.buttons);
} else {
// numeric pointer id
#ifndef __WIN32
# define PRIu64_ PRIu64
#else
# define PRIu64_ "I64u" // Windows...
#endif
LOG_CMSG("touch [id=%" PRIu64_ "] %-4s position=%" PRIi32 ",%"
PRIi32 " pressure=%g buttons=%06lx",
id,
@@ -195,8 +199,7 @@ control_msg_log(const struct control_msg *msg) {
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action));
break;
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
LOG_CMSG("clipboard %" PRIu64_ " %s \"%s\"",
msg->set_clipboard.sequence,
LOG_CMSG("clipboard %s \"%s\"",
msg->set_clipboard.paste ? "paste" : "copy",
msg->set_clipboard.text);
break;

View File

@@ -70,7 +70,6 @@ struct control_msg {
// screen may only be turned on on ACTION_DOWN
} back_or_screen_on;
struct {
uint64_t sequence;
char *text; // owned, to be freed by free()
bool paste;
} set_clipboard;

View File

@@ -5,11 +5,10 @@
#include "util/log.h"
bool
controller_init(struct controller *controller, sc_socket control_socket,
struct sc_acksync *acksync) {
controller_init(struct controller *controller, sc_socket control_socket) {
cbuf_init(&controller->queue);
bool ok = receiver_init(&controller->receiver, control_socket, acksync);
bool ok = receiver_init(&controller->receiver, control_socket);
if (!ok) {
return false;
}

View File

@@ -7,7 +7,6 @@
#include "control_msg.h"
#include "receiver.h"
#include "util/acksync.h"
#include "util/cbuf.h"
#include "util/net.h"
#include "util/thread.h"
@@ -25,8 +24,7 @@ struct controller {
};
bool
controller_init(struct controller *controller, sc_socket control_socket,
struct sc_acksync *acksync);
controller_init(struct controller *controller, sc_socket control_socket);
void
controller_destroy(struct controller *controller);

View File

@@ -1,6 +1,5 @@
#include "device_msg.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -35,11 +34,6 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
msg->clipboard.text = text;
return 5 + clipboard_len;
}
case DEVICE_MSG_TYPE_ACK_CLIPBOARD: {
uint64_t sequence = buffer_read64be(&buf[1]);
msg->ack_clipboard.sequence = sequence;
return 9;
}
default:
LOGW("Unknown device message type: %d", (int) msg->type);
return -1; // error, we cannot recover

View File

@@ -13,7 +13,6 @@
enum device_msg_type {
DEVICE_MSG_TYPE_CLIPBOARD,
DEVICE_MSG_TYPE_ACK_CLIPBOARD,
};
struct device_msg {
@@ -22,9 +21,6 @@ struct device_msg {
struct {
char *text; // owned, to be freed by free()
} clipboard;
struct {
uint64_t sequence;
} ack_clipboard;
};
};

View File

@@ -128,7 +128,8 @@ run_file_handler(void *data) {
if (req.action == ACTION_INSTALL_APK) {
LOGI("Installing %s...", req.file);
bool ok = adb_install(intr, serial, req.file);
bool ok = adb_install(intr, serial, req.file,
SC_INHERIT_STDOUT | SC_INHERIT_STDERR);
if (ok) {
LOGI("%s successfully installed", req.file);
} else {
@@ -136,7 +137,8 @@ run_file_handler(void *data) {
}
} else {
LOGI("Pushing %s...", req.file);
bool ok = adb_push(intr, serial, req.file, push_target);
bool ok = adb_push(intr, serial, req.file, push_target,
SC_INHERIT_STDOUT | SC_INHERIT_STDERR);
if (ok) {
LOGI("%s successfully pushed to %s", req.file, push_target);
} else {

View File

@@ -278,8 +278,7 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
static void
sc_key_processor_process_key(struct sc_key_processor *kp,
const SDL_KeyboardEvent *event,
uint64_t ack_to_wait) {
const SDL_KeyboardEvent *event) {
if (event->repeat) {
// In USB HID protocol, key repeat is handled by the host (Android), so
// just ignore key repeat here.
@@ -299,12 +298,16 @@ sc_key_processor_process_key(struct sc_key_processor *kp,
}
}
if (ack_to_wait) {
SDL_Keycode keycode = event->keysym.sym;
bool down = event->type == SDL_KEYDOWN;
bool ctrl = event->keysym.mod & KMOD_CTRL;
bool shift = event->keysym.mod & KMOD_SHIFT;
if (ctrl && !shift && keycode == SDLK_v && down) {
// Ctrl+v is pressed, so clipboard synchronization has been
// requested. Wait until clipboard synchronization is acknowledged
// by the server, otherwise it could paste the old clipboard
// content.
hid_event.ack_to_wait = ack_to_wait;
// requested. Wait a bit so that the clipboard is set before
// injecting Ctrl+v via HID, otherwise it would paste the old
// clipboard content.
hid_event.delay = SC_TICK_FROM_MS(5);
}
if (!sc_aoa_push_hid_event(kb->aoa, &hid_event)) {
@@ -345,10 +348,6 @@ sc_hid_keyboard_init(struct sc_hid_keyboard *kb, struct sc_aoa *aoa) {
.process_text = sc_key_processor_process_text,
};
// Clipboard synchronization is requested over the control socket, while HID
// events are sent over AOA, so it must wait for clipboard synchronization
// to be acknowledged by the device before injecting Ctrl+v.
kb->key_processor.async_paste = true;
kb->key_processor.ops = &ops;
return true;

View File

@@ -82,8 +82,6 @@ input_manager_init(struct input_manager *im, struct controller *controller,
im->last_keycode = SDLK_UNKNOWN;
im->last_mod = 0;
im->key_repeat = 0;
im->next_sequence = 1; // 0 is reserved for SC_SEQUENCE_INVALID
}
static void
@@ -210,35 +208,35 @@ collapse_panels(struct controller *controller) {
}
}
static bool
set_device_clipboard(struct controller *controller, bool paste,
uint64_t sequence) {
static void
set_device_clipboard(struct controller *controller, bool paste) {
char *text = SDL_GetClipboardText();
if (!text) {
LOGW("Could not get clipboard text: %s", SDL_GetError());
return false;
return;
}
if (!*text) {
// empty text
SDL_free(text);
return;
}
char *text_dup = strdup(text);
SDL_free(text);
if (!text_dup) {
LOGW("Could not strdup input text");
return false;
return;
}
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
msg.set_clipboard.sequence = sequence;
msg.set_clipboard.text = text_dup;
msg.set_clipboard.paste = paste;
if (!controller_push_msg(controller, &msg)) {
free(text_dup);
LOGW("Could not request 'set device clipboard'");
return false;
}
return true;
}
static void
@@ -464,10 +462,8 @@ input_manager_process_key(struct input_manager *im,
// inject the text as input events
clipboard_paste(controller);
} else {
// store the text in the device clipboard and paste,
// without requesting an acknowledgment
set_device_clipboard(controller, true,
SC_SEQUENCE_INVALID);
// store the text in the device clipboard and paste
set_device_clipboard(controller, true);
}
}
return;
@@ -516,36 +512,18 @@ input_manager_process_key(struct input_manager *im,
return;
}
uint64_t ack_to_wait = SC_SEQUENCE_INVALID;
bool is_ctrl_v = ctrl && !shift && keycode == SDLK_v && down && !repeat;
if (is_ctrl_v) {
if (ctrl && !shift && keycode == SDLK_v && down && !repeat) {
if (im->legacy_paste) {
// inject the text as input events
clipboard_paste(controller);
return;
}
// Request an acknowledgement only if necessary
uint64_t sequence = im->kp->async_paste ? im->next_sequence
: SC_SEQUENCE_INVALID;
// Synchronize the computer clipboard to the device clipboard before
// sending Ctrl+v, to allow seamless copy-paste.
bool ok = set_device_clipboard(controller, false, sequence);
if (!ok) {
LOGW("Clipboard could not be synchronized, Ctrl+v not injected");
return;
}
if (im->kp->async_paste) {
// The key processor must wait for this ack before injecting Ctrl+v
ack_to_wait = sequence;
// Increment only when the request succeeded
++im->next_sequence;
}
set_device_clipboard(controller, false);
}
im->kp->ops->process_key(im->kp, event, ack_to_wait);
im->kp->ops->process_key(im->kp, event);
}
static void

View File

@@ -38,8 +38,6 @@ struct input_manager {
unsigned key_repeat;
SDL_Keycode last_keycode;
uint16_t last_mod;
uint64_t next_sequence; // used for request acknowledgements
};
void

View File

@@ -188,13 +188,7 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
static void
sc_key_processor_process_key(struct sc_key_processor *kp,
const SDL_KeyboardEvent *event,
uint64_t ack_to_wait) {
// The device clipboard synchronization and the key event messages are
// serialized, there is nothing special to do to ensure that the clipboard
// is set before injecting Ctrl+v.
(void) ack_to_wait;
const SDL_KeyboardEvent *event) {
struct sc_keyboard_inject *ki = DOWNCAST(kp);
if (event->repeat) {
@@ -256,7 +250,5 @@ sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
.process_text = sc_key_processor_process_text,
};
// Key injection and clipboard synchronization are serialized
ki->key_processor.async_paste = false;
ki->key_processor.ops = &ops;
}

View File

@@ -19,8 +19,6 @@ const struct scrcpy_options scrcpy_options_default = {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST,
.last = DEFAULT_LOCAL_PORT_RANGE_LAST,
},
.tunnel_host = 0,
.tunnel_port = 0,
.shortcut_mods = {
.data = {SC_MOD_LALT, SC_MOD_LSUPER},
.count = 2,

View File

@@ -77,8 +77,6 @@ struct scrcpy_options {
enum sc_record_format record_format;
enum sc_keyboard_input_mode keyboard_input_mode;
struct sc_port_range port_range;
uint32_t tunnel_host;
uint16_t tunnel_port;
struct sc_shortcut_mods shortcut_mods;
uint16_t max_size;
uint32_t bit_rate;

View File

@@ -7,16 +7,12 @@
#include "util/log.h"
bool
receiver_init(struct receiver *receiver, sc_socket control_socket,
struct sc_acksync *acksync) {
receiver_init(struct receiver *receiver, sc_socket control_socket) {
bool ok = sc_mutex_init(&receiver->mutex);
if (!ok) {
return false;
}
receiver->control_socket = control_socket;
receiver->acksync = acksync;
return true;
}
@@ -26,7 +22,7 @@ receiver_destroy(struct receiver *receiver) {
}
static void
process_msg(struct receiver *receiver, struct device_msg *msg) {
process_msg(struct device_msg *msg) {
switch (msg->type) {
case DEVICE_MSG_TYPE_CLIPBOARD: {
char *current = SDL_GetClipboardText();
@@ -41,17 +37,11 @@ process_msg(struct receiver *receiver, struct device_msg *msg) {
SDL_SetClipboardText(msg->clipboard.text);
break;
}
case DEVICE_MSG_TYPE_ACK_CLIPBOARD:
assert(receiver->acksync);
LOGD("Ack device clipboard sequence=%" PRIu64_,
msg->ack_clipboard.sequence);
sc_acksync_ack(receiver->acksync, msg->ack_clipboard.sequence);
break;
}
}
static ssize_t
process_msgs(struct receiver *receiver, const unsigned char *buf, size_t len) {
process_msgs(const unsigned char *buf, size_t len) {
size_t head = 0;
for (;;) {
struct device_msg msg;
@@ -63,7 +53,7 @@ process_msgs(struct receiver *receiver, const unsigned char *buf, size_t len) {
return head;
}
process_msg(receiver, &msg);
process_msg(&msg);
device_msg_destroy(&msg);
head += r;
@@ -91,7 +81,7 @@ run_receiver(void *data) {
}
head += r;
ssize_t consumed = process_msgs(receiver, buf, head);
ssize_t consumed = process_msgs(buf, head);
if (consumed == -1) {
// an error occurred
break;

View File

@@ -5,7 +5,6 @@
#include <stdbool.h>
#include "util/acksync.h"
#include "util/net.h"
#include "util/thread.h"
@@ -15,13 +14,10 @@ struct receiver {
sc_socket control_socket;
sc_thread thread;
sc_mutex mutex;
struct sc_acksync *acksync;
};
bool
receiver_init(struct receiver *receiver, sc_socket control_socket,
struct sc_acksync *acksync);
receiver_init(struct receiver *receiver, sc_socket control_socket);
void
receiver_destroy(struct receiver *receiver);

View File

@@ -27,7 +27,6 @@
#include "screen.h"
#include "server.h"
#include "stream.h"
#include "util/acksync.h"
#include "util/log.h"
#include "util/net.h"
#ifdef HAVE_V4L2
@@ -47,8 +46,6 @@ struct scrcpy {
struct file_handler file_handler;
#ifdef HAVE_AOA_HID
struct sc_aoa aoa;
// sequence/ack helper to synchronize clipboard and Ctrl+v via HID
struct sc_acksync acksync;
#endif
union {
struct sc_keyboard_inject keyboard_inject;
@@ -343,15 +340,11 @@ scrcpy(struct scrcpy_options *options) {
bool controller_started = false;
bool screen_initialized = false;
struct sc_acksync *acksync = NULL;
struct sc_server_params params = {
.serial = options->serial,
.log_level = options->log_level,
.crop = options->crop,
.port_range = options->port_range,
.tunnel_host = options->tunnel_host,
.tunnel_port = options->tunnel_port,
.max_size = options->max_size,
.bit_rate = options->bit_rate,
.max_fps = options->max_fps,
@@ -450,18 +443,7 @@ scrcpy(struct scrcpy_options *options) {
}
if (options->control) {
#ifdef HAVE_AOA_HID
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID) {
bool ok = sc_acksync_init(&s->acksync);
if (!ok) {
goto end;
}
acksync = &s->acksync;
}
#endif
if (!controller_init(&s->controller, s->server.control_socket,
acksync)) {
if (!controller_init(&s->controller, s->server.control_socket)) {
goto end;
}
controller_initialized = true;
@@ -537,7 +519,7 @@ scrcpy(struct scrcpy_options *options) {
#ifdef HAVE_AOA_HID
bool aoa_hid_ok = false;
bool ok = sc_aoa_init(&s->aoa, serial, acksync);
bool ok = sc_aoa_init(&s->aoa, serial);
if (!ok) {
goto aoa_hid_end;
}
@@ -602,9 +584,6 @@ end:
sc_hid_keyboard_destroy(&s->keyboard_hid);
sc_aoa_stop(&s->aoa);
}
if (acksync) {
sc_acksync_destroy(acksync);
}
#endif
if (controller_started) {
controller_stop(&s->controller);

View File

@@ -112,7 +112,8 @@ push_server(struct sc_intr *intr, const char *serial) {
free(server_path);
return false;
}
bool ok = adb_push(intr, serial, server_path, SC_DEVICE_SERVER_PATH);
bool ok = adb_push(intr, serial, server_path, SC_DEVICE_SERVER_PATH,
SC_INHERIT_STDERR);
free(server_path);
return ok;
}
@@ -198,13 +199,14 @@ execute_server(struct sc_server *server,
// Port: 5005
// Then click on "Debug"
#endif
return adb_execute(serial, cmd, ARRAY_LEN(cmd));
// Inherit both stdout and stderr (all server logs are printed to stdout)
return adb_execute(serial, cmd, ARRAY_LEN(cmd),
SC_INHERIT_STDOUT | SC_INHERIT_STDERR);
}
static bool
connect_and_read_byte(struct sc_intr *intr, sc_socket socket,
uint32_t tunnel_host, uint16_t tunnel_port) {
bool ok = net_connect_intr(intr, socket, tunnel_host, tunnel_port);
connect_and_read_byte(struct sc_intr *intr, sc_socket socket, uint16_t port) {
bool ok = net_connect_intr(intr, socket, IPV4_LOCALHOST, port);
if (!ok) {
return false;
}
@@ -221,13 +223,13 @@ connect_and_read_byte(struct sc_intr *intr, sc_socket socket,
}
static sc_socket
connect_to_server(struct sc_server *server, uint32_t attempts, sc_tick delay,
uint32_t host, uint16_t port) {
connect_to_server(struct sc_server *server, uint32_t attempts, sc_tick delay) {
uint16_t port = server->tunnel.local_port;
do {
LOGD("Remaining connection attempts: %d", (int) attempts);
sc_socket socket = net_socket();
if (socket != SC_SOCKET_NONE) {
bool ok = connect_and_read_byte(&server->intr, socket, host, port);
bool ok = connect_and_read_byte(&server->intr, socket, port);
if (ok) {
// it worked!
return socket;
@@ -353,20 +355,9 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
goto fail;
}
} else {
uint32_t tunnel_host = server->params.tunnel_host;
if (!tunnel_host) {
tunnel_host = IPV4_LOCALHOST;
}
uint16_t tunnel_port = server->params.tunnel_port;
if (!tunnel_port) {
tunnel_port = tunnel->local_port;
}
uint32_t attempts = 100;
sc_tick delay = SC_TICK_FROM_MS(100);
video_socket = connect_to_server(server, attempts, delay, tunnel_host,
tunnel_port);
video_socket = connect_to_server(server, attempts, delay);
if (video_socket == SC_SOCKET_NONE) {
goto fail;
}
@@ -376,8 +367,8 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) {
if (control_socket == SC_SOCKET_NONE) {
goto fail;
}
bool ok = net_connect_intr(&server->intr, control_socket, tunnel_host,
tunnel_port);
bool ok = net_connect_intr(&server->intr, control_socket,
IPV4_LOCALHOST, tunnel->local_port);
if (!ok) {
goto fail;
}
@@ -442,7 +433,8 @@ sc_server_fill_serial(struct sc_server *server) {
// 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);
server->params.serial = adb_get_serialno(&server->intr,
SC_INHERIT_STDERR);
if (!server->params.serial) {
LOGE("Could not get device serial");
return false;
@@ -469,6 +461,8 @@ run_server(void *data) {
goto error_connection_failed;
}
LOGI("Server pushed");
ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, params->serial,
params->port_range, params->force_adb_forward);
if (!ok) {

View File

@@ -29,8 +29,6 @@ struct sc_server_params {
const char *codec_options;
const char *encoder_name;
struct sc_port_range port_range;
uint32_t tunnel_host;
uint16_t tunnel_port;
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;

View File

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

View File

@@ -1,3 +1,6 @@
// <https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873>
#define _WIN32_WINNT 0x0600 // For extended process API
#include "util/process.h"
#include <processthreadsapi.h>
@@ -24,12 +27,17 @@ build_cmd(char *cmd, size_t len, const char *const argv[]) {
}
enum sc_process_result
sc_process_execute_p(const char *const argv[], HANDLE *handle,
sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned inherit,
HANDLE *pin, HANDLE *pout, HANDLE *perr) {
enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
bool inherit_stdout = inherit & SC_INHERIT_STDOUT;
bool inherit_stderr = inherit & SC_INHERIT_STDERR;
// Add 1 per non-NULL pointer
unsigned handle_count = !!pin + !!pout + !!perr;
// If pout is defined, then inherit MUST NOT contain SC_INHERIT_STDOUT.
assert(!pout || !inherit_stdout);
// If perr is defined, then inherit MUST NOT contain SC_INHERIT_STDERR.
assert(!perr || !inherit_stderr);
enum sc_process_result ret = SC_PROCESS_ERROR_GENERIC;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
@@ -77,59 +85,52 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle,
HANDLE handles[3];
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
if (handle_count) {
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (pin) {
si.StartupInfo.hStdInput = stdin_read_handle;
}
if (pout) {
si.StartupInfo.hStdOutput = stdout_write_handle;
}
if (perr) {
si.StartupInfo.hStdError = stderr_write_handle;
}
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
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;
}
// Explicitly pass the HANDLEs that must be inherited
unsigned i = 0;
if (pin) {
handles[i++] = stdin_read_handle;
}
if (pout) {
handles[i++] = stdout_write_handle;
}
if (perr) {
handles[i++] = stderr_write_handle;
}
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;
unsigned handle_count = 0;
if (pin) {
si.StartupInfo.hStdInput = stdin_read_handle;
handles[handle_count++] = si.StartupInfo.hStdInput;
}
if (pout || inherit_stdout) {
si.StartupInfo.hStdOutput = pout ? stdout_write_handle
: GetStdHandle(STD_OUTPUT_HANDLE);
handles[handle_count++] = si.StartupInfo.hStdOutput;
}
if (perr || inherit_stderr) {
si.StartupInfo.hStdError = perr ? stderr_write_handle
: GetStdHandle(STD_ERROR_HANDLE);
handles[handle_count++] = 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);
if (!cmd || !build_cmd(cmd, CMD_MAX_LEN, argv)) {
@@ -143,10 +144,9 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle,
goto error_free_attribute_list;
}
BOOL bInheritHandles = handle_count > 0;
DWORD dwCreationFlags = handle_count > 0 ? EXTENDED_STARTUPINFO_PRESENT : 0;
BOOL ok = CreateProcessW(NULL, wide, NULL, NULL, bInheritHandles,
dwCreationFlags, NULL, NULL, &si.StartupInfo, &pi);
ok = CreateProcessW(NULL, wide, NULL, NULL, TRUE,
EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
&si.StartupInfo, &pi);
free(wide);
if (!ok) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {

View File

@@ -14,31 +14,12 @@
* Component able to process and inject keys should implement this trait.
*/
struct sc_key_processor {
/**
* Set by the implementation to indicate that it must explicitly wait for
* the clipboard to be set on the device before injecting Ctrl+v to avoid
* race conditions. If it is set, the input_manager will pass a valid
* ack_to_wait to process_key() in case of clipboard synchronization
* resulting of the key event.
*/
bool async_paste;
const struct sc_key_processor_ops *ops;
};
struct sc_key_processor_ops {
/**
* Process the keyboard event
*
* The `sequence` number (if different from `SC_SEQUENCE_INVALID`) indicates
* the acknowledgement number to wait for before injecting this event.
* This allows to ensure that the device clipboard is set before injecting
* Ctrl+v on the device.
*/
void
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event,
uint64_t ack_to_wait);
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event);
void
(*process_text)(struct sc_key_processor *kp,

View File

@@ -1,76 +0,0 @@
#include "acksync.h"
#include <assert.h>
#include "util/log.h"
bool
sc_acksync_init(struct sc_acksync *as) {
bool ok = sc_mutex_init(&as->mutex);
if (!ok) {
return false;
}
ok = sc_cond_init(&as->cond);
if (!ok) {
sc_mutex_destroy(&as->mutex);
return false;
}
as->stopped = false;
as->ack = SC_SEQUENCE_INVALID;
return true;
}
void
sc_acksync_destroy(struct sc_acksync *as) {
sc_cond_destroy(&as->cond);
sc_mutex_destroy(&as->mutex);
}
void
sc_acksync_ack(struct sc_acksync *as, uint64_t sequence) {
sc_mutex_lock(&as->mutex);
// Acknowledgements must be monotonic
assert(sequence >= as->ack);
as->ack = sequence;
sc_cond_signal(&as->cond);
sc_mutex_unlock(&as->mutex);
}
enum sc_acksync_wait_result
sc_acksync_wait(struct sc_acksync *as, uint64_t ack, sc_tick deadline) {
sc_mutex_lock(&as->mutex);
bool timed_out = false;
while (!as->stopped && as->ack < ack && !timed_out) {
timed_out = !sc_cond_timedwait(&as->cond, &as->mutex, deadline);
}
enum sc_acksync_wait_result ret;
if (as->stopped) {
ret = SC_ACKSYNC_WAIT_INTR;
} else if (as->ack >= ack) {
ret = SC_ACKSYNC_WAIT_OK;
} else {
assert(timed_out);
ret = SC_ACKSYNC_WAIT_TIMEOUT;
}
sc_mutex_unlock(&as->mutex);
return ret;
}
/**
* Interrupt any `sc_acksync_wait()`
*/
void
sc_acksync_interrupt(struct sc_acksync *as) {
sc_mutex_lock(&as->mutex);
as->stopped = true;
sc_cond_signal(&as->cond);
sc_mutex_unlock(&as->mutex);
}

View File

@@ -1,66 +0,0 @@
#ifndef SC_ACK_SYNC_H
#define SC_ACK_SYNC_H
#include "common.h"
#include "thread.h"
#define SC_SEQUENCE_INVALID 0
/**
* Helper to wait for acknowledgments
*
* In practice, it is used to wait for device clipboard acknowledgement from the
* server before injecting Ctrl+v via AOA HID, in order to avoid pasting the
* content of the old device clipboard (if Ctrl+v was injected before the
* clipboard content was actually set).
*/
struct sc_acksync {
sc_mutex mutex;
sc_cond cond;
bool stopped;
// Last acked value, initially SC_SEQUENCE_INVALID
uint64_t ack;
};
enum sc_acksync_wait_result {
// Acknowledgment received
SC_ACKSYNC_WAIT_OK,
// Timeout expired
SC_ACKSYNC_WAIT_TIMEOUT,
// Interrupted from another thread by sc_acksync_interrupt()
SC_ACKSYNC_WAIT_INTR,
};
bool
sc_acksync_init(struct sc_acksync *as);
void
sc_acksync_destroy(struct sc_acksync *as);
/**
* Acknowledge `sequence`
*
* The `sequence` must be greater than (or equal to) any previous acknowledged
* sequence.
*/
void
sc_acksync_ack(struct sc_acksync *as, uint64_t sequence);
/**
* Wait for acknowledgment of sequence `ack` (or higher)
*/
enum sc_acksync_wait_result
sc_acksync_wait(struct sc_acksync *as, uint64_t ack, sc_tick deadline);
/**
* Interrupt any `sc_acksync_wait()`
*/
void
sc_acksync_interrupt(struct sc_acksync *as);
#endif

View File

@@ -7,7 +7,6 @@
#include "log.h"
#ifdef __WINDOWS__
# include <ws2tcpip.h>
typedef int socklen_t;
typedef SOCKET sc_raw_socket;
#else
@@ -226,15 +225,3 @@ net_close(sc_socket socket) {
return !close(raw_sock);
#endif
}
bool
net_parse_ipv4(const char *s, uint32_t *ipv4) {
struct in_addr addr;
if (!inet_pton(AF_INET, s, &addr)) {
LOGE("Invalid IPv4 address: %s", s);
return false;
}
*ipv4 = ntohl(addr.s_addr);
return true;
}

View File

@@ -68,10 +68,4 @@ net_interrupt(sc_socket socket);
bool
net_close(sc_socket socket);
/**
* Parse `ip` "xxx.xxx.xxx.xxx" to an IPv4 host representation
*/
bool
net_parse_ipv4(const char *ip, uint32_t *ipv4);
#endif

View File

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

View File

@@ -67,20 +67,32 @@ enum sc_process_result {
SC_PROCESS_ERROR_MISSING_BINARY,
};
#define SC_INHERIT_NONE 0
#define SC_INHERIT_STDOUT (1 << 0)
#define SC_INHERIT_STDERR (1 << 1)
/**
* Execute the command and write the process id to `pid`
*
* The parameter `inherit` is a ORed value of SC_INHERIT_* flags. 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
sc_process_execute(const char *const argv[], sc_pid *pid);
sc_process_execute(const char *const argv[], sc_pid *pid, unsigned inherit);
/**
* Execute the command and write the process id to `pid`
*
* If not NULL, provide a pipe for stdin (`pin`), stdout (`pout`) and stderr
* (`perr`).
*
* The parameter `inherit` has the same semantics as in `sc_process_execute()`.
* If `pout` is not NULL, then `inherit` MUST NOT contain SC_INHERIT_STDOUT.
* If `perr` is not NULL, then `inherit` MUST NOT contain SC_INHERIT_STDERR.
*/
enum sc_process_result
sc_process_execute_p(const char *const argv[], sc_pid *pid,
sc_process_execute_p(const char *const argv[], sc_pid *pid, unsigned inherit,
sc_pipe *pin, sc_pipe *pout, sc_pipe *perr);
/**

View File

@@ -78,7 +78,7 @@ static void test_serialize_inject_touch_event(void) {
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
.inject_touch_event = {
.action = AMOTION_EVENT_ACTION_DOWN,
.pointer_id = UINT64_C(0x1234567887654321),
.pointer_id = 0x1234567887654321L,
.position = {
.point = {
.x = 100,
@@ -226,7 +226,6 @@ static void test_serialize_set_clipboard(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
.set_clipboard = {
.sequence = UINT64_C(0x0102030405060708),
.paste = true,
.text = "hello, world!",
},
@@ -234,11 +233,10 @@ static void test_serialize_set_clipboard(void) {
unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf);
assert(size == 27);
assert(size == 19);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_SET_CLIPBOARD,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
1, // paste
0x00, 0x00, 0x00, 0x0d, // text length
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text

View File

@@ -47,26 +47,11 @@ static void test_deserialize_clipboard_big(void) {
device_msg_destroy(&msg);
}
static void test_deserialize_ack_set_clipboard(void) {
const unsigned char input[] = {
DEVICE_MSG_TYPE_ACK_CLIPBOARD,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
};
struct device_msg msg;
ssize_t r = device_msg_deserialize(input, sizeof(input), &msg);
assert(r == 9);
assert(msg.type == DEVICE_MSG_TYPE_ACK_CLIPBOARD);
assert(msg.ack_clipboard.sequence == UINT64_C(0x0102030405060708));
}
int main(int argc, char *argv[]) {
(void) argc;
(void) argv;
test_deserialize_clipboard();
test_deserialize_clipboard_big();
test_deserialize_ack_set_clipboard();
return 0;
}

View File

@@ -18,8 +18,6 @@ public final class ControlMessage {
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
public static final int TYPE_ROTATE_DEVICE = 11;
public static final long SEQUENCE_INVALID = 0;
private int type;
private String text;
private int metaState; // KeyEvent.META_*
@@ -33,7 +31,6 @@ public final class ControlMessage {
private int vScroll;
private boolean paste;
private int repeat;
private long sequence;
private ControlMessage() {
}
@@ -82,10 +79,9 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createSetClipboard(long sequence, String text, boolean paste) {
public static ControlMessage createSetClipboard(String text, boolean paste) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_SET_CLIPBOARD;
msg.sequence = sequence;
msg.text = text;
msg.paste = paste;
return msg;
@@ -158,8 +154,4 @@ public final class ControlMessage {
public int getRepeat() {
return repeat;
}
public long getSequence() {
return sequence;
}
}

View File

@@ -13,11 +13,11 @@ public class ControlMessageReader {
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 9;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
public static final int CLIPBOARD_TEXT_MAX_LENGTH = MESSAGE_MAX_SIZE - 14; // type: 1 byte; sequence: 8 bytes; paste flag: 1 byte; length: 4 bytes
public static final int CLIPBOARD_TEXT_MAX_LENGTH = MESSAGE_MAX_SIZE - 6; // type: 1 byte; paste flag: 1 byte; length: 4 bytes
public static final int INJECT_TEXT_MAX_LENGTH = 300;
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
@@ -166,13 +166,12 @@ public class ControlMessageReader {
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
return null;
}
long sequence = buffer.getLong();
boolean paste = buffer.get() != 0;
String text = parseString();
if (text == null) {
return null;
}
return ControlMessage.createSetClipboard(sequence, text, paste);
return ControlMessage.createSetClipboard(text, paste);
}
private ControlMessage parseSetScreenPowerMode() {

View File

@@ -120,12 +120,7 @@ public class Controller {
}
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
long sequence = msg.getSequence();
setClipboard(msg.getText(), msg.getPaste());
if (sequence != ControlMessage.SEQUENCE_INVALID) {
// Acknowledgement requested
sender.pushAckClipboard(sequence);
}
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
if (device.supportsInputEvents()) {

View File

@@ -3,13 +3,9 @@ package com.genymobile.scrcpy;
public final class DeviceMessage {
public static final int TYPE_CLIPBOARD = 0;
public static final int TYPE_ACK_CLIPBOARD = 1;
public static final long SEQUENCE_INVALID = ControlMessage.SEQUENCE_INVALID;
private int type;
private String text;
private long sequence;
private DeviceMessage() {
}
@@ -21,13 +17,6 @@ public final class DeviceMessage {
return event;
}
public static DeviceMessage createAckClipboard(long sequence) {
DeviceMessage event = new DeviceMessage();
event.type = TYPE_ACK_CLIPBOARD;
event.sequence = sequence;
return event;
}
public int getType() {
return type;
}
@@ -35,8 +24,4 @@ public final class DeviceMessage {
public String getText() {
return text;
}
public long getSequence() {
return sequence;
}
}

View File

@@ -8,8 +8,6 @@ public final class DeviceMessageSender {
private String clipboardText;
private long ack;
public DeviceMessageSender(DesktopConnection connection) {
this.connection = connection;
}
@@ -19,34 +17,18 @@ public final class DeviceMessageSender {
notify();
}
public synchronized void pushAckClipboard(long sequence) {
ack = sequence;
notify();
}
public void loop() throws IOException, InterruptedException {
while (true) {
String text;
long sequence;
synchronized (this) {
while (ack == DeviceMessage.SEQUENCE_INVALID && clipboardText == null) {
while (clipboardText == null) {
wait();
}
text = clipboardText;
clipboardText = null;
sequence = ack;
ack = DeviceMessage.SEQUENCE_INVALID;
}
if (sequence != DeviceMessage.SEQUENCE_INVALID) {
DeviceMessage event = DeviceMessage.createAckClipboard(sequence);
connection.sendDeviceMessage(event);
}
if (text != null) {
DeviceMessage event = DeviceMessage.createClipboard(text);
connection.sendDeviceMessage(event);
}
DeviceMessage event = DeviceMessage.createClipboard(text);
connection.sendDeviceMessage(event);
}
}
}

View File

@@ -15,7 +15,7 @@ public class DeviceMessageWriter {
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
buffer.clear();
buffer.put((byte) msg.getType());
buffer.put((byte) DeviceMessage.TYPE_CLIPBOARD);
switch (msg.getType()) {
case DeviceMessage.TYPE_CLIPBOARD:
String text = msg.getText();
@@ -25,10 +25,6 @@ public class DeviceMessageWriter {
buffer.put(raw, 0, len);
output.write(rawBuffer, 0, buffer.position());
break;
case DeviceMessage.TYPE_ACK_CLIPBOARD:
buffer.putLong(msg.getSequence());
output.write(rawBuffer, 0, buffer.position());
break;
default:
Ln.w("Unknown device message: " + msg.getType());
break;

View File

@@ -235,7 +235,6 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
dos.writeLong(0x0102030405060708L); // sequence
dos.writeByte(1); // paste
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
dos.writeInt(text.length);
@@ -247,7 +246,6 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals(0x0102030405060708L, event.getSequence());
Assert.assertEquals("testé", event.getText());
Assert.assertTrue(event.getPaste());
}
@@ -261,7 +259,6 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
byte[] rawText = new byte[ControlMessageReader.CLIPBOARD_TEXT_MAX_LENGTH];
dos.writeLong(0x0807060504030201L); // sequence
dos.writeByte(1); // paste
Arrays.fill(rawText, (byte) 'a');
String text = new String(rawText, 0, rawText.length);
@@ -275,7 +272,6 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals(0x0807060504030201L, event.getSequence());
Assert.assertEquals(text, event.getText());
Assert.assertTrue(event.getPaste());
}

View File

@@ -32,24 +32,4 @@ public class DeviceMessageWriterTest {
Assert.assertArrayEquals(expected, actual);
}
@Test
public void testSerializeAckSetClipboard() throws IOException {
DeviceMessageWriter writer = new DeviceMessageWriter();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(DeviceMessage.TYPE_ACK_CLIPBOARD);
dos.writeLong(0x0102030405060708L);
byte[] expected = bos.toByteArray();
DeviceMessage msg = DeviceMessage.createAckClipboard(0x0102030405060708L);
bos = new ByteArrayOutputStream();
writer.writeTo(msg, bos);
byte[] actual = bos.toByteArray();
Assert.assertArrayEquals(expected, actual);
}
}