Always synchronize clipboard on explicit COPY/CUT

If --no-clipboard-autosync is enabled, the automatic clipboard
synchronization performed whenever the device clipboard changes is
disabled.

But on explicit COPY and CUT scrcpy shortcuts (MOD+c and MOD+x), the
clipboard should still be synchronized, so that it remains possible to
copy-paste from the device to the computer.

This is consistent with the behavior of MOD+v, which pastes the computer
clipboard to the device.

Refs #2228 <https://github.com/Genymobile/scrcpy/issues/2228>
Refs #2817 <https://github.com/Genymobile/scrcpy/pull/2817>
PR #2834 <https://github.com/Genymobile/scrcpy/pull/2834>
This commit is contained in:
Romain Vimont 2021-11-29 09:30:57 +01:00
commit e2b3968c66
9 changed files with 102 additions and 26 deletions

View file

@ -20,6 +20,10 @@ public final class ControlMessage {
public static final long SEQUENCE_INVALID = 0;
public static final int COPY_KEY_NONE = 0;
public static final int COPY_KEY_COPY = 1;
public static final int COPY_KEY_CUT = 2;
private int type;
private String text;
private int metaState; // KeyEvent.META_*
@ -31,6 +35,7 @@ public final class ControlMessage {
private Position position;
private int hScroll;
private int vScroll;
private int copyKey;
private boolean paste;
private int repeat;
private long sequence;
@ -82,6 +87,13 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createGetClipboard(int copyKey) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_GET_CLIPBOARD;
msg.copyKey = copyKey;
return msg;
}
public static ControlMessage createSetClipboard(long sequence, String text, boolean paste) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_SET_CLIPBOARD;
@ -151,6 +163,10 @@ public final class ControlMessage {
return vScroll;
}
public int getCopyKey() {
return copyKey;
}
public boolean getPaste() {
return paste;
}

View file

@ -13,6 +13,7 @@ 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 GET_CLIPBOARD_LENGTH = 1;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 9;
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
@ -70,6 +71,9 @@ public class ControlMessageReader {
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
msg = parseBackOrScreenOnEvent();
break;
case ControlMessage.TYPE_GET_CLIPBOARD:
msg = parseGetClipboard();
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
msg = parseSetClipboard();
break;
@ -79,7 +83,6 @@ public class ControlMessageReader {
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
case ControlMessage.TYPE_EXPAND_SETTINGS_PANEL:
case ControlMessage.TYPE_COLLAPSE_PANELS:
case ControlMessage.TYPE_GET_CLIPBOARD:
case ControlMessage.TYPE_ROTATE_DEVICE:
msg = ControlMessage.createEmpty(type);
break;
@ -162,6 +165,14 @@ public class ControlMessageReader {
return ControlMessage.createBackOrScreenOn(action);
}
private ControlMessage parseGetClipboard() {
if (buffer.remaining() < GET_CLIPBOARD_LENGTH) {
return null;
}
int copyKey = toUnsigned(buffer.get());
return ControlMessage.createGetClipboard(copyKey);
}
private ControlMessage parseSetClipboard() {
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
return null;

View file

@ -21,6 +21,7 @@ public class Controller {
private final Device device;
private final DesktopConnection connection;
private final DeviceMessageSender sender;
private final boolean clipboardAutosync;
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
@ -31,9 +32,10 @@ public class Controller {
private boolean keepPowerModeOff;
public Controller(Device device, DesktopConnection connection) {
public Controller(Device device, DesktopConnection connection, boolean clipboardAutosync) {
this.device = device;
this.connection = connection;
this.clipboardAutosync = clipboardAutosync;
initPointers();
sender = new DeviceMessageSender(connection);
}
@ -114,10 +116,7 @@ public class Controller {
Device.collapsePanels();
break;
case ControlMessage.TYPE_GET_CLIPBOARD:
String clipboardText = Device.getClipboardText();
if (clipboardText != null) {
sender.pushClipboardText(clipboardText);
}
getClipboard(msg.getCopyKey());
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
setClipboard(msg.getText(), msg.getPaste(), msg.getSequence());
@ -276,6 +275,25 @@ public class Controller {
return device.pressReleaseKeycode(KeyEvent.KEYCODE_POWER, Device.INJECT_MODE_ASYNC);
}
private void getClipboard(int copyKey) {
// On Android >= 7, press the COPY or CUT key if requested
if (copyKey != ControlMessage.COPY_KEY_NONE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
int key = copyKey == ControlMessage.COPY_KEY_COPY ? KeyEvent.KEYCODE_COPY : KeyEvent.KEYCODE_CUT;
// Wait until the event is finished, to ensure that the clipboard text we read just after is the correct one
device.pressReleaseKeycode(key, Device.INJECT_MODE_WAIT_FOR_FINISH);
}
// If clipboard autosync is enabled, then the device clipboard is synchronized to the computer clipboard whenever it changes, in
// particular when COPY or CUT are injected, so it should not be synchronized twice. On Android < 7, do not synchronize at all rather than
// copying an old clipboard content.
if (!clipboardAutosync) {
String clipboardText = Device.getClipboardText();
if (clipboardText != null) {
sender.pushClipboardText(clipboardText);
}
}
}
private boolean setClipboard(String text, boolean paste, long sequence) {
boolean ok = device.setClipboardText(text);
if (ok) {

View file

@ -74,7 +74,7 @@ public final class Server {
Thread controllerThread = null;
Thread deviceMessageSenderThread = null;
if (options.getControl()) {
final Controller controller = new Controller(device, connection);
final Controller controller = new Controller(device, connection, options.getClipboardAutosync());
// asynchronous
controllerThread = startController(controller);

View file

@ -219,6 +219,7 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_GET_CLIPBOARD);
dos.writeByte(ControlMessage.COPY_KEY_COPY);
byte[] packet = bos.toByteArray();
@ -226,6 +227,7 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_GET_CLIPBOARD, event.getType());
Assert.assertEquals(ControlMessage.COPY_KEY_COPY, event.getCopyKey());
}
@Test