Compare commits

..

4 Commits

Author SHA1 Message Date
Romain Vimont
f56be59a0c Document remote ADB server in README
Refs #2807 <https://github.com/Genymobile/scrcpy/pull/2807>
2021-11-19 21:11:56 +01:00
Romain Vimont
64400c8740 Force adb forward if tunnel host/port is provided
Tunnel host and port are only meaningful in "adb forward" mode.

They indicate where the client must connect to communicate with the
server. In "adb reverse" mode, the client _listens_, it does not
_connect_.

Refs #2807 <https://github.com/Genymobile/scrcpy/pull/2807>
2021-11-19 21:07:22 +01:00
RipleyTom
46f7cd31b9 Add options to configure tunnel host and port
In "adb forward" mode, by default, scrcpy connects to localhost:PORT,
where PORT is the local port passed to "adb forward". This assumes that
the tunnel is established on the local host with a local adb server
(which is the common case).

For advanced usage, add --tunnel-host and --tunnel-port to force the
connection to a different destination.

Fixes #2801 <https://github.com/Genymobile/scrcpy/issues/2801>
PR #2807 <https://github.com/Genymobile/scrcpy/pull/2807>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-11-19 21:05:11 +01:00
RipleyTom
d290b44248 Add function to parse IPv4 addresses
PR #2807 <https://github.com/Genymobile/scrcpy/pull/2807>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-11-19 21:04:52 +01:00
42 changed files with 91 additions and 479 deletions

View File

@@ -730,9 +730,6 @@ of <kbd>Ctrl</kbd>+<kbd>v</kbd> and <kbd>MOD</kbd>+<kbd>v</kbd> so that they
also inject the computer clipboard text as a sequence of key events (the same
way as <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>).
To disable automatic clipboard synchronization, use
`--no-clipboard-autosync`.
#### Pinch-to-zoom
To simulate "pinch-to-zoom": <kbd>Ctrl</kbd>+_click-and-move_.

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()

View File

@@ -114,12 +114,6 @@ Limit both the width and height of the video to \fIvalue\fR. The other dimension
Default is 0 (unlimited).
.TP
.B \-\-no\-clipboard\-autosync
By default, scrcpy automatically synchronizes the computer clipboard to the device clipboard before injecting Ctrl+v, and the device clipboard to the computer clipboard whenever it changes.
This option disables this automatic synchronization.
.TP
.B \-n, \-\-no\-control
Disable device control (mirror the device in read\-only).

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

@@ -49,7 +49,6 @@
#define OPT_V4L2_BUFFER 1029
#define OPT_TUNNEL_HOST 1030
#define OPT_TUNNEL_PORT 1031
#define OPT_NO_CLIPBOARD_AUTOSYNC 1032
struct sc_option {
char shortopt;
@@ -209,15 +208,6 @@ static const struct sc_option options[] = {
"is preserved.\n"
"Default is 0 (unlimited).",
},
{
.longopt_id = OPT_NO_CLIPBOARD_AUTOSYNC,
.longopt = "no-clipboard-autosync",
.text = "By default, scrcpy automatically synchronizes the computer "
"clipboard to the device clipboard before injecting Ctrl+v, "
"and the device clipboard to the computer clipboard whenever "
"it changes.\n"
"This option disables this automatic synchronization."
},
{
.shortopt = 'n',
.longopt = "no-control",
@@ -1374,9 +1364,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
break;
case OPT_NO_CLIPBOARD_AUTOSYNC:
opts->clipboard_autosync = false;
break;
#ifdef HAVE_V4L2
case OPT_V4L2_SINK:
opts->v4l2_device = optarg;

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

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

@@ -66,7 +66,6 @@ input_manager_init(struct input_manager *im, struct controller *controller,
im->control = options->control;
im->forward_all_clicks = options->forward_all_clicks;
im->legacy_paste = options->legacy_paste;
im->clipboard_autosync = options->clipboard_autosync;
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
assert(shortcut_mods->count);
@@ -83,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
@@ -211,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
@@ -465,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;
@@ -517,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 (im->clipboard_autosync && 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

@@ -24,7 +24,6 @@ struct input_manager {
bool control;
bool forward_all_clicks;
bool legacy_paste;
bool clipboard_autosync;
struct {
unsigned data[SC_MAX_SHORTCUT_MODS];
@@ -39,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

@@ -53,5 +53,4 @@ const struct scrcpy_options scrcpy_options_default = {
.forward_all_clicks = false,
.legacy_paste = false,
.power_off_on_close = false,
.clipboard_autosync = true,
};

View File

@@ -108,7 +108,6 @@ struct scrcpy_options {
bool forward_all_clicks;
bool legacy_paste;
bool power_off_on_close;
bool clipboard_autosync;
};
extern const struct scrcpy_options scrcpy_options_default;

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,8 +340,6 @@ 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,
@@ -364,7 +359,6 @@ scrcpy(struct scrcpy_options *options) {
.encoder_name = options->encoder_name,
.force_adb_forward = options->force_adb_forward,
.power_off_on_close = options->power_off_on_close,
.clipboard_autosync = options->clipboard_autosync,
};
static const struct sc_server_callbacks cbs = {
@@ -451,18 +445,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;
@@ -538,7 +521,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;
}
@@ -603,9 +586,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

@@ -186,7 +186,6 @@ execute_server(struct sc_server *server,
params->codec_options ? params->codec_options : "-",
params->encoder_name ? params->encoder_name : "-",
params->power_off_on_close ? "true" : "false",
params->clipboard_autosync ? "true" : "false",
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "

View File

@@ -41,7 +41,6 @@ struct sc_server_params {
bool stay_awake;
bool force_adb_forward;
bool power_off_on_close;
bool clipboard_autosync;
};
struct sc_server {

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>

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

@@ -1,3 +1,7 @@
// For inet_pton() on Windows
#define _WIN32_WINNT 0x0600
#define WINVER 0x0600
#include "net.h"
#include <assert.h>

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

@@ -82,8 +82,8 @@ public final class Device {
}
}, displayId);
if (options.getControl() && options.getClipboardAutosync()) {
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
if (options.getControl()) {
// If control is enabled, synchronize Android clipboard to the computer automatically
ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager();
if (clipboardManager != null) {
clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {

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

@@ -18,7 +18,6 @@ public class Options {
private String codecOptions;
private String encoderName;
private boolean powerOffScreenOnClose;
private boolean clipboardAutosync;
public Ln.Level getLogLevel() {
return logLevel;
@@ -139,12 +138,4 @@ public class Options {
public boolean getPowerOffScreenOnClose() {
return this.powerOffScreenOnClose;
}
public boolean getClipboardAutosync() {
return clipboardAutosync;
}
public void setClipboardAutosync(boolean clipboardAutosync) {
this.clipboardAutosync = clipboardAutosync;
}
}

View File

@@ -160,7 +160,7 @@ public final class Server {
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
}
final int expectedParameters = 17;
final int expectedParameters = 16;
if (args.length != expectedParameters) {
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
}
@@ -213,9 +213,6 @@ public final class Server {
boolean powerOffScreenOnClose = Boolean.parseBoolean(args[15]);
options.setPowerOffScreenOnClose(powerOffScreenOnClose);
boolean clipboardAutosync = Boolean.parseBoolean(args[16]);
options.setClipboardAutosync(clipboardAutosync);
return options;
}

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