From 4bd1c5981db307155452fa7594945e069542ddb3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 7 Dec 2024 14:12:53 +0100 Subject: [PATCH 1/8] Split gamepad device added/removed events Use two separate callbacks for gamepad device added and gamepad device removed. It looks cleaner. PR #5623 --- app/src/input_events.h | 16 ---------- app/src/input_manager.c | 19 +++++------ app/src/trait/gamepad_processor.h | 15 +++++++-- app/src/uhid/gamepad_uhid.c | 37 +++++++++++---------- app/src/usb/gamepad_aoa.c | 53 ++++++++++++++++--------------- app/src/usb/screen_otg.c | 22 ++++++------- 6 files changed, 80 insertions(+), 82 deletions(-) diff --git a/app/src/input_events.h b/app/src/input_events.h index c8966a355b..ad3afa81c8 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -412,18 +412,12 @@ struct sc_touch_event { float pressure; }; -enum sc_gamepad_device_event_type { - SC_GAMEPAD_DEVICE_ADDED, - SC_GAMEPAD_DEVICE_REMOVED, -}; - // As documented in : // The ID value starts at 0 and increments from there. The value -1 is an // invalid ID. #define SC_GAMEPAD_ID_INVALID UINT32_C(-1) struct sc_gamepad_device_event { - enum sc_gamepad_device_event_type type; uint32_t gamepad_id; }; @@ -503,16 +497,6 @@ sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) { return buttons_state; } -static inline enum sc_gamepad_device_event_type -sc_gamepad_device_event_type_from_sdl_type(uint32_t type) { - assert(type == SDL_CONTROLLERDEVICEADDED - || type == SDL_CONTROLLERDEVICEREMOVED); - if (type == SDL_CONTROLLERDEVICEADDED) { - return SC_GAMEPAD_DEVICE_ADDED; - } - return SC_GAMEPAD_DEVICE_REMOVED; -} - static inline enum sc_gamepad_axis sc_gamepad_axis_from_sdl(uint8_t axis) { if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) { diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 3955c211d7..2e4337db14 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -908,7 +908,6 @@ 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) { - SDL_JoystickID id; if (event->type == SDL_CONTROLLERDEVICEADDED) { SDL_GameController *gc = SDL_GameControllerOpen(event->which); if (!gc) { @@ -923,9 +922,12 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im, return; } - id = SDL_JoystickInstanceID(joystick); + struct sc_gamepad_device_event evt = { + .gamepad_id = SDL_JoystickInstanceID(joystick), + }; + im->gp->ops->process_gamepad_added(im->gp, &evt); } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { - id = event->which; + SDL_JoystickID id = event->which; SDL_GameController *gc = SDL_GameControllerFromInstanceID(id); if (gc) { @@ -933,16 +935,15 @@ sc_input_manager_process_gamepad_device(struct sc_input_manager *im, } else { LOGW("Unknown gamepad device removed"); } + + struct sc_gamepad_device_event evt = { + .gamepad_id = id, + }; + im->gp->ops->process_gamepad_removed(im->gp, &evt); } else { // Nothing to do return; } - - struct sc_gamepad_device_event evt = { - .type = sc_gamepad_device_event_type_from_sdl_type(event->type), - .gamepad_id = id, - }; - im->gp->ops->process_gamepad_device(im->gp, &evt); } static void diff --git a/app/src/trait/gamepad_processor.h b/app/src/trait/gamepad_processor.h index 724797839e..19629a9a1e 100644 --- a/app/src/trait/gamepad_processor.h +++ b/app/src/trait/gamepad_processor.h @@ -20,13 +20,22 @@ struct sc_gamepad_processor { struct sc_gamepad_processor_ops { /** - * Process a gamepad device added or removed + * Process a gamepad device added event * * This function is mandatory. */ void - (*process_gamepad_device)(struct sc_gamepad_processor *gp, - const struct sc_gamepad_device_event *event); + (*process_gamepad_added)(struct sc_gamepad_processor *gp, + const struct sc_gamepad_device_event *event); + + /** + * Process a gamepad device removed event + * + * This function is mandatory. + */ + void + (*process_gamepad_removed)(struct sc_gamepad_processor *gp, + const struct sc_gamepad_device_event *event); /** * Process a gamepad axis event diff --git a/app/src/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index 62b0f65375..42db63e75e 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -52,29 +52,31 @@ sc_gamepad_uhid_send_close(struct sc_gamepad_uhid *gamepad, } static void -sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp, +sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp, const struct sc_gamepad_device_event *event) { struct sc_gamepad_uhid *gamepad = DOWNCAST(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)) { - return; - } + struct sc_hid_open hid_open; + if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open, + event->gamepad_id)) { + return; + } - sc_gamepad_uhid_send_open(gamepad, &hid_open); - } else { - assert(event->type == SC_GAMEPAD_DEVICE_REMOVED); + sc_gamepad_uhid_send_open(gamepad, &hid_open); +} - struct sc_hid_close hid_close; - if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close, - event->gamepad_id)) { - return; - } +static void +sc_gamepad_processor_process_gamepad_removed(struct sc_gamepad_processor *gp, + const struct sc_gamepad_device_event *event) { + struct sc_gamepad_uhid *gamepad = DOWNCAST(gp); - sc_gamepad_uhid_send_close(gamepad, &hid_close); + struct sc_hid_close hid_close; + if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close, + event->gamepad_id)) { + return; } + + sc_gamepad_uhid_send_close(gamepad, &hid_close); } static void @@ -114,7 +116,8 @@ sc_gamepad_uhid_init(struct sc_gamepad_uhid *gamepad, gamepad->controller = controller; static const struct sc_gamepad_processor_ops ops = { - .process_gamepad_device = sc_gamepad_processor_process_gamepad_device, + .process_gamepad_added = sc_gamepad_processor_process_gamepad_added, + .process_gamepad_removed = sc_gamepad_processor_process_gamepad_removed, .process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis, .process_gamepad_button = sc_gamepad_processor_process_gamepad_button, }; diff --git a/app/src/usb/gamepad_aoa.c b/app/src/usb/gamepad_aoa.c index 37587532eb..4372379f05 100644 --- a/app/src/usb/gamepad_aoa.c +++ b/app/src/usb/gamepad_aoa.c @@ -7,33 +7,35 @@ #define DOWNCAST(GP) container_of(GP, struct sc_gamepad_aoa, gamepad_processor) static void -sc_gamepad_processor_process_gamepad_device(struct sc_gamepad_processor *gp, +sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp, const struct sc_gamepad_device_event *event) { struct sc_gamepad_aoa *gamepad = DOWNCAST(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)) { - return; - } - - // exit_on_error: false (a gamepad open failure should not exit scrcpy) - if (!sc_aoa_push_open(gamepad->aoa, &hid_open, false)) { - LOGW("Could not push AOA HID open (gamepad)"); - } - } else { - assert(event->type == SC_GAMEPAD_DEVICE_REMOVED); - - struct sc_hid_close hid_close; - if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close, - event->gamepad_id)) { - return; - } - - if (!sc_aoa_push_close(gamepad->aoa, &hid_close)) { - LOGW("Could not push AOA HID close (gamepad)"); - } + struct sc_hid_open hid_open; + if (!sc_hid_gamepad_generate_open(&gamepad->hid, &hid_open, + event->gamepad_id)) { + return; + } + + // exit_on_error: false (a gamepad open failure should not exit scrcpy) + if (!sc_aoa_push_open(gamepad->aoa, &hid_open, false)) { + LOGW("Could not push AOA HID open (gamepad)"); + } +} + +static void +sc_gamepad_processor_process_gamepad_removed(struct sc_gamepad_processor *gp, + const struct sc_gamepad_device_event *event) { + struct sc_gamepad_aoa *gamepad = DOWNCAST(gp); + + struct sc_hid_close hid_close; + if (!sc_hid_gamepad_generate_close(&gamepad->hid, &hid_close, + event->gamepad_id)) { + return; + } + + if (!sc_aoa_push_close(gamepad->aoa, &hid_close)) { + LOGW("Could not push AOA HID close (gamepad)"); } } @@ -76,7 +78,8 @@ sc_gamepad_aoa_init(struct sc_gamepad_aoa *gamepad, struct sc_aoa *aoa) { sc_hid_gamepad_init(&gamepad->hid); static const struct sc_gamepad_processor_ops ops = { - .process_gamepad_device = sc_gamepad_processor_process_gamepad_device, + .process_gamepad_added = sc_gamepad_processor_process_gamepad_added, + .process_gamepad_removed = sc_gamepad_processor_process_gamepad_removed, .process_gamepad_axis = sc_gamepad_processor_process_gamepad_axis, .process_gamepad_button = sc_gamepad_processor_process_gamepad_button, }; diff --git a/app/src/usb/screen_otg.c b/app/src/usb/screen_otg.c index 183770743f..368af125b7 100644 --- a/app/src/usb/screen_otg.c +++ b/app/src/usb/screen_otg.c @@ -175,7 +175,6 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen, assert(screen->gamepad); struct sc_gamepad_processor *gp = &screen->gamepad->gamepad_processor; - SDL_JoystickID id; if (event->type == SDL_CONTROLLERDEVICEADDED) { SDL_GameController *gc = SDL_GameControllerOpen(event->which); if (!gc) { @@ -190,9 +189,12 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen, return; } - id = SDL_JoystickInstanceID(joystick); + struct sc_gamepad_device_event evt = { + .gamepad_id = SDL_JoystickInstanceID(joystick), + }; + gp->ops->process_gamepad_added(gp, &evt); } else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { - id = event->which; + SDL_JoystickID id = event->which; SDL_GameController *gc = SDL_GameControllerFromInstanceID(id); if (gc) { @@ -200,16 +202,12 @@ sc_screen_otg_process_gamepad_device(struct sc_screen_otg *screen, } else { LOGW("Unknown gamepad device removed"); } - } else { - // Nothing to do - return; - } - struct sc_gamepad_device_event evt = { - .type = sc_gamepad_device_event_type_from_sdl_type(event->type), - .gamepad_id = id, - }; - gp->ops->process_gamepad_device(gp, &evt); + struct sc_gamepad_device_event evt = { + .gamepad_id = id, + }; + gp->ops->process_gamepad_removed(gp, &evt); + } } static void From 9cf4d527215a3f21077b4d28466632be26f72917 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 6 Dec 2024 12:53:07 +0100 Subject: [PATCH 2/8] Fix HID gamepad comments PR #5623 --- app/src/hid/hid_gamepad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/hid/hid_gamepad.c b/app/src/hid/hid_gamepad.c index e2bf06165f..99facdd020 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -65,7 +65,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = { 0x75, 0x10, // Report Count (4) 0x95, 0x04, - // Input (Data, Variable, Absolute): 4 bytes (X, Y, Z, Rz) + // Input (Data, Variable, Absolute): 4x2 bytes (X, Y, Z, Rz) 0x81, 0x02, // Usage Page (Simulation Controls) @@ -82,7 +82,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = { 0x75, 0x10, // Report Count (2) 0x95, 0x02, - // Input (Data, Variable, Absolute): 2 bytes (L2, R2) + // Input (Data, Variable, Absolute): 2x2 bytes (L2, R2) 0x81, 0x02, // Usage Page (Buttons) @@ -182,7 +182,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = { * `------------- SC_GAMEPAD_BUTTON_RIGHT_STICK * * +---------------+ - * byte 14: |0 0 0 . . . . .| hat switch (dpad) position (0-8) + * byte 14: |0 0 0 0 . . . .| hat switch (dpad) position (0-8) * +---------------+ * 9 possible positions and their values: * 8 1 2 From 1786f28e6f9f9c597f4d66de88c206489cb87122 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 6 Dec 2024 12:53:28 +0100 Subject: [PATCH 3/8] Fix gamepad HID descriptor Use Z and Rz for L2/R2, which are more widely supported than Brake/Accelerator. The right stick must then be bound to Rx and Ry. Fixes #5362 PR #5623 --- app/src/hid/hid_gamepad.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/hid/hid_gamepad.c b/app/src/hid/hid_gamepad.c index 99facdd020..977bcf68f1 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -52,10 +52,10 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = { 0x09, 0x30, // Usage (Y) Left stick y 0x09, 0x31, - // Usage (Z) Right stick x - 0x09, 0x32, - // Usage (Rz) Right stick y - 0x09, 0x35, + // Usage (Rx) Right stick x + 0x09, 0x33, + // Usage (Ry) Right stick y + 0x09, 0x34, // Logical Minimum (0) 0x15, 0x00, // Logical Maximum (65535) @@ -68,12 +68,12 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = { // Input (Data, Variable, Absolute): 4x2 bytes (X, Y, Z, Rz) 0x81, 0x02, - // Usage Page (Simulation Controls) - 0x05, 0x02, - // Usage (Brake) - 0x09, 0xC5, - // Usage (Accelerator) - 0x09, 0xC4, + // Usage Page (Generic Desktop) + 0x05, 0x01, + // Usage (Z) + 0x09, 0x32, + // Usage (Rz) + 0x09, 0x35, // Logical Minimum (0) 0x15, 0x00, // Logical Maximum (32767) From 86a68fac6c631a01e8d0132eee0fc5a831e78417 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 7 Dec 2024 14:40:04 +0100 Subject: [PATCH 4/8] Fix gamepad axis initial values By default, initialize axis to 0, which is represented by 0x8000 as a 16-bit unsigned value. PR #5623 --- app/src/hid/hid_gamepad.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/hid/hid_gamepad.c b/app/src/hid/hid_gamepad.c index 977bcf68f1..892d21f29a 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -191,16 +191,19 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = { * (8 is top-left, 1 is top, 2 is top-right, etc.) */ +// [-32768 to 32767] -> [0 to 65535] +#define AXIS_RESCALE(V) (uint16_t) (((int32_t) V) + 0x8000) + static void sc_hid_gamepad_slot_init(struct sc_hid_gamepad_slot *slot, uint32_t gamepad_id) { assert(gamepad_id != SC_GAMEPAD_ID_INVALID); slot->gamepad_id = gamepad_id; slot->buttons = 0; - slot->axis_left_x = 0; - slot->axis_left_y = 0; - slot->axis_right_x = 0; - slot->axis_right_y = 0; + slot->axis_left_x = AXIS_RESCALE(0); + slot->axis_left_y = AXIS_RESCALE(0); + slot->axis_right_x = AXIS_RESCALE(0); + slot->axis_right_y = AXIS_RESCALE(0); slot->axis_left_trigger = 0; slot->axis_right_trigger = 0; } @@ -423,8 +426,6 @@ sc_hid_gamepad_generate_input_from_axis(struct sc_hid_gamepad *hid, struct sc_hid_gamepad_slot *slot = &hid->slots[slot_idx]; -// [-32768 to 32767] -> [0 to 65535] -#define AXIS_RESCALE(V) (uint16_t) (((int32_t) V) + 0x8000) switch (event->axis) { case SC_GAMEPAD_AXIS_LEFTX: slot->axis_left_x = AXIS_RESCALE(event->value); From 27a5934a1d5365332e4338f76508139dbd61d1ef Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 7 Dec 2024 12:43:49 +0100 Subject: [PATCH 5/8] Define UHID vendorId and productId from the client Let the client choose the USB ids, that it transmits in UHID_CREATE requests. PR #5623 --- app/src/control_msg.c | 14 ++++++++++---- app/src/control_msg.h | 2 ++ app/src/uhid/gamepad_uhid.c | 5 +++++ app/src/uhid/keyboard_uhid.c | 2 ++ app/src/uhid/mouse_uhid.c | 2 ++ app/tests/test_control_msg_serialize.c | 6 +++++- .../genymobile/scrcpy/control/ControlMessage.java | 14 +++++++++++++- .../scrcpy/control/ControlMessageReader.java | 4 +++- .../com/genymobile/scrcpy/control/Controller.java | 2 +- .../com/genymobile/scrcpy/control/UhidManager.java | 10 +++++----- .../scrcpy/control/ControlMessageReaderTest.java | 4 ++++ 11 files changed, 52 insertions(+), 13 deletions(-) diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 0defda9257..e78f0c573e 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -152,8 +152,10 @@ 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); - size_t index = 3; + size_t index = 7; index += write_string_tiny(&buf[index], msg->uhid_create.name, 127); sc_write16be(&buf[index], msg->uhid_create.report_desc_size); @@ -278,9 +280,13 @@ 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 + " name=%s%s%s report_desc_size=%" PRIu16, + msg->uhid_create.id, + msg->uhid_create.vendor_id, + msg->uhid_create.product_id, + 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 f0a2e37346..74dbcba800 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -94,6 +94,8 @@ struct sc_control_msg { } set_display_power; struct { uint16_t id; + uint16_t vendor_id; + uint16_t product_id; 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/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index 42db63e75e..5b5744097c 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -7,6 +7,9 @@ /** Downcast gamepad processor to sc_gamepad_uhid */ #define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor) +#define SC_GAMEPAD_UHID_VENDOR_ID 0 +#define SC_GAMEPAD_UHID_PRODUCT_ID 0 + static void sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad, const struct sc_hid_input *hid_input, @@ -30,6 +33,8 @@ 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 = SC_GAMEPAD_UHID_VENDOR_ID; + msg.uhid_create.product_id = SC_GAMEPAD_UHID_PRODUCT_ID; 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/keyboard_uhid.c b/app/src/uhid/keyboard_uhid.c index 496da23d79..4d2c978d23 100644 --- a/app/src/uhid/keyboard_uhid.c +++ b/app/src/uhid/keyboard_uhid.c @@ -141,6 +141,8 @@ 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 = 0; + msg.uhid_create.product_id = 0; 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 1dc02777bd..d6044bdcd2 100644 --- a/app/src/uhid/mouse_uhid.c +++ b/app/src/uhid/mouse_uhid.c @@ -81,6 +81,8 @@ 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 = 0; + msg.uhid_create.product_id = 0; 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/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 9adf2a3d7f..af97182d6b 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -329,6 +329,8 @@ static void test_serialize_uhid_create(void) { .type = SC_CONTROL_MSG_TYPE_UHID_CREATE, .uhid_create = { .id = 42, + .vendor_id = 0x1234, + .product_id = 0x5678, .name = "ABC", .report_desc_size = sizeof(report_desc), .report_desc = report_desc, @@ -337,11 +339,13 @@ 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 == 24); const uint8_t expected[] = { SC_CONTROL_MSG_TYPE_UHID_CREATE, 0, 42, // id + 0x12, 0x34, // vendor id + 0x56, 0x78, // product id 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 7455cdf836..0eb96adcbc 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java @@ -51,6 +51,8 @@ public final class ControlMessage { private int id; private byte[] data; private boolean on; + private int vendorId; + private int productId; private ControlMessage() { } @@ -131,10 +133,12 @@ public static ControlMessage createEmpty(int type) { return msg; } - public static ControlMessage createUhidCreate(int id, String name, byte[] reportDesc) { + public static ControlMessage createUhidCreate(int id, int vendorId, int productId, String name, byte[] reportDesc) { ControlMessage msg = new ControlMessage(); msg.type = TYPE_UHID_CREATE; msg.id = id; + msg.vendorId = vendorId; + msg.productId = productId; msg.text = name; msg.data = reportDesc; return msg; @@ -237,4 +241,12 @@ public byte[] getData() { public boolean getOn() { return on; } + + public int getVendorId() { + return vendorId; + } + + public int getProductId() { + return productId; + } } 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 b82615ede8..e503ec615e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java @@ -142,9 +142,11 @@ private ControlMessage parseSetDisplayPower() throws IOException { private ControlMessage parseUhidCreate() throws IOException { int id = dis.readUnsignedShort(); + int vendorId = dis.readUnsignedShort(); + int productId = dis.readUnsignedShort(); String name = parseString(1); byte[] data = parseByteArray(2); - return ControlMessage.createUhidCreate(id, name, data); + return ControlMessage.createUhidCreate(id, vendorId, productId, name, data); } 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 a0bdc58427..5e64a4c5b6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -290,7 +290,7 @@ private boolean handleEvent() throws IOException { Device.rotateDevice(getActionDisplayId()); break; case ControlMessage.TYPE_UHID_CREATE: - getUhidManager().open(msg.getId(), msg.getText(), msg.getData()); + getUhidManager().open(msg.getId(), msg.getVendorId(), msg.getProductId(), msg.getText(), msg.getData()); 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 8121adfcb7..1d7678ecba 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java @@ -48,7 +48,7 @@ public UhidManager(DeviceMessageSender sender) { } } - public void open(int id, String name, byte[] reportDesc) throws IOException { + public void open(int id, int vendorId, int productId, String name, byte[] reportDesc) throws IOException { try { FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0); try { @@ -58,7 +58,7 @@ public void open(int id, String name, byte[] reportDesc) throws IOException { close(old); } - byte[] req = buildUhidCreate2Req(name, reportDesc); + byte[] req = buildUhidCreate2Req(vendorId, productId, name, reportDesc); Os.write(fd, req, 0, req.length); registerUhidListener(id, fd); @@ -148,7 +148,7 @@ public void writeInput(int id, byte[] data) throws IOException { } } - private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) { + private static byte[] buildUhidCreate2Req(int vendorId, int productId, String name, byte[] reportDesc) { /* * struct uhid_event { * uint32_t type; @@ -183,8 +183,8 @@ private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) { buf.putShort((short) reportDesc.length); buf.putShort(BUS_VIRTUAL); - buf.putInt(0); // vendor id - buf.putInt(0); // product id + buf.putInt(vendorId); + buf.putInt(productId); buf.putInt(0); // version buf.putInt(0); // country; buf.put(reportDesc); 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 a25507b483..74df064f7d 100644 --- a/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/control/ControlMessageReaderTest.java @@ -322,6 +322,8 @@ public void testParseUhidCreate() throws IOException { DataOutputStream dos = new DataOutputStream(bos); dos.writeByte(ControlMessage.TYPE_UHID_CREATE); dos.writeShort(42); // id + dos.writeShort(0x1234); // vendorId + dos.writeShort(0x5678); // productId 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}; @@ -335,6 +337,8 @@ public void testParseUhidCreate() throws IOException { ControlMessage event = reader.read(); Assert.assertEquals(ControlMessage.TYPE_UHID_CREATE, event.getType()); Assert.assertEquals(42, event.getId()); + Assert.assertEquals(0x1234, event.getVendorId()); + Assert.assertEquals(0x5678, event.getProductId()); Assert.assertEquals("ABC", event.getText()); Assert.assertArrayEquals(data, event.getData()); From 0a09518a49cb495ba76573597cf38169f6813209 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 7 Dec 2024 18:01:23 +0100 Subject: [PATCH 6/8] Use Xbox 360 gamepad USB ids Use the vendorId and productId of an Xbox 360 controller for better support (the HID gamepad protocol used in scrcpy is similar to that of the Xbox 360 controller). Fixes #5362 PR #5623 --- app/src/uhid/gamepad_uhid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index 5b5744097c..2a063af546 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -7,8 +7,9 @@ /** Downcast gamepad processor to sc_gamepad_uhid */ #define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor) -#define SC_GAMEPAD_UHID_VENDOR_ID 0 -#define SC_GAMEPAD_UHID_PRODUCT_ID 0 +// Xbox 360 +#define SC_GAMEPAD_UHID_VENDOR_ID UINT16_C(0x045e) +#define SC_GAMEPAD_UHID_PRODUCT_ID UINT16_C(0x028e) static void sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad, From 7418fd06626a2ae405f4bfbe9980a9c4ebd744a7 Mon Sep 17 00:00:00 2001 From: Withoutruless <57673426+Withoutruless@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:49:07 +0100 Subject: [PATCH 7/8] Use Xbox 360 gamepad name Some games do not work without a known gamepad name. Fixes #5362 Refs #5623 comment PR #5623 Signed-off-by: Romain Vimont --- app/src/hid/hid_event.h | 1 - app/src/hid/hid_gamepad.c | 6 ------ app/src/hid/hid_keyboard.c | 1 - app/src/hid/hid_mouse.c | 1 - app/src/uhid/gamepad_uhid.c | 3 ++- app/src/uhid/keyboard_uhid.c | 2 +- app/src/uhid/mouse_uhid.c | 2 +- .../java/com/genymobile/scrcpy/control/UhidManager.java | 2 +- 8 files changed, 5 insertions(+), 13 deletions(-) diff --git a/app/src/hid/hid_event.h b/app/src/hid/hid_event.h index 37c3611baf..d6818e3014 100644 --- a/app/src/hid/hid_event.h +++ b/app/src/hid/hid_event.h @@ -15,7 +15,6 @@ struct sc_hid_input { struct sc_hid_open { uint16_t hid_id; - 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 892d21f29a..8f4e4527b2 100644 --- a/app/src/hid/hid_gamepad.c +++ b/app/src/hid/hid_gamepad.c @@ -246,14 +246,8 @@ sc_hid_gamepad_generate_open(struct sc_hid_gamepad *hid, sc_hid_gamepad_slot_init(&hid->slots[slot_idx], gamepad_id); - SDL_GameController* game_controller = - SDL_GameControllerFromInstanceID(gamepad_id); - assert(game_controller); - const char *name = SDL_GameControllerName(game_controller); - uint16_t hid_id = sc_hid_gamepad_slot_get_id(slot_idx); hid_open->hid_id = hid_id; - 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_keyboard.c b/app/src/hid/hid_keyboard.c index 2109224a6e..961ad790a7 100644 --- a/app/src/hid/hid_keyboard.c +++ b/app/src/hid/hid_keyboard.c @@ -335,7 +335,6 @@ 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->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 ac215165eb..7acc413bcf 100644 --- a/app/src/hid/hid_mouse.c +++ b/app/src/hid/hid_mouse.c @@ -190,7 +190,6 @@ 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->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/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index 2a063af546..4da4a21e34 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -10,6 +10,7 @@ // Xbox 360 #define SC_GAMEPAD_UHID_VENDOR_ID UINT16_C(0x045e) #define SC_GAMEPAD_UHID_PRODUCT_ID UINT16_C(0x028e) +#define SC_GAMEPAD_UHID_NAME "Microsoft X-Box 360 Pad" static void sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad, @@ -36,7 +37,7 @@ sc_gamepad_uhid_send_open(struct sc_gamepad_uhid *gamepad, msg.uhid_create.id = hid_open->hid_id; msg.uhid_create.vendor_id = SC_GAMEPAD_UHID_VENDOR_ID; msg.uhid_create.product_id = SC_GAMEPAD_UHID_PRODUCT_ID; - msg.uhid_create.name = hid_open->name; + msg.uhid_create.name = SC_GAMEPAD_UHID_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/keyboard_uhid.c b/app/src/uhid/keyboard_uhid.c index 4d2c978d23..76d70cc51b 100644 --- a/app/src/uhid/keyboard_uhid.c +++ b/app/src/uhid/keyboard_uhid.c @@ -143,7 +143,7 @@ sc_keyboard_uhid_init(struct sc_keyboard_uhid *kb, msg.uhid_create.id = SC_HID_ID_KEYBOARD; msg.uhid_create.vendor_id = 0; msg.uhid_create.product_id = 0; - msg.uhid_create.name = hid_open.name; + msg.uhid_create.name = NULL; msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc_size = hid_open.report_desc_size; if (!sc_controller_push_msg(controller, &msg)) { diff --git a/app/src/uhid/mouse_uhid.c b/app/src/uhid/mouse_uhid.c index d6044bdcd2..471030e7a5 100644 --- a/app/src/uhid/mouse_uhid.c +++ b/app/src/uhid/mouse_uhid.c @@ -83,7 +83,7 @@ sc_mouse_uhid_init(struct sc_mouse_uhid *mouse, msg.uhid_create.id = SC_HID_ID_MOUSE; msg.uhid_create.vendor_id = 0; msg.uhid_create.product_id = 0; - msg.uhid_create.name = hid_open.name; + msg.uhid_create.name = NULL; msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc_size = hid_open.report_desc_size; if (!sc_controller_push_msg(controller, &msg)) { 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 1d7678ecba..c4867a3f35 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java @@ -174,7 +174,7 @@ private static byte[] buildUhidCreate2Req(int vendorId, int productId, String na ByteBuffer buf = ByteBuffer.allocate(280 + reportDesc.length).order(ByteOrder.nativeOrder()); buf.putInt(UHID_CREATE2); - String actualName = name.isEmpty() ? "scrcpy" : "scrcpy: " + name; + String actualName = name.isEmpty() ? "scrcpy" : name; byte[] utf8Name = actualName.getBytes(StandardCharsets.UTF_8); int len = StringUtils.getUtf8TruncationIndex(utf8Name, 127); assert len <= 127; From 328bb74f8002693e4be2703450305e82fc015e88 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 8 Dec 2024 17:07:03 +0100 Subject: [PATCH 8/8] Log gamepad added/removed Add a log when a gamepad is added or removed. PR #5623 --- app/src/uhid/gamepad_uhid.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/uhid/gamepad_uhid.c b/app/src/uhid/gamepad_uhid.c index 4da4a21e34..a066cf0305 100644 --- a/app/src/uhid/gamepad_uhid.c +++ b/app/src/uhid/gamepad_uhid.c @@ -69,6 +69,12 @@ sc_gamepad_processor_process_gamepad_added(struct sc_gamepad_processor *gp, return; } + SDL_GameController* game_controller = + SDL_GameControllerFromInstanceID(event->gamepad_id); + assert(game_controller); + const char *name = SDL_GameControllerName(game_controller); + LOGI("Gamepad added: [%" PRIu32 "] %s", event->gamepad_id, name); + sc_gamepad_uhid_send_open(gamepad, &hid_open); } @@ -83,6 +89,8 @@ sc_gamepad_processor_process_gamepad_removed(struct sc_gamepad_processor *gp, return; } + LOGI("Gamepad removed: [%" PRIu32 "]", event->gamepad_id); + sc_gamepad_uhid_send_close(gamepad, &hid_close); }