mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-20 03:25:03 +00:00
Press COPY on "get clipboard" request if possible
If the device runs at least Android 7, just press COPY on the device (the clipboard content will be sent via the clipboard listener). <https://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_COPY>
This commit is contained in:
parent
31cee2c49f
commit
9badd2bdf0
11 changed files with 65 additions and 19 deletions
|
@ -582,7 +582,7 @@ _`Meta` is typically the `Windows` key on the keyboard, or `Cmd` on macOS._
|
|||
| Rotate device screen | `Meta`+`r`
|
||||
| Expand notification panel | `Meta`+`n`
|
||||
| Collapse notification panel | `Meta`+`Shift`+`n`
|
||||
| Copy device clipboard to computer | `Meta`+`c`
|
||||
| Press COPY³, then Copy device clipboard to computer | `Meta`+`c`
|
||||
| Paste computer clipboard to device | `Meta`+`v`
|
||||
| Copy computer clipboard to device, then press PASTE³ | `Meta`+`Shift`+`v`
|
||||
| Enable/disable FPS counter (on stdout) | `Meta`+`i`
|
||||
|
|
|
@ -277,7 +277,7 @@ Collapse notification panel
|
|||
|
||||
.TP
|
||||
.B Meta+c
|
||||
Copy device clipboard to computer
|
||||
Press COPY (Android >= 7), then copy device clipboard to computer
|
||||
|
||||
.TP
|
||||
.B Meta+v
|
||||
|
|
|
@ -245,7 +245,8 @@ scrcpy_print_usage(const char *arg0) {
|
|||
" Collapse notification panel\n"
|
||||
"\n"
|
||||
" " MOD "+c\n"
|
||||
" Copy device clipboard to computer\n"
|
||||
" Press COPY (Android >= 7), then copy device clipboard to\n"
|
||||
" computer\n"
|
||||
"\n"
|
||||
" " MOD "+v\n"
|
||||
" Paste computer clipboard to device\n"
|
||||
|
|
|
@ -66,6 +66,9 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||
buffer_write32be(&buf[17],
|
||||
(uint32_t) msg->inject_scroll_event.vscroll);
|
||||
return 21;
|
||||
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||
buf[1] = msg->get_clipboard.copy;
|
||||
return 2;
|
||||
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
|
||||
buf[1] = !!msg->set_clipboard.paste;
|
||||
size_t len = write_string(msg->set_clipboard.text,
|
||||
|
@ -79,7 +82,6 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
|||
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
||||
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
||||
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||
// no additional data
|
||||
return 1;
|
||||
|
|
|
@ -60,6 +60,9 @@ struct control_msg {
|
|||
int32_t hscroll;
|
||||
int32_t vscroll;
|
||||
} inject_scroll_event;
|
||||
struct {
|
||||
bool copy;
|
||||
} get_clipboard;
|
||||
struct {
|
||||
char *text; // owned, to be freed by SDL_free()
|
||||
bool paste;
|
||||
|
|
|
@ -102,9 +102,10 @@ collapse_notification_panel(struct controller *controller) {
|
|||
}
|
||||
|
||||
static void
|
||||
request_device_clipboard(struct controller *controller) {
|
||||
request_device_clipboard(struct controller *controller, bool copy) {
|
||||
struct control_msg msg;
|
||||
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
|
||||
msg.get_clipboard.copy = copy;
|
||||
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request device clipboard");
|
||||
|
@ -341,7 +342,8 @@ input_manager_process_key(struct input_manager *im,
|
|||
return;
|
||||
case SDLK_c:
|
||||
if (control && !shift && !repeat && down) {
|
||||
request_device_clipboard(controller);
|
||||
// Press COPY, then get the clipboard content
|
||||
request_device_clipboard(controller, true);
|
||||
}
|
||||
return;
|
||||
case SDLK_v:
|
||||
|
|
|
@ -185,14 +185,18 @@ static void test_serialize_collapse_notification_panel(void) {
|
|||
static void test_serialize_get_clipboard(void) {
|
||||
struct control_msg msg = {
|
||||
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
.get_clipboard = {
|
||||
.copy = true,
|
||||
},
|
||||
};
|
||||
|
||||
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||
int size = control_msg_serialize(&msg, buf);
|
||||
assert(size == 1);
|
||||
assert(size == 2);
|
||||
|
||||
const unsigned char expected[] = {
|
||||
CONTROL_MSG_TYPE_GET_CLIPBOARD,
|
||||
1, // copy
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public final class ControlMessage {
|
|||
private Position position;
|
||||
private int hScroll;
|
||||
private int vScroll;
|
||||
private boolean paste;
|
||||
private boolean pressCopyOrPaste;
|
||||
|
||||
private ControlMessage() {
|
||||
}
|
||||
|
@ -69,11 +69,18 @@ public final class ControlMessage {
|
|||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createGetClipboard(boolean copy) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_GET_CLIPBOARD;
|
||||
msg.pressCopyOrPaste = copy;
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createSetClipboard(String text, boolean paste) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_SET_CLIPBOARD;
|
||||
msg.text = text;
|
||||
msg.paste = paste;
|
||||
msg.pressCopyOrPaste = paste;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -137,7 +144,7 @@ public final class ControlMessage {
|
|||
return vScroll;
|
||||
}
|
||||
|
||||
public boolean getPaste() {
|
||||
return paste;
|
||||
public boolean getPressCopyOrPaste() {
|
||||
return pressCopyOrPaste;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public class ControlMessageReader {
|
|||
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
|
||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||
static final int GET_CLIPBOARD_PAYLOAD_LENGTH = 1;
|
||||
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
|
||||
|
||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4092; // 4096 - 1 (type) - 1 (parse flag) - 2 (length)
|
||||
|
@ -67,6 +68,9 @@ public class ControlMessageReader {
|
|||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||
msg = parseInjectScrollEvent();
|
||||
break;
|
||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||
msg = parseGetClipboard();
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||
msg = parseSetClipboard();
|
||||
break;
|
||||
|
@ -76,7 +80,6 @@ public class ControlMessageReader {
|
|||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||
msg = ControlMessage.createEmpty(type);
|
||||
break;
|
||||
|
@ -148,6 +151,14 @@ public class ControlMessageReader {
|
|||
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
|
||||
}
|
||||
|
||||
private ControlMessage parseGetClipboard() {
|
||||
if (buffer.remaining() < GET_CLIPBOARD_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
boolean copy = buffer.get() != 0;
|
||||
return ControlMessage.createGetClipboard(copy);
|
||||
}
|
||||
|
||||
private ControlMessage parseSetClipboard() {
|
||||
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
|
|
|
@ -104,13 +104,10 @@ public class Controller {
|
|||
device.collapsePanels();
|
||||
break;
|
||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||
String clipboardText = device.getClipboardText();
|
||||
if (clipboardText != null) {
|
||||
sender.pushClipboardText(clipboardText);
|
||||
}
|
||||
getClipboard(msg.getPressCopyOrPaste());
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||
setClipboard(msg.getText(), msg.getPaste());
|
||||
setClipboard(msg.getText(), msg.getPressCopyOrPaste());
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||
if (device.supportsInputEvents()) {
|
||||
|
@ -228,6 +225,23 @@ public class Controller {
|
|||
return device.injectKeycode(keycode);
|
||||
}
|
||||
|
||||
private boolean getClipboard(boolean copy) {
|
||||
// On Android >= 7, also press the COPY key if requested
|
||||
if (copy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
|
||||
// If there is something to copy, the clipboard will be automatically sent to the computer clipboard via the ClipboardListener
|
||||
return device.injectKeycode(KeyEvent.KEYCODE_COPY);
|
||||
}
|
||||
|
||||
// We can't press COPY, so only synchronize the current clipboard
|
||||
String clipboardText = device.getClipboardText();
|
||||
if (clipboardText != null) {
|
||||
sender.pushClipboardText(clipboardText);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean setClipboard(String text, boolean paste) {
|
||||
boolean ok = device.setClipboardText(text);
|
||||
if (ok) {
|
||||
|
|
|
@ -200,6 +200,7 @@ public class ControlMessageReaderTest {
|
|||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_GET_CLIPBOARD);
|
||||
dos.writeByte(1); // copy
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
|
@ -207,6 +208,7 @@ public class ControlMessageReaderTest {
|
|||
ControlMessage event = reader.next();
|
||||
|
||||
Assert.assertEquals(ControlMessage.TYPE_GET_CLIPBOARD, event.getType());
|
||||
Assert.assertTrue(event.getPressCopyOrPaste());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -228,7 +230,7 @@ public class ControlMessageReaderTest {
|
|||
|
||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||
Assert.assertEquals("testé", event.getText());
|
||||
Assert.assertTrue(event.getPaste());
|
||||
Assert.assertTrue(event.getPressCopyOrPaste());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -254,7 +256,7 @@ public class ControlMessageReaderTest {
|
|||
|
||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||
Assert.assertEquals(text, event.getText());
|
||||
Assert.assertTrue(event.getPaste());
|
||||
Assert.assertTrue(event.getPressCopyOrPaste());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Reference in a new issue