From ac334496a1d9bf777cca0c063265d013e219c505 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 10 Oct 2024 09:24:41 +0200 Subject: [PATCH] Use real gamepad USB ids for UHID Create the UHID device using the same values as the physical gamepad: - vendorId - productId - productVersion --- app/src/control_msg.c | 16 +++++++++++---- app/src/control_msg.h | 3 +++ app/src/hid/hid_event.h | 3 +++ app/src/hid/hid_gamepad.c | 8 +++++++- app/src/hid/hid_gamepad.h | 5 ++++- app/src/hid/hid_keyboard.c | 3 +++ app/src/hid/hid_mouse.c | 3 +++ app/src/input_events.h | 3 +++ app/src/input_manager.c | 11 ++++++++++ app/src/uhid/gamepad_uhid.c | 8 +++++++- app/src/uhid/keyboard_uhid.c | 3 +++ app/src/uhid/mouse_uhid.c | 3 +++ app/src/usb/gamepad_aoa.c | 5 ++++- app/tests/test_control_msg_serialize.c | 8 +++++++- .../scrcpy/control/ControlMessage.java | 20 ++++++++++++++++++- .../scrcpy/control/ControlMessageReader.java | 5 ++++- .../genymobile/scrcpy/control/Controller.java | 2 +- .../scrcpy/control/UhidManager.java | 12 +++++------ .../control/ControlMessageReaderTest.java | 6 ++++++ 19 files changed, 109 insertions(+), 18 deletions(-) diff --git a/app/src/control_msg.c b/app/src/control_msg.c index d599b62d..b9376f00 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -163,8 +163,11 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) { return 2; case SC_CONTROL_MSG_TYPE_UHID_CREATE: sc_write16be(&buf[1], msg->uhid_create.id); + sc_write16be(&buf[3], msg->uhid_create.vendor_id); + sc_write16be(&buf[5], msg->uhid_create.product_id); + sc_write16be(&buf[7], msg->uhid_create.product_version); - size_t index = 3; + size_t index = 9; index += write_string_tiny(&buf[index], msg->uhid_create.name, 127); sc_write16be(&buf[index], msg->uhid_create.report_desc_size); @@ -284,9 +287,14 @@ sc_control_msg_log(const struct sc_control_msg *msg) { // Quote only if name is not null const char *name = msg->uhid_create.name; const char *quote = name ? "\"" : ""; - LOG_CMSG("UHID create [%" PRIu16 "] name=%s%s%s " - "report_desc_size=%" PRIu16, msg->uhid_create.id, - quote, name, quote, msg->uhid_create.report_desc_size); + LOG_CMSG("UHID create [%" PRIu16 "] %04" PRIx16 ":%04" PRIx16 + " (%" PRIu16 ") name=%s%s%s report_desc_size=%" PRIu16, + msg->uhid_create.id, + msg->uhid_create.vendor_id, + msg->uhid_create.product_id, + msg->uhid_create.product_version, + quote, name, quote, + msg->uhid_create.report_desc_size); break; } case SC_CONTROL_MSG_TYPE_UHID_INPUT: { diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 1ae8cae4..42057ae1 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -98,6 +98,9 @@ struct sc_control_msg { } set_screen_power_mode; struct { uint16_t id; + uint16_t vendor_id; + uint16_t product_id; + uint16_t product_version; const char *name; // pointer to static data uint16_t report_desc_size; const uint8_t *report_desc; // pointer to static data diff --git a/app/src/hid/hid_event.h b/app/src/hid/hid_event.h index 37c3611b..a9fc07ad 100644 --- a/app/src/hid/hid_event.h +++ b/app/src/hid/hid_event.h @@ -15,6 +15,9 @@ struct sc_hid_input { struct sc_hid_open { uint16_t hid_id; + uint16_t vendor_id; + uint16_t product_id; + uint16_t product_version; const char *name; // pointer to static memory const uint8_t *report_desc; // pointer to static memory size_t report_desc_size; diff --git a/app/src/hid/hid_gamepad.c b/app/src/hid/hid_gamepad.c index e2bf0616..50c91b12 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -233,7 +233,10 @@ sc_hid_gamepad_slot_get_id(size_t slot_idx) { bool sc_hid_gamepad_generate_open(struct sc_hid_gamepad *hid, struct sc_hid_open *hid_open, - uint32_t gamepad_id) { + uint32_t gamepad_id, + uint16_t vendor_id, + uint16_t product_id, + uint16_t product_version) { assert(gamepad_id != SC_GAMEPAD_ID_INVALID); ssize_t slot_idx = sc_hid_gamepad_slot_find(hid, SC_GAMEPAD_ID_INVALID); if (slot_idx == -1) { @@ -250,6 +253,9 @@ sc_hid_gamepad_generate_open(struct sc_hid_gamepad *hid, uint16_t hid_id = sc_hid_gamepad_slot_get_id(slot_idx); hid_open->hid_id = hid_id; + hid_open->vendor_id = vendor_id; + hid_open->product_id = product_id; + hid_open->product_version = product_version; hid_open->name = name; hid_open->report_desc = SC_HID_GAMEPAD_REPORT_DESC; hid_open->report_desc_size = sizeof(SC_HID_GAMEPAD_REPORT_DESC); diff --git a/app/src/hid/hid_gamepad.h b/app/src/hid/hid_gamepad.h index b532a703..3b1b3940 100644 --- a/app/src/hid/hid_gamepad.h +++ b/app/src/hid/hid_gamepad.h @@ -33,7 +33,10 @@ sc_hid_gamepad_init(struct sc_hid_gamepad *hid); bool sc_hid_gamepad_generate_open(struct sc_hid_gamepad *hid, struct sc_hid_open *hid_open, - uint32_t gamepad_id); + uint32_t gamepad_id, + uint16_t vendor_id, + uint16_t product_id, + uint16_t product_version); bool sc_hid_gamepad_generate_close(struct sc_hid_gamepad *hid, diff --git a/app/src/hid/hid_keyboard.c b/app/src/hid/hid_keyboard.c index 2109224a..1aaf9561 100644 --- a/app/src/hid/hid_keyboard.c +++ b/app/src/hid/hid_keyboard.c @@ -335,6 +335,9 @@ sc_hid_keyboard_generate_input_from_mods(struct sc_hid_input *hid_input, void sc_hid_keyboard_generate_open(struct sc_hid_open *hid_open) { hid_open->hid_id = SC_HID_ID_KEYBOARD; + hid_open->vendor_id = 0; + hid_open->product_id = 0; + hid_open->product_version = 0; hid_open->name = NULL; // No name specified after "scrcpy" hid_open->report_desc = SC_HID_KEYBOARD_REPORT_DESC; hid_open->report_desc_size = sizeof(SC_HID_KEYBOARD_REPORT_DESC); diff --git a/app/src/hid/hid_mouse.c b/app/src/hid/hid_mouse.c index ac215165..4a111574 100644 --- a/app/src/hid/hid_mouse.c +++ b/app/src/hid/hid_mouse.c @@ -190,6 +190,9 @@ sc_hid_mouse_generate_input_from_scroll(struct sc_hid_input *hid_input, void sc_hid_mouse_generate_open(struct sc_hid_open *hid_open) { hid_open->hid_id = SC_HID_ID_MOUSE; + hid_open->vendor_id = 0; + hid_open->product_id = 0; + hid_open->product_version = 0; hid_open->name = NULL; // No name specified after "scrcpy" hid_open->report_desc = SC_HID_MOUSE_REPORT_DESC; hid_open->report_desc_size = sizeof(SC_HID_MOUSE_REPORT_DESC); diff --git a/app/src/input_events.h b/app/src/input_events.h index c8966a35..b3d79b04 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -425,6 +425,9 @@ enum sc_gamepad_device_event_type { struct sc_gamepad_device_event { enum sc_gamepad_device_event_type type; uint32_t gamepad_id; + uint16_t vendor_id; + uint16_t product_id; + uint16_t product_version; }; struct sc_gamepad_button_event { diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 969196e3..07aa913f 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -895,6 +895,10 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, static void sc_input_manager_process_gamepad_device(struct sc_input_manager *im, const SDL_ControllerDeviceEvent *event) { + uint16_t vendor_id = 0; + uint16_t product_id = 0; + uint16_t product_version = 0; + SDL_JoystickID id; if (event->type == SDL_CONTROLLERDEVICEADDED) { SDL_GameController *gc = SDL_GameControllerOpen(event->which); @@ -903,6 +907,10 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im, return; } + vendor_id = SDL_GameControllerGetVendor(gc); + product_id = SDL_GameControllerGetProduct(gc); + product_version = SDL_GameControllerGetProductVersion(gc); + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gc); if (!joystick) { LOGW("Could not get controller joystick"); @@ -928,6 +936,9 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im, struct sc_gamepad_device_event evt = { .type = sc_gamepad_device_event_type_from_sdl_type(event->type), .gamepad_id = id, + .vendor_id = vendor_id, + .product_id = product_id, + .product_version = product_version, }; im->gp->ops->process_gamepad_device(im->gp, &evt); } diff --git a/app/src/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index 62b0f653..da2cc2f3 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -30,6 +30,9 @@ sc_gamepad_uhid_send_open(struct sc_gamepad_uhid *gamepad, struct sc_control_msg msg; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.uhid_create.id = hid_open->hid_id; + msg.uhid_create.vendor_id = hid_open->vendor_id; + msg.uhid_create.product_id = hid_open->product_id; + msg.uhid_create.product_version = hid_open->product_version; msg.uhid_create.name = hid_open->name; msg.uhid_create.report_desc = hid_open->report_desc; msg.uhid_create.report_desc_size = hid_open->report_desc_size; @@ -59,7 +62,10 @@ sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp, if (event->type == SC_GAMEPAD_DEVICE_ADDED) { struct sc_hid_open hid_open; if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open, - event->gamepad_id)) { + event->gamepad_id, + event->vendor_id, + event->product_id, + event->product_version)) { return; } diff --git a/app/src/uhid/keyboard_uhid.c b/app/src/uhid/keyboard_uhid.c index 496da23d..059d57c0 100644 --- a/app/src/uhid/keyboard_uhid.c +++ b/app/src/uhid/keyboard_uhid.c @@ -141,6 +141,9 @@ sc_keyboard_uhid_init(struct sc_keyboard_uhid *kb, struct sc_control_msg msg; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.uhid_create.id = SC_HID_ID_KEYBOARD; + msg.uhid_create.vendor_id = hid_open.vendor_id; + msg.uhid_create.product_id = hid_open.product_id; + msg.uhid_create.product_version = hid_open.product_version; msg.uhid_create.name = hid_open.name; msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc_size = hid_open.report_desc_size; diff --git a/app/src/uhid/mouse_uhid.c b/app/src/uhid/mouse_uhid.c index 1dc02777..1a730788 100644 --- a/app/src/uhid/mouse_uhid.c +++ b/app/src/uhid/mouse_uhid.c @@ -81,6 +81,9 @@ sc_mouse_uhid_init(struct sc_mouse_uhid *mouse, struct sc_control_msg msg; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.uhid_create.id = SC_HID_ID_MOUSE; + msg.uhid_create.vendor_id = hid_open.vendor_id; + msg.uhid_create.product_id = hid_open.product_id; + msg.uhid_create.product_version = hid_open.product_version; msg.uhid_create.name = hid_open.name; msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc_size = hid_open.report_desc_size; diff --git a/app/src/usb/gamepad_aoa.c b/app/src/usb/gamepad_aoa.c index 37587532..48fadaf3 100644 --- a/app/src/usb/gamepad_aoa.c +++ b/app/src/usb/gamepad_aoa.c @@ -14,7 +14,10 @@ sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp, if (event->type == SC_GAMEPAD_DEVICE_ADDED) { struct sc_hid_open hid_open; if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open, - event->gamepad_id)) { + event->gamepad_id, + event->vendor_id, + event->product_id, + event->product_version)) { return; } diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 72ec61ee..64487987 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -329,6 +329,9 @@ static void test_serialize_uhid_create(void) { .type = SC_CONTROL_MSG_TYPE_UHID_CREATE, .uhid_create = { .id = 42, + .vendor_id = 1234, + .product_id = 5678, + .product_version = 30, .name = "ABC", .report_desc_size = sizeof(report_desc), .report_desc = report_desc, @@ -337,11 +340,14 @@ static void test_serialize_uhid_create(void) { uint8_t buf[SC_CONTROL_MSG_MAX_SIZE]; size_t size = sc_control_msg_serialize(&msg, buf); - assert(size == 20); + assert(size == 26); const uint8_t expected[] = { SC_CONTROL_MSG_TYPE_UHID_CREATE, 0, 42, // id + 1234 >> 8, 1234 & 0xff, // vendor id + 5678 >> 8, 5678 & 0xff, // product id + 0, 30, // product version 3, // name size 65, 66, 67, // "ABC" 0, 11, // report desc size 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 d1406ed0..cd9e8644 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java @@ -48,6 +48,9 @@ public final class ControlMessage { private long sequence; private int id; private byte[] data; + private int vendorId; + private int productId; + private int productVersion; private ControlMessage() { } @@ -131,12 +134,15 @@ public final class ControlMessage { return msg; } - public static ControlMessage createUhidCreate(int id, String name, byte[] reportDesc) { + public static ControlMessage createUhidCreate(int id, String name, byte[] reportDesc, int vendorId, int productId, int productVersion) { ControlMessage msg = new ControlMessage(); msg.type = TYPE_UHID_CREATE; msg.id = id; msg.text = name; msg.data = reportDesc; + msg.vendorId = vendorId; + msg.productId = productId; + msg.productVersion = productVersion; return msg; } @@ -226,4 +232,16 @@ public final class ControlMessage { public byte[] getData() { return data; } + + public int getVendorId() { + return vendorId; + } + + public int getProductId() { + return productId; + } + + public int getProductVersion() { + return productVersion; + } } 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 17e121c2..3dced2c4 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java @@ -139,9 +139,12 @@ public class ControlMessageReader { private ControlMessage parseUhidCreate() throws IOException { int id = dis.readUnsignedShort(); + int vendorId = dis.readUnsignedShort(); + int productId = dis.readUnsignedShort(); + int productVersion = dis.readUnsignedShort(); String name = parseString(1); byte[] data = parseByteArray(2); - return ControlMessage.createUhidCreate(id, name, data); + return ControlMessage.createUhidCreate(id, name, data, vendorId, productId, productVersion); } private ControlMessage parseUhidInput() throws IOException { diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java index b445427d..ad1e1b0c 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -210,7 +210,7 @@ public class Controller implements AsyncProcessor { device.rotateDevice(); break; case ControlMessage.TYPE_UHID_CREATE: - getUhidManager().open(msg.getId(), msg.getText(), msg.getData()); + getUhidManager().open(msg.getId(), msg.getText(), msg.getData(), msg.getVendorId(), msg.getProductId(), msg.getProductVersion()); break; case ControlMessage.TYPE_UHID_INPUT: getUhidManager().writeInput(msg.getId(), msg.getData()); diff --git a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java index d8cfd81f..49935f44 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java @@ -47,7 +47,7 @@ public final class UhidManager { } } - public void open(int id, String name, byte[] reportDesc) throws IOException { + public void open(int id, String name, byte[] reportDesc, int vendorId, int productId, int productVersion) throws IOException { try { FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0); try { @@ -57,7 +57,7 @@ public final class UhidManager { close(old); } - byte[] req = buildUhidCreate2Req(name, reportDesc); + byte[] req = buildUhidCreate2Req(name, reportDesc, vendorId, productId, productVersion); Os.write(fd, req, 0, req.length); registerUhidListener(id, fd); @@ -147,7 +147,7 @@ public final class UhidManager { } } - private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) { + private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc, int vendorId, int productId, int productVersion) { /* * struct uhid_event { * uint32_t type; @@ -182,9 +182,9 @@ public final class UhidManager { buf.putShort((short) reportDesc.length); buf.putShort(BUS_VIRTUAL); - buf.putInt(0); // vendor id - buf.putInt(0); // product id - buf.putInt(0); // version + buf.putInt(vendorId); // vendor id + buf.putInt(productId); // product id + buf.putInt(productVersion); // version buf.putInt(0); // country; buf.put(reportDesc); return buf.array(); 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 f29be2f4..570a05d9 100644 --- a/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java @@ -324,6 +324,9 @@ public class ControlMessageReaderTest { DataOutputStream dos = new DataOutputStream(bos); dos.writeByte(ControlMessage.TYPE_UHID_CREATE); dos.writeShort(42); // id + dos.writeShort(1234); // vendor id + dos.writeShort(5678); // product id + dos.writeShort(30); // product version dos.writeByte(3); // name size dos.write("ABC".getBytes(StandardCharsets.US_ASCII)); byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; @@ -337,6 +340,9 @@ public class ControlMessageReaderTest { ControlMessage event = reader.read(); Assert.assertEquals(ControlMessage.TYPE_UHID_CREATE, event.getType()); Assert.assertEquals(42, event.getId()); + Assert.assertEquals(1234, event.getVendorId()); + Assert.assertEquals(5678, event.getProductId()); + Assert.assertEquals(30, event.getProductVersion()); Assert.assertEquals("ABC", event.getText()); Assert.assertArrayEquals(data, event.getData());