diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 74e3315c..83ab0b7b 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -118,11 +118,12 @@ 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: { - buf[1] = !!msg->set_clipboard.paste; + buffer_write64be(&buf[1], msg->set_clipboard.sequence); + buf[9] = !!msg->set_clipboard.paste; size_t len = write_string(msg->set_clipboard.text, CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH, - &buf[2]); - return 2 + len; + &buf[10]); + return 10 + len; } case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE: buf[1] = msg->set_screen_power_mode.mode; @@ -199,7 +200,8 @@ 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 %s \"%s\"", + LOG_CMSG("clipboard %" PRIu64_ " %s \"%s\"", + msg->set_clipboard.sequence, msg->set_clipboard.paste ? "paste" : "copy", msg->set_clipboard.text); break; diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 16492849..7352defe 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -70,6 +70,7 @@ 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; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 16c82234..e7afee77 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -225,6 +225,7 @@ set_device_clipboard(struct controller *controller, bool paste) { struct control_msg msg; msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD; + msg.set_clipboard.sequence = 0; // unused for now msg.set_clipboard.text = text_dup; msg.set_clipboard.paste = paste; diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index b237e28e..5cd7056b 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -226,6 +226,7 @@ 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!", }, @@ -233,10 +234,11 @@ static void test_serialize_set_clipboard(void) { unsigned char buf[CONTROL_MSG_MAX_SIZE]; size_t size = control_msg_serialize(&msg, buf); - assert(size == 19); + assert(size == 27); 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 diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java index f8edd53c..a536ed91 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java @@ -31,6 +31,7 @@ public final class ControlMessage { private int vScroll; private boolean paste; private int repeat; + private long sequence; private ControlMessage() { } @@ -79,9 +80,10 @@ public final class ControlMessage { return msg; } - public static ControlMessage createSetClipboard(String text, boolean paste) { + public static ControlMessage createSetClipboard(long sequence, String text, boolean paste) { ControlMessage msg = new ControlMessage(); msg.type = TYPE_SET_CLIPBOARD; + msg.sequence = sequence; msg.text = text; msg.paste = paste; return msg; @@ -154,4 +156,8 @@ public final class ControlMessage { public int getRepeat() { return repeat; } + + public long getSequence() { + return sequence; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java index e4ab8402..80931e94 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java @@ -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 = 1; + static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 9; private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k - 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 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 INJECT_TEXT_MAX_LENGTH = 300; private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE]; @@ -166,12 +166,13 @@ 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(text, paste); + return ControlMessage.createSetClipboard(sequence, text, paste); } private ControlMessage parseSetScreenPowerMode() { diff --git a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java index 7f3d3f61..3b0b6c0d 100644 --- a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java @@ -235,6 +235,7 @@ 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); @@ -246,6 +247,7 @@ 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()); } @@ -259,6 +261,7 @@ 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); @@ -272,6 +275,7 @@ 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()); }