diff --git a/app/src/control_msg.c b/app/src/control_msg.c index daa3bde7..b9d50222 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -155,6 +155,9 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) { sc_write16be(&buf[3], msg->uhid_input.size); memcpy(&buf[5], msg->uhid_input.data, msg->uhid_input.size); return 5 + msg->uhid_input.size; + case SC_CONTROL_MSG_TYPE_UHID_DESTROY: + sc_write16be(&buf[1], msg->uhid_destroy.id); + return 3; case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL: case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL: case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS: @@ -269,6 +272,9 @@ sc_control_msg_log(const struct sc_control_msg *msg) { } break; } + case SC_CONTROL_MSG_TYPE_UHID_DESTROY: + LOG_CMSG("UHID destroy [%" PRIu16 "]", msg->uhid_destroy.id); + break; case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS: LOG_CMSG("open hard keyboard settings"); break; @@ -281,8 +287,11 @@ sc_control_msg_log(const struct sc_control_msg *msg) { bool sc_control_msg_is_droppable(const struct sc_control_msg *msg) { // Cannot drop UHID_CREATE messages, because it would cause all further - // UHID_INPUT messages for this device to be invalid - return msg->type != SC_CONTROL_MSG_TYPE_UHID_CREATE; + // UHID_INPUT messages for this device to be invalid. + // Cannot drop UHID_DESTROY messages either, because a further UHID_CREATE + // with the same id may fail. + return msg->type != SC_CONTROL_MSG_TYPE_UHID_CREATE + && msg->type != SC_CONTROL_MSG_TYPE_UHID_DESTROY; } void diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 63670705..b48d91af 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -39,6 +39,7 @@ enum sc_control_msg_type { SC_CONTROL_MSG_TYPE_ROTATE_DEVICE, SC_CONTROL_MSG_TYPE_UHID_CREATE, SC_CONTROL_MSG_TYPE_UHID_INPUT, + SC_CONTROL_MSG_TYPE_UHID_DESTROY, SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS, }; @@ -105,6 +106,9 @@ struct sc_control_msg { uint16_t size; uint8_t data[SC_HID_MAX_SIZE]; } uhid_input; + struct { + uint16_t id; + } uhid_destroy; }; }; diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 7a978f2b..f88048d8 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -370,6 +370,25 @@ static void test_serialize_uhid_input(void) { assert(!memcmp(buf, expected, sizeof(expected))); } +static void test_serialize_uhid_destroy(void) { + struct sc_control_msg msg = { + .type = SC_CONTROL_MSG_TYPE_UHID_DESTROY, + .uhid_destroy = { + .id = 42, + }, + }; + + uint8_t buf[SC_CONTROL_MSG_MAX_SIZE]; + size_t size = sc_control_msg_serialize(&msg, buf); + assert(size == 3); + + const uint8_t expected[] = { + SC_CONTROL_MSG_TYPE_UHID_DESTROY, + 0, 42, // id + }; + assert(!memcmp(buf, expected, sizeof(expected))); +} + static void test_serialize_open_hard_keyboard(void) { struct sc_control_msg msg = { .type = SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS, @@ -405,6 +424,7 @@ int main(int argc, char *argv[]) { test_serialize_rotate_device(); test_serialize_uhid_create(); test_serialize_uhid_input(); + test_serialize_uhid_destroy(); test_serialize_open_hard_keyboard(); return 0; } diff --git a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java index c414f2a5..ef71353a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java @@ -21,7 +21,8 @@ public final class ControlMessage { public static final int TYPE_ROTATE_DEVICE = 11; public static final int TYPE_UHID_CREATE = 12; public static final int TYPE_UHID_INPUT = 13; - public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 14; + public static final int TYPE_UHID_DESTROY = 14; + public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15; public static final long SEQUENCE_INVALID = 0; @@ -146,6 +147,13 @@ public final class ControlMessage { return msg; } + public static ControlMessage createUhidDestroy(int id) { + ControlMessage msg = new ControlMessage(); + msg.type = TYPE_UHID_DESTROY; + msg.id = id; + return msg; + } + public int getType() { return type; } diff --git a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java index f2e89da2..ef7877f1 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java @@ -51,6 +51,8 @@ public class ControlMessageReader { return parseUhidCreate(); case ControlMessage.TYPE_UHID_INPUT: return parseUhidInput(); + case ControlMessage.TYPE_UHID_DESTROY: + return parseUhidDestroy(); default: throw new ControlProtocolException("Unknown event type: " + type); } @@ -142,6 +144,11 @@ public class ControlMessageReader { return ControlMessage.createUhidInput(id, data); } + private ControlMessage parseUhidDestroy() throws IOException { + int id = dis.readUnsignedShort(); + return ControlMessage.createUhidDestroy(id); + } + private Position parsePosition() throws IOException { int x = dis.readInt(); int y = dis.readInt(); diff --git a/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java index ae18154d..0fd5f0ac 100644 --- a/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java @@ -362,6 +362,24 @@ public class ControlMessageReaderTest { Assert.assertEquals(-1, bis.read()); // EOS } + @Test + public void testParseUhidDestroy() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + dos.writeByte(ControlMessage.TYPE_UHID_DESTROY); + dos.writeShort(42); // id + byte[] packet = bos.toByteArray(); + + ByteArrayInputStream bis = new ByteArrayInputStream(packet); + ControlMessageReader reader = new ControlMessageReader(bis); + + ControlMessage event = reader.read(); + Assert.assertEquals(ControlMessage.TYPE_UHID_DESTROY, event.getType()); + Assert.assertEquals(42, event.getId()); + + Assert.assertEquals(-1, bis.read()); // EOS + } + @Test public void testParseOpenHardKeyboardSettings() throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream();