Compare commits
4 Commits
no_clipboa
...
pr2807
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f56be59a0c | ||
|
|
64400c8740 | ||
|
|
46f7cd31b9 | ||
|
|
d290b44248 |
@@ -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
|
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>).
|
way as <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>).
|
||||||
|
|
||||||
To disable automatic clipboard synchronization, use
|
|
||||||
`--no-clipboard-autosync`.
|
|
||||||
|
|
||||||
#### Pinch-to-zoom
|
#### Pinch-to-zoom
|
||||||
|
|
||||||
To simulate "pinch-to-zoom": <kbd>Ctrl</kbd>+_click-and-move_.
|
To simulate "pinch-to-zoom": <kbd>Ctrl</kbd>+_click-and-move_.
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ src = [
|
|||||||
'src/server.c',
|
'src/server.c',
|
||||||
'src/stream.c',
|
'src/stream.c',
|
||||||
'src/video_buffer.c',
|
'src/video_buffer.c',
|
||||||
'src/util/acksync.c',
|
|
||||||
'src/util/file.c',
|
'src/util/file.c',
|
||||||
'src/util/intr.c',
|
'src/util/intr.c',
|
||||||
'src/util/log.c',
|
'src/util/log.c',
|
||||||
@@ -40,26 +39,16 @@ src = [
|
|||||||
'src/util/tick.c',
|
'src/util/tick.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
conf = configuration_data()
|
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
src += [
|
src += [
|
||||||
'src/sys/win/file.c',
|
'src/sys/win/file.c',
|
||||||
'src/sys/win/process.c',
|
'src/sys/win/process.c',
|
||||||
]
|
]
|
||||||
conf.set('_WIN32_WINNT', '0x0600')
|
|
||||||
conf.set('WINVER', '0x0600')
|
|
||||||
else
|
else
|
||||||
src += [
|
src += [
|
||||||
'src/sys/unix/file.c',
|
'src/sys/unix/file.c',
|
||||||
'src/sys/unix/process.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
|
endif
|
||||||
|
|
||||||
v4l2_support = host_machine.system() == 'linux'
|
v4l2_support = host_machine.system() == 'linux'
|
||||||
@@ -139,6 +128,8 @@ if host_machine.system() == 'windows'
|
|||||||
dependencies += cc.find_library('ws2_32')
|
dependencies += cc.find_library('ws2_32')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
conf = configuration_data()
|
||||||
|
|
||||||
foreach f : check_functions
|
foreach f : check_functions
|
||||||
if cc.has_function(f)
|
if cc.has_function(f)
|
||||||
define = 'HAVE_' + f.underscorify().to_upper()
|
define = 'HAVE_' + f.underscorify().to_upper()
|
||||||
|
|||||||
@@ -114,12 +114,6 @@ Limit both the width and height of the video to \fIvalue\fR. The other dimension
|
|||||||
|
|
||||||
Default is 0 (unlimited).
|
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
|
.TP
|
||||||
.B \-n, \-\-no\-control
|
.B \-n, \-\-no\-control
|
||||||
Disable device control (mirror the device in read\-only).
|
Disable device control (mirror the device in read\-only).
|
||||||
|
|||||||
@@ -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->accessory_id = accessory_id;
|
||||||
hid_event->buffer = buffer;
|
hid_event->buffer = buffer;
|
||||||
hid_event->size = buffer_size;
|
hid_event->size = buffer_size;
|
||||||
hid_event->ack_to_wait = SC_SEQUENCE_INVALID;
|
hid_event->delay = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -118,10 +118,7 @@ sc_aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
sc_aoa_init(struct sc_aoa *aoa, const char *serial) {
|
||||||
struct sc_acksync *acksync) {
|
|
||||||
assert(acksync);
|
|
||||||
|
|
||||||
cbuf_init(&aoa->queue);
|
cbuf_init(&aoa->queue);
|
||||||
|
|
||||||
if (!sc_mutex_init(&aoa->mutex)) {
|
if (!sc_mutex_init(&aoa->mutex)) {
|
||||||
@@ -158,7 +155,6 @@ sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
|||||||
}
|
}
|
||||||
|
|
||||||
aoa->stopped = false;
|
aoa->stopped = false;
|
||||||
aoa->acksync = acksync;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -336,28 +332,23 @@ run_aoa_thread(void *data) {
|
|||||||
assert(non_empty);
|
assert(non_empty);
|
||||||
(void) non_empty;
|
(void) non_empty;
|
||||||
|
|
||||||
uint64_t ack_to_wait = event.ack_to_wait;
|
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);
|
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);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
|
||||||
bool ok = sc_aoa_send_hid_event(aoa, &event);
|
bool ok = sc_aoa_send_hid_event(aoa, &event);
|
||||||
sc_hid_event_destroy(&event);
|
sc_hid_event_destroy(&event);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@@ -386,8 +377,6 @@ sc_aoa_stop(struct sc_aoa *aoa) {
|
|||||||
aoa->stopped = true;
|
aoa->stopped = true;
|
||||||
sc_cond_signal(&aoa->event_cond);
|
sc_cond_signal(&aoa->event_cond);
|
||||||
sc_mutex_unlock(&aoa->mutex);
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
|
||||||
sc_acksync_interrupt(aoa->acksync);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
#include "util/acksync.h"
|
|
||||||
#include "util/cbuf.h"
|
#include "util/cbuf.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
#include "util/tick.h"
|
#include "util/tick.h"
|
||||||
@@ -15,7 +14,7 @@ struct sc_hid_event {
|
|||||||
uint16_t accessory_id;
|
uint16_t accessory_id;
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
uint64_t ack_to_wait;
|
sc_tick delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Takes ownership of buffer
|
// Takes ownership of buffer
|
||||||
@@ -37,12 +36,10 @@ struct sc_aoa {
|
|||||||
sc_cond event_cond;
|
sc_cond event_cond;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
struct sc_hid_event_queue queue;
|
struct sc_hid_event_queue queue;
|
||||||
|
|
||||||
struct sc_acksync *acksync;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
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
|
void
|
||||||
sc_aoa_destroy(struct sc_aoa *aoa);
|
sc_aoa_destroy(struct sc_aoa *aoa);
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
#define OPT_V4L2_BUFFER 1029
|
#define OPT_V4L2_BUFFER 1029
|
||||||
#define OPT_TUNNEL_HOST 1030
|
#define OPT_TUNNEL_HOST 1030
|
||||||
#define OPT_TUNNEL_PORT 1031
|
#define OPT_TUNNEL_PORT 1031
|
||||||
#define OPT_NO_CLIPBOARD_AUTOSYNC 1032
|
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
char shortopt;
|
char shortopt;
|
||||||
@@ -209,15 +208,6 @@ static const struct sc_option options[] = {
|
|||||||
"is preserved.\n"
|
"is preserved.\n"
|
||||||
"Default is 0 (unlimited).",
|
"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',
|
.shortopt = 'n',
|
||||||
.longopt = "no-control",
|
.longopt = "no-control",
|
||||||
@@ -1374,9 +1364,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OPT_NO_CLIPBOARD_AUTOSYNC:
|
|
||||||
opts->clipboard_autosync = false;
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
case OPT_V4L2_SINK:
|
case OPT_V4L2_SINK:
|
||||||
opts->v4l2_device = optarg;
|
opts->v4l2_device = optarg;
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
#ifndef COMPAT_H
|
#ifndef COMPAT_H
|
||||||
#define 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 <libavformat/version.h>
|
||||||
#include <SDL2/SDL_version.h>
|
#include <SDL2/SDL_version.h>
|
||||||
|
|
||||||
#ifndef __WIN32
|
|
||||||
# define PRIu64_ PRIu64
|
|
||||||
#else
|
|
||||||
# define PRIu64_ "I64u" // Windows...
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// In ffmpeg/doc/APIchanges:
|
// In ffmpeg/doc/APIchanges:
|
||||||
// 2018-02-06 - 0694d87024 - lavf 58.9.100 - avformat.h
|
// 2018-02-06 - 0694d87024 - lavf 58.9.100 - avformat.h
|
||||||
// Deprecate use of av_register_input_format(), av_register_output_format(),
|
// Deprecate use of av_register_input_format(), av_register_output_format(),
|
||||||
|
|||||||
@@ -118,12 +118,11 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||||||
buf[1] = msg->inject_keycode.action;
|
buf[1] = msg->inject_keycode.action;
|
||||||
return 2;
|
return 2;
|
||||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
|
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
|
||||||
buffer_write64be(&buf[1], msg->set_clipboard.sequence);
|
buf[1] = !!msg->set_clipboard.paste;
|
||||||
buf[9] = !!msg->set_clipboard.paste;
|
|
||||||
size_t len = write_string(msg->set_clipboard.text,
|
size_t len = write_string(msg->set_clipboard.text,
|
||||||
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
|
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
|
||||||
&buf[10]);
|
&buf[2]);
|
||||||
return 10 + len;
|
return 2 + len;
|
||||||
}
|
}
|
||||||
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
||||||
buf[1] = msg->set_screen_power_mode.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);
|
(long) msg->inject_touch_event.buttons);
|
||||||
} else {
|
} else {
|
||||||
// numeric pointer id
|
// numeric pointer id
|
||||||
|
#ifndef __WIN32
|
||||||
|
# define PRIu64_ PRIu64
|
||||||
|
#else
|
||||||
|
# define PRIu64_ "I64u" // Windows...
|
||||||
|
#endif
|
||||||
LOG_CMSG("touch [id=%" PRIu64_ "] %-4s position=%" PRIi32 ",%"
|
LOG_CMSG("touch [id=%" PRIu64_ "] %-4s position=%" PRIi32 ",%"
|
||||||
PRIi32 " pressure=%g buttons=%06lx",
|
PRIi32 " pressure=%g buttons=%06lx",
|
||||||
id,
|
id,
|
||||||
@@ -195,8 +199,7 @@ control_msg_log(const struct control_msg *msg) {
|
|||||||
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action));
|
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action));
|
||||||
break;
|
break;
|
||||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
case CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
||||||
LOG_CMSG("clipboard %" PRIu64_ " %s \"%s\"",
|
LOG_CMSG("clipboard %s \"%s\"",
|
||||||
msg->set_clipboard.sequence,
|
|
||||||
msg->set_clipboard.paste ? "paste" : "copy",
|
msg->set_clipboard.paste ? "paste" : "copy",
|
||||||
msg->set_clipboard.text);
|
msg->set_clipboard.text);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ struct control_msg {
|
|||||||
// screen may only be turned on on ACTION_DOWN
|
// screen may only be turned on on ACTION_DOWN
|
||||||
} back_or_screen_on;
|
} back_or_screen_on;
|
||||||
struct {
|
struct {
|
||||||
uint64_t sequence;
|
|
||||||
char *text; // owned, to be freed by free()
|
char *text; // owned, to be freed by free()
|
||||||
bool paste;
|
bool paste;
|
||||||
} set_clipboard;
|
} set_clipboard;
|
||||||
|
|||||||
@@ -5,11 +5,10 @@
|
|||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_init(struct controller *controller, sc_socket control_socket,
|
controller_init(struct controller *controller, sc_socket control_socket) {
|
||||||
struct sc_acksync *acksync) {
|
|
||||||
cbuf_init(&controller->queue);
|
cbuf_init(&controller->queue);
|
||||||
|
|
||||||
bool ok = receiver_init(&controller->receiver, control_socket, acksync);
|
bool ok = receiver_init(&controller->receiver, control_socket);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
#include "util/acksync.h"
|
|
||||||
#include "util/cbuf.h"
|
#include "util/cbuf.h"
|
||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
@@ -25,8 +24,7 @@ struct controller {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
controller_init(struct controller *controller, sc_socket control_socket,
|
controller_init(struct controller *controller, sc_socket control_socket);
|
||||||
struct sc_acksync *acksync);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_destroy(struct controller *controller);
|
controller_destroy(struct controller *controller);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "device_msg.h"
|
#include "device_msg.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -35,11 +34,6 @@ device_msg_deserialize(const unsigned char *buf, size_t len,
|
|||||||
msg->clipboard.text = text;
|
msg->clipboard.text = text;
|
||||||
return 5 + clipboard_len;
|
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:
|
default:
|
||||||
LOGW("Unknown device message type: %d", (int) msg->type);
|
LOGW("Unknown device message type: %d", (int) msg->type);
|
||||||
return -1; // error, we cannot recover
|
return -1; // error, we cannot recover
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
enum device_msg_type {
|
enum device_msg_type {
|
||||||
DEVICE_MSG_TYPE_CLIPBOARD,
|
DEVICE_MSG_TYPE_CLIPBOARD,
|
||||||
DEVICE_MSG_TYPE_ACK_CLIPBOARD,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct device_msg {
|
struct device_msg {
|
||||||
@@ -22,9 +21,6 @@ struct device_msg {
|
|||||||
struct {
|
struct {
|
||||||
char *text; // owned, to be freed by free()
|
char *text; // owned, to be freed by free()
|
||||||
} clipboard;
|
} clipboard;
|
||||||
struct {
|
|
||||||
uint64_t sequence;
|
|
||||||
} ack_clipboard;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -278,8 +278,7 @@ push_mod_lock_state(struct sc_hid_keyboard *kb, uint16_t sdl_mod) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
sc_key_processor_process_key(struct sc_key_processor *kp,
|
sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||||
const SDL_KeyboardEvent *event,
|
const SDL_KeyboardEvent *event) {
|
||||||
uint64_t ack_to_wait) {
|
|
||||||
if (event->repeat) {
|
if (event->repeat) {
|
||||||
// In USB HID protocol, key repeat is handled by the host (Android), so
|
// In USB HID protocol, key repeat is handled by the host (Android), so
|
||||||
// just ignore key repeat here.
|
// 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
|
// Ctrl+v is pressed, so clipboard synchronization has been
|
||||||
// requested. Wait until clipboard synchronization is acknowledged
|
// requested. Wait a bit so that the clipboard is set before
|
||||||
// by the server, otherwise it could paste the old clipboard
|
// injecting Ctrl+v via HID, otherwise it would paste the old
|
||||||
// content.
|
// clipboard content.
|
||||||
hid_event.ack_to_wait = ack_to_wait;
|
hid_event.delay = SC_TICK_FROM_MS(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sc_aoa_push_hid_event(kb->aoa, &hid_event)) {
|
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,
|
.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;
|
kb->key_processor.ops = &ops;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ input_manager_init(struct input_manager *im, struct controller *controller,
|
|||||||
im->control = options->control;
|
im->control = options->control;
|
||||||
im->forward_all_clicks = options->forward_all_clicks;
|
im->forward_all_clicks = options->forward_all_clicks;
|
||||||
im->legacy_paste = options->legacy_paste;
|
im->legacy_paste = options->legacy_paste;
|
||||||
im->clipboard_autosync = options->clipboard_autosync;
|
|
||||||
|
|
||||||
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
|
const struct sc_shortcut_mods *shortcut_mods = &options->shortcut_mods;
|
||||||
assert(shortcut_mods->count);
|
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_keycode = SDLK_UNKNOWN;
|
||||||
im->last_mod = 0;
|
im->last_mod = 0;
|
||||||
im->key_repeat = 0;
|
im->key_repeat = 0;
|
||||||
|
|
||||||
im->next_sequence = 1; // 0 is reserved for SC_SEQUENCE_INVALID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -211,35 +208,35 @@ collapse_panels(struct controller *controller) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
set_device_clipboard(struct controller *controller, bool paste,
|
set_device_clipboard(struct controller *controller, bool paste) {
|
||||||
uint64_t sequence) {
|
|
||||||
char *text = SDL_GetClipboardText();
|
char *text = SDL_GetClipboardText();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
LOGW("Could not get clipboard text: %s", SDL_GetError());
|
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);
|
char *text_dup = strdup(text);
|
||||||
SDL_free(text);
|
SDL_free(text);
|
||||||
if (!text_dup) {
|
if (!text_dup) {
|
||||||
LOGW("Could not strdup input text");
|
LOGW("Could not strdup input text");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct control_msg msg;
|
struct control_msg msg;
|
||||||
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
|
||||||
msg.set_clipboard.sequence = sequence;
|
|
||||||
msg.set_clipboard.text = text_dup;
|
msg.set_clipboard.text = text_dup;
|
||||||
msg.set_clipboard.paste = paste;
|
msg.set_clipboard.paste = paste;
|
||||||
|
|
||||||
if (!controller_push_msg(controller, &msg)) {
|
if (!controller_push_msg(controller, &msg)) {
|
||||||
free(text_dup);
|
free(text_dup);
|
||||||
LOGW("Could not request 'set device clipboard'");
|
LOGW("Could not request 'set device clipboard'");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -465,10 +462,8 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
// inject the text as input events
|
// inject the text as input events
|
||||||
clipboard_paste(controller);
|
clipboard_paste(controller);
|
||||||
} else {
|
} else {
|
||||||
// store the text in the device clipboard and paste,
|
// store the text in the device clipboard and paste
|
||||||
// without requesting an acknowledgment
|
set_device_clipboard(controller, true);
|
||||||
set_device_clipboard(controller, true,
|
|
||||||
SC_SEQUENCE_INVALID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -517,36 +512,18 @@ input_manager_process_key(struct input_manager *im,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ack_to_wait = SC_SEQUENCE_INVALID;
|
if (ctrl && !shift && keycode == SDLK_v && down && !repeat) {
|
||||||
bool is_ctrl_v = ctrl && !shift && keycode == SDLK_v && down && !repeat;
|
|
||||||
if (im->clipboard_autosync && is_ctrl_v) {
|
|
||||||
if (im->legacy_paste) {
|
if (im->legacy_paste) {
|
||||||
// inject the text as input events
|
// inject the text as input events
|
||||||
clipboard_paste(controller);
|
clipboard_paste(controller);
|
||||||
return;
|
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
|
// Synchronize the computer clipboard to the device clipboard before
|
||||||
// sending Ctrl+v, to allow seamless copy-paste.
|
// sending Ctrl+v, to allow seamless copy-paste.
|
||||||
bool ok = set_device_clipboard(controller, false, sequence);
|
set_device_clipboard(controller, false);
|
||||||
if (!ok) {
|
|
||||||
LOGW("Clipboard could not be synchronized, Ctrl+v not injected");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (im->kp->async_paste) {
|
im->kp->ops->process_key(im->kp, event);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
im->kp->ops->process_key(im->kp, event, ack_to_wait);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ struct input_manager {
|
|||||||
bool control;
|
bool control;
|
||||||
bool forward_all_clicks;
|
bool forward_all_clicks;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
bool clipboard_autosync;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned data[SC_MAX_SHORTCUT_MODS];
|
unsigned data[SC_MAX_SHORTCUT_MODS];
|
||||||
@@ -39,8 +38,6 @@ struct input_manager {
|
|||||||
unsigned key_repeat;
|
unsigned key_repeat;
|
||||||
SDL_Keycode last_keycode;
|
SDL_Keycode last_keycode;
|
||||||
uint16_t last_mod;
|
uint16_t last_mod;
|
||||||
|
|
||||||
uint64_t next_sequence; // used for request acknowledgements
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -188,13 +188,7 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
sc_key_processor_process_key(struct sc_key_processor *kp,
|
sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||||
const SDL_KeyboardEvent *event,
|
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;
|
|
||||||
|
|
||||||
struct sc_keyboard_inject *ki = DOWNCAST(kp);
|
struct sc_keyboard_inject *ki = DOWNCAST(kp);
|
||||||
|
|
||||||
if (event->repeat) {
|
if (event->repeat) {
|
||||||
@@ -256,7 +250,5 @@ sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
|||||||
.process_text = sc_key_processor_process_text,
|
.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;
|
ki->key_processor.ops = &ops;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,5 +53,4 @@ const struct scrcpy_options scrcpy_options_default = {
|
|||||||
.forward_all_clicks = false,
|
.forward_all_clicks = false,
|
||||||
.legacy_paste = false,
|
.legacy_paste = false,
|
||||||
.power_off_on_close = false,
|
.power_off_on_close = false,
|
||||||
.clipboard_autosync = true,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ struct scrcpy_options {
|
|||||||
bool forward_all_clicks;
|
bool forward_all_clicks;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
bool power_off_on_close;
|
bool power_off_on_close;
|
||||||
bool clipboard_autosync;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct scrcpy_options scrcpy_options_default;
|
extern const struct scrcpy_options scrcpy_options_default;
|
||||||
|
|||||||
@@ -7,16 +7,12 @@
|
|||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
receiver_init(struct receiver *receiver, sc_socket control_socket,
|
receiver_init(struct receiver *receiver, sc_socket control_socket) {
|
||||||
struct sc_acksync *acksync) {
|
|
||||||
bool ok = sc_mutex_init(&receiver->mutex);
|
bool ok = sc_mutex_init(&receiver->mutex);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
receiver->control_socket = control_socket;
|
receiver->control_socket = control_socket;
|
||||||
receiver->acksync = acksync;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +22,7 @@ receiver_destroy(struct receiver *receiver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_msg(struct receiver *receiver, struct device_msg *msg) {
|
process_msg(struct device_msg *msg) {
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case DEVICE_MSG_TYPE_CLIPBOARD: {
|
case DEVICE_MSG_TYPE_CLIPBOARD: {
|
||||||
char *current = SDL_GetClipboardText();
|
char *current = SDL_GetClipboardText();
|
||||||
@@ -41,17 +37,11 @@ process_msg(struct receiver *receiver, struct device_msg *msg) {
|
|||||||
SDL_SetClipboardText(msg->clipboard.text);
|
SDL_SetClipboardText(msg->clipboard.text);
|
||||||
break;
|
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
|
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;
|
size_t head = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct device_msg msg;
|
struct device_msg msg;
|
||||||
@@ -63,7 +53,7 @@ process_msgs(struct receiver *receiver, const unsigned char *buf, size_t len) {
|
|||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_msg(receiver, &msg);
|
process_msg(&msg);
|
||||||
device_msg_destroy(&msg);
|
device_msg_destroy(&msg);
|
||||||
|
|
||||||
head += r;
|
head += r;
|
||||||
@@ -91,7 +81,7 @@ run_receiver(void *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
head += r;
|
head += r;
|
||||||
ssize_t consumed = process_msgs(receiver, buf, head);
|
ssize_t consumed = process_msgs(buf, head);
|
||||||
if (consumed == -1) {
|
if (consumed == -1) {
|
||||||
// an error occurred
|
// an error occurred
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "util/acksync.h"
|
|
||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
|
||||||
@@ -15,13 +14,10 @@ struct receiver {
|
|||||||
sc_socket control_socket;
|
sc_socket control_socket;
|
||||||
sc_thread thread;
|
sc_thread thread;
|
||||||
sc_mutex mutex;
|
sc_mutex mutex;
|
||||||
|
|
||||||
struct sc_acksync *acksync;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
receiver_init(struct receiver *receiver, sc_socket control_socket,
|
receiver_init(struct receiver *receiver, sc_socket control_socket);
|
||||||
struct sc_acksync *acksync);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
receiver_destroy(struct receiver *receiver);
|
receiver_destroy(struct receiver *receiver);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
#include "util/acksync.h"
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
@@ -47,8 +46,6 @@ struct scrcpy {
|
|||||||
struct file_handler file_handler;
|
struct file_handler file_handler;
|
||||||
#ifdef HAVE_AOA_HID
|
#ifdef HAVE_AOA_HID
|
||||||
struct sc_aoa aoa;
|
struct sc_aoa aoa;
|
||||||
// sequence/ack helper to synchronize clipboard and Ctrl+v via HID
|
|
||||||
struct sc_acksync acksync;
|
|
||||||
#endif
|
#endif
|
||||||
union {
|
union {
|
||||||
struct sc_keyboard_inject keyboard_inject;
|
struct sc_keyboard_inject keyboard_inject;
|
||||||
@@ -343,8 +340,6 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
bool controller_started = false;
|
bool controller_started = false;
|
||||||
bool screen_initialized = false;
|
bool screen_initialized = false;
|
||||||
|
|
||||||
struct sc_acksync *acksync = NULL;
|
|
||||||
|
|
||||||
struct sc_server_params params = {
|
struct sc_server_params params = {
|
||||||
.serial = options->serial,
|
.serial = options->serial,
|
||||||
.log_level = options->log_level,
|
.log_level = options->log_level,
|
||||||
@@ -364,7 +359,6 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
.encoder_name = options->encoder_name,
|
.encoder_name = options->encoder_name,
|
||||||
.force_adb_forward = options->force_adb_forward,
|
.force_adb_forward = options->force_adb_forward,
|
||||||
.power_off_on_close = options->power_off_on_close,
|
.power_off_on_close = options->power_off_on_close,
|
||||||
.clipboard_autosync = options->clipboard_autosync,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sc_server_callbacks cbs = {
|
static const struct sc_server_callbacks cbs = {
|
||||||
@@ -451,18 +445,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options->control) {
|
if (options->control) {
|
||||||
#ifdef HAVE_AOA_HID
|
if (!controller_init(&s->controller, s->server.control_socket)) {
|
||||||
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)) {
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
controller_initialized = true;
|
controller_initialized = true;
|
||||||
@@ -538,7 +521,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
#ifdef HAVE_AOA_HID
|
#ifdef HAVE_AOA_HID
|
||||||
bool aoa_hid_ok = false;
|
bool aoa_hid_ok = false;
|
||||||
|
|
||||||
bool ok = sc_aoa_init(&s->aoa, serial, acksync);
|
bool ok = sc_aoa_init(&s->aoa, serial);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto aoa_hid_end;
|
goto aoa_hid_end;
|
||||||
}
|
}
|
||||||
@@ -603,9 +586,6 @@ end:
|
|||||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||||
sc_aoa_stop(&s->aoa);
|
sc_aoa_stop(&s->aoa);
|
||||||
}
|
}
|
||||||
if (acksync) {
|
|
||||||
sc_acksync_destroy(acksync);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
if (controller_started) {
|
if (controller_started) {
|
||||||
controller_stop(&s->controller);
|
controller_stop(&s->controller);
|
||||||
|
|||||||
@@ -186,7 +186,6 @@ execute_server(struct sc_server *server,
|
|||||||
params->codec_options ? params->codec_options : "-",
|
params->codec_options ? params->codec_options : "-",
|
||||||
params->encoder_name ? params->encoder_name : "-",
|
params->encoder_name ? params->encoder_name : "-",
|
||||||
params->power_off_on_close ? "true" : "false",
|
params->power_off_on_close ? "true" : "false",
|
||||||
params->clipboard_autosync ? "true" : "false",
|
|
||||||
};
|
};
|
||||||
#ifdef SERVER_DEBUGGER
|
#ifdef SERVER_DEBUGGER
|
||||||
LOGI("Server debugger waiting for a client on device port "
|
LOGI("Server debugger waiting for a client on device port "
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ struct sc_server_params {
|
|||||||
bool stay_awake;
|
bool stay_awake;
|
||||||
bool force_adb_forward;
|
bool force_adb_forward;
|
||||||
bool power_off_on_close;
|
bool power_off_on_close;
|
||||||
bool clipboard_autosync;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_server {
|
struct sc_server {
|
||||||
|
|||||||
@@ -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 "util/process.h"
|
||||||
|
|
||||||
#include <processthreadsapi.h>
|
#include <processthreadsapi.h>
|
||||||
|
|||||||
@@ -14,31 +14,12 @@
|
|||||||
* Component able to process and inject keys should implement this trait.
|
* Component able to process and inject keys should implement this trait.
|
||||||
*/
|
*/
|
||||||
struct sc_key_processor {
|
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;
|
const struct sc_key_processor_ops *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_key_processor_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
|
void
|
||||||
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event,
|
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event);
|
||||||
uint64_t ack_to_wait);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
(*process_text)(struct sc_key_processor *kp,
|
(*process_text)(struct sc_key_processor *kp,
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
// For inet_pton() on Windows
|
||||||
|
#define _WIN32_WINNT 0x0600
|
||||||
|
#define WINVER 0x0600
|
||||||
|
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ static void test_serialize_inject_touch_event(void) {
|
|||||||
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
.inject_touch_event = {
|
.inject_touch_event = {
|
||||||
.action = AMOTION_EVENT_ACTION_DOWN,
|
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||||
.pointer_id = UINT64_C(0x1234567887654321),
|
.pointer_id = 0x1234567887654321L,
|
||||||
.position = {
|
.position = {
|
||||||
.point = {
|
.point = {
|
||||||
.x = 100,
|
.x = 100,
|
||||||
@@ -226,7 +226,6 @@ static void test_serialize_set_clipboard(void) {
|
|||||||
struct control_msg msg = {
|
struct control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||||
.set_clipboard = {
|
.set_clipboard = {
|
||||||
.sequence = UINT64_C(0x0102030405060708),
|
|
||||||
.paste = true,
|
.paste = true,
|
||||||
.text = "hello, world!",
|
.text = "hello, world!",
|
||||||
},
|
},
|
||||||
@@ -234,11 +233,10 @@ static void test_serialize_set_clipboard(void) {
|
|||||||
|
|
||||||
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
unsigned char buf[CONTROL_MSG_MAX_SIZE];
|
||||||
size_t size = control_msg_serialize(&msg, buf);
|
size_t size = control_msg_serialize(&msg, buf);
|
||||||
assert(size == 27);
|
assert(size == 19);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sequence
|
|
||||||
1, // paste
|
1, // paste
|
||||||
0x00, 0x00, 0x00, 0x0d, // text length
|
0x00, 0x00, 0x00, 0x0d, // text length
|
||||||
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
|
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
|
||||||
|
|||||||
@@ -47,26 +47,11 @@ static void test_deserialize_clipboard_big(void) {
|
|||||||
device_msg_destroy(&msg);
|
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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
|
|
||||||
test_deserialize_clipboard();
|
test_deserialize_clipboard();
|
||||||
test_deserialize_clipboard_big();
|
test_deserialize_clipboard_big();
|
||||||
test_deserialize_ack_set_clipboard();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ public final class ControlMessage {
|
|||||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
||||||
public static final int TYPE_ROTATE_DEVICE = 11;
|
public static final int TYPE_ROTATE_DEVICE = 11;
|
||||||
|
|
||||||
public static final long SEQUENCE_INVALID = 0;
|
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String text;
|
private String text;
|
||||||
private int metaState; // KeyEvent.META_*
|
private int metaState; // KeyEvent.META_*
|
||||||
@@ -33,7 +31,6 @@ public final class ControlMessage {
|
|||||||
private int vScroll;
|
private int vScroll;
|
||||||
private boolean paste;
|
private boolean paste;
|
||||||
private int repeat;
|
private int repeat;
|
||||||
private long sequence;
|
|
||||||
|
|
||||||
private ControlMessage() {
|
private ControlMessage() {
|
||||||
}
|
}
|
||||||
@@ -82,10 +79,9 @@ public final class ControlMessage {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ControlMessage createSetClipboard(long sequence, String text, boolean paste) {
|
public static ControlMessage createSetClipboard(String text, boolean paste) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage msg = new ControlMessage();
|
||||||
msg.type = TYPE_SET_CLIPBOARD;
|
msg.type = TYPE_SET_CLIPBOARD;
|
||||||
msg.sequence = sequence;
|
|
||||||
msg.text = text;
|
msg.text = text;
|
||||||
msg.paste = paste;
|
msg.paste = paste;
|
||||||
return msg;
|
return msg;
|
||||||
@@ -158,8 +154,4 @@ public final class ControlMessage {
|
|||||||
public int getRepeat() {
|
public int getRepeat() {
|
||||||
return repeat;
|
return repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSequence() {
|
|
||||||
return sequence;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ public class ControlMessageReader {
|
|||||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||||
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
|
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
|
||||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_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
|
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;
|
public static final int INJECT_TEXT_MAX_LENGTH = 300;
|
||||||
|
|
||||||
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
|
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
|
||||||
@@ -166,13 +166,12 @@ public class ControlMessageReader {
|
|||||||
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
|
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
long sequence = buffer.getLong();
|
|
||||||
boolean paste = buffer.get() != 0;
|
boolean paste = buffer.get() != 0;
|
||||||
String text = parseString();
|
String text = parseString();
|
||||||
if (text == null) {
|
if (text == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return ControlMessage.createSetClipboard(sequence, text, paste);
|
return ControlMessage.createSetClipboard(text, paste);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlMessage parseSetScreenPowerMode() {
|
private ControlMessage parseSetScreenPowerMode() {
|
||||||
|
|||||||
@@ -120,12 +120,7 @@ public class Controller {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||||
long sequence = msg.getSequence();
|
|
||||||
setClipboard(msg.getText(), msg.getPaste());
|
setClipboard(msg.getText(), msg.getPaste());
|
||||||
if (sequence != ControlMessage.SEQUENCE_INVALID) {
|
|
||||||
// Acknowledgement requested
|
|
||||||
sender.pushAckClipboard(sequence);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
if (device.supportsInputEvents()) {
|
if (device.supportsInputEvents()) {
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ public final class Device {
|
|||||||
}
|
}
|
||||||
}, displayId);
|
}, displayId);
|
||||||
|
|
||||||
if (options.getControl() && options.getClipboardAutosync()) {
|
if (options.getControl()) {
|
||||||
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
|
// If control is enabled, synchronize Android clipboard to the computer automatically
|
||||||
ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager();
|
ClipboardManager clipboardManager = SERVICE_MANAGER.getClipboardManager();
|
||||||
if (clipboardManager != null) {
|
if (clipboardManager != null) {
|
||||||
clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {
|
clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {
|
||||||
|
|||||||
@@ -3,13 +3,9 @@ package com.genymobile.scrcpy;
|
|||||||
public final class DeviceMessage {
|
public final class DeviceMessage {
|
||||||
|
|
||||||
public static final int TYPE_CLIPBOARD = 0;
|
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 int type;
|
||||||
private String text;
|
private String text;
|
||||||
private long sequence;
|
|
||||||
|
|
||||||
private DeviceMessage() {
|
private DeviceMessage() {
|
||||||
}
|
}
|
||||||
@@ -21,13 +17,6 @@ public final class DeviceMessage {
|
|||||||
return event;
|
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() {
|
public int getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -35,8 +24,4 @@ public final class DeviceMessage {
|
|||||||
public String getText() {
|
public String getText() {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSequence() {
|
|
||||||
return sequence;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ public final class DeviceMessageSender {
|
|||||||
|
|
||||||
private String clipboardText;
|
private String clipboardText;
|
||||||
|
|
||||||
private long ack;
|
|
||||||
|
|
||||||
public DeviceMessageSender(DesktopConnection connection) {
|
public DeviceMessageSender(DesktopConnection connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
@@ -19,34 +17,18 @@ public final class DeviceMessageSender {
|
|||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void pushAckClipboard(long sequence) {
|
|
||||||
ack = sequence;
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loop() throws IOException, InterruptedException {
|
public void loop() throws IOException, InterruptedException {
|
||||||
while (true) {
|
while (true) {
|
||||||
String text;
|
String text;
|
||||||
long sequence;
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
while (ack == DeviceMessage.SEQUENCE_INVALID && clipboardText == null) {
|
while (clipboardText == null) {
|
||||||
wait();
|
wait();
|
||||||
}
|
}
|
||||||
text = clipboardText;
|
text = clipboardText;
|
||||||
clipboardText = null;
|
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);
|
DeviceMessage event = DeviceMessage.createClipboard(text);
|
||||||
connection.sendDeviceMessage(event);
|
connection.sendDeviceMessage(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class DeviceMessageWriter {
|
|||||||
|
|
||||||
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
|
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
buffer.put((byte) msg.getType());
|
buffer.put((byte) DeviceMessage.TYPE_CLIPBOARD);
|
||||||
switch (msg.getType()) {
|
switch (msg.getType()) {
|
||||||
case DeviceMessage.TYPE_CLIPBOARD:
|
case DeviceMessage.TYPE_CLIPBOARD:
|
||||||
String text = msg.getText();
|
String text = msg.getText();
|
||||||
@@ -25,10 +25,6 @@ public class DeviceMessageWriter {
|
|||||||
buffer.put(raw, 0, len);
|
buffer.put(raw, 0, len);
|
||||||
output.write(rawBuffer, 0, buffer.position());
|
output.write(rawBuffer, 0, buffer.position());
|
||||||
break;
|
break;
|
||||||
case DeviceMessage.TYPE_ACK_CLIPBOARD:
|
|
||||||
buffer.putLong(msg.getSequence());
|
|
||||||
output.write(rawBuffer, 0, buffer.position());
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
Ln.w("Unknown device message: " + msg.getType());
|
Ln.w("Unknown device message: " + msg.getType());
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public class Options {
|
|||||||
private String codecOptions;
|
private String codecOptions;
|
||||||
private String encoderName;
|
private String encoderName;
|
||||||
private boolean powerOffScreenOnClose;
|
private boolean powerOffScreenOnClose;
|
||||||
private boolean clipboardAutosync;
|
|
||||||
|
|
||||||
public Ln.Level getLogLevel() {
|
public Ln.Level getLogLevel() {
|
||||||
return logLevel;
|
return logLevel;
|
||||||
@@ -139,12 +138,4 @@ public class Options {
|
|||||||
public boolean getPowerOffScreenOnClose() {
|
public boolean getPowerOffScreenOnClose() {
|
||||||
return this.powerOffScreenOnClose;
|
return this.powerOffScreenOnClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getClipboardAutosync() {
|
|
||||||
return clipboardAutosync;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClipboardAutosync(boolean clipboardAutosync) {
|
|
||||||
this.clipboardAutosync = clipboardAutosync;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ public final class Server {
|
|||||||
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
final int expectedParameters = 17;
|
final int expectedParameters = 16;
|
||||||
if (args.length != expectedParameters) {
|
if (args.length != expectedParameters) {
|
||||||
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
|
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
|
||||||
}
|
}
|
||||||
@@ -213,9 +213,6 @@ public final class Server {
|
|||||||
boolean powerOffScreenOnClose = Boolean.parseBoolean(args[15]);
|
boolean powerOffScreenOnClose = Boolean.parseBoolean(args[15]);
|
||||||
options.setPowerOffScreenOnClose(powerOffScreenOnClose);
|
options.setPowerOffScreenOnClose(powerOffScreenOnClose);
|
||||||
|
|
||||||
boolean clipboardAutosync = Boolean.parseBoolean(args[16]);
|
|
||||||
options.setClipboardAutosync(clipboardAutosync);
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,7 +235,6 @@ public class ControlMessageReaderTest {
|
|||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
|
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
|
||||||
dos.writeLong(0x0102030405060708L); // sequence
|
|
||||||
dos.writeByte(1); // paste
|
dos.writeByte(1); // paste
|
||||||
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
|
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
|
||||||
dos.writeInt(text.length);
|
dos.writeInt(text.length);
|
||||||
@@ -247,7 +246,6 @@ public class ControlMessageReaderTest {
|
|||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||||
Assert.assertEquals(0x0102030405060708L, event.getSequence());
|
|
||||||
Assert.assertEquals("testé", event.getText());
|
Assert.assertEquals("testé", event.getText());
|
||||||
Assert.assertTrue(event.getPaste());
|
Assert.assertTrue(event.getPaste());
|
||||||
}
|
}
|
||||||
@@ -261,7 +259,6 @@ public class ControlMessageReaderTest {
|
|||||||
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
|
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
|
||||||
|
|
||||||
byte[] rawText = new byte[ControlMessageReader.CLIPBOARD_TEXT_MAX_LENGTH];
|
byte[] rawText = new byte[ControlMessageReader.CLIPBOARD_TEXT_MAX_LENGTH];
|
||||||
dos.writeLong(0x0807060504030201L); // sequence
|
|
||||||
dos.writeByte(1); // paste
|
dos.writeByte(1); // paste
|
||||||
Arrays.fill(rawText, (byte) 'a');
|
Arrays.fill(rawText, (byte) 'a');
|
||||||
String text = new String(rawText, 0, rawText.length);
|
String text = new String(rawText, 0, rawText.length);
|
||||||
@@ -275,7 +272,6 @@ public class ControlMessageReaderTest {
|
|||||||
ControlMessage event = reader.next();
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||||
Assert.assertEquals(0x0807060504030201L, event.getSequence());
|
|
||||||
Assert.assertEquals(text, event.getText());
|
Assert.assertEquals(text, event.getText());
|
||||||
Assert.assertTrue(event.getPaste());
|
Assert.assertTrue(event.getPaste());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,24 +32,4 @@ public class DeviceMessageWriterTest {
|
|||||||
|
|
||||||
Assert.assertArrayEquals(expected, actual);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user