Compare commits

..

36 Commits

Author SHA1 Message Date
Romain Vimont
b54b0a6958 Add UHID gamepad rumble WIP 2024-09-07 23:24:42 +02:00
Romain Vimont
e5c7840530 Simplify UHID outputs routing
There was a registration mechanism to listen to HID outputs with a
specific HID id.

However, the UHID gamepad processor handles several ids, so it will not
work. We could complexify the registration mechanism, but instead,
directly dispatch to the expected processor based on the UHID id.

Concretely, instead of passing a sc_uhid_devices instance to construct a
sc_keyboard_uhid, so that it can register itself, construct the
sc_uhid_devices with all the UHID instances (currently only
sc_keyboard_uhid) so that it can dispatch HID outputs directly.
2024-09-07 23:24:42 +02:00
Romain Vimont
ccc030625b Make -K -M and -G use AOA in OTG mode
For convenience, short options were added to select UHID input modes:
 - -K for --keyboard=uhid
 - -M for --mouse=uhid
 - -G for --gamepad=uhid

In OTG mode, UHID is not available, so the short options should select
AOA instead.
2024-09-07 23:24:42 +02:00
Romain Vimont
b0b05909c0 Add UHID gamepad support
Similar to UHID keyboard and mouse, but for gamepads.

Can be enabled with --gamepad=uhid or -G.

It is not enabled by default because not all devices support UHID
(there is a permission error on old Android versions).
2024-09-07 23:24:42 +02:00
Romain Vimont
330d844231 Add UHID_DESTROY control message
This message will be sent on gamepad disconnection.

Contrary to keyboard and mouse, which are registered once and are
unregistered when scrcpy exists, each gamepad is mapped with its own HID
id, and they can be plugged/unplugged dynamically.
2024-09-07 23:24:42 +02:00
Romain Vimont
ab6732f32d Add gamepad support in OTG mode
Implement gamepad support for OTG.
2024-09-07 23:24:42 +02:00
Romain Vimont
32c73a5448 Add AOA gamepad support
Similar to AOA keyboard and mouse, but for gamepads.

Can be enabled with --gamepad=aoa.
2024-09-07 23:24:42 +02:00
Romain Vimont
c72c772c81 Implement HID gamepad
Implement the HID protocol for gamepads, that will be used in further
commits by the AOA and UHID gamepad processor implementations.
2024-09-07 23:24:42 +02:00
Romain Vimont
d2e1d96fbd Add util functions to write in little-endian
This will be helpful for writing HID values.
2024-09-07 23:24:42 +02:00
Romain Vimont
07d5a6ca80 Add connected gamepads on start
Trigger SDL_CONTROLLERDEVICEADDED for all gamepads already connected
when scrcpy starts. We want to handle both the gamepads initially
connected and the gamepads connected while scrcpy is running.

It is a bit racy, because a device may be added after the controller
subsystem initialization, but before the call to list the joysticks. In
that case, a single device may in theory be detected as added twice.
This should be harmless though (the second initialization will just fail
without major impact).
2024-09-07 23:24:15 +02:00
Romain Vimont
2d929fa4e5 Handle SDL gamepad events
Introduce a gamepad processor trait, similar to the keyboard processor
and mouse processor traits.

Handle gamepad events received from SDL, convert them to scrcpy-specific
gamepad events, and forward them to the gamepad processor.

Further commits will provide AOA and UHID implementations of the gamepad
processor trait.

Co-authored-by: Luiz Henrique Laurini <luizhenriquelaurini@gmail.com>
2024-09-07 22:45:05 +02:00
Romain Vimont
623346b558 Fix HID comments
Fix typo and reference the latest version of "HID Usage Tables"
specifications.
2024-09-07 22:45:05 +02:00
Romain Vimont
5b52afd980 Make AOA keyboard/mouse open error fatal
Now that the AOA open/close are asynchronous, an open error did not make
scrcpy exit anymore.

Add a mechanism to exit if the AOA device could not be opened
asynchronously.
2024-09-07 22:45:05 +02:00
Romain Vimont
4c5d43d41d Unregister all AOA devices automatically on exit
Pushing a close event from the keyboard_aoa or mouse_aoa implementation
was racy, because the AOA thread might be stopped before these events
were processed.

Instead, keep the list of open AOA devices to close them automatically
from the AOA thread before exiting.
2024-09-07 22:45:05 +02:00
Romain Vimont
2351eed46f Make HID logs uniform 2024-09-07 22:45:05 +02:00
Romain Vimont
2595bcfa75 Add AOA open/close verbose logs 2024-09-07 22:45:05 +02:00
Romain Vimont
93dad86634 Introduce hid_open and hid_close events
This allows to handle HID open/close at the same place as HID input
events (in the HID layer).

This will be especially useful to manage HID gamepads, to avoid
implementing one part in the HID layer and another part in the gamepad
processor implementation.
2024-09-07 22:45:05 +02:00
Romain Vimont
282e6503e1 Rename hid_event to hid_input
The sc_hid_event structure represents HID input data. Rename it so that
we can add other hid event structs without confusion.
2024-09-07 22:45:05 +02:00
Romain Vimont
9c86cf74e6 Make AOA open and close asynchronous
For AOA keyboard and mouse, only input events were asynchronous.
Register/unregister were called from the main thread.

This had the benefit to fail immediately if the AOA registration failed,
but to support gamepads we want to open/close AOA devices dynamically.

Also, it is better to avoid USB I/O from the main thread.
2024-09-07 22:45:05 +02:00
Romain Vimont
2caf593960 Reorder AOA functions
This will allow sc_aoa_setup_hid() to compile even when
sc_aoa_unregister_hid() will be made static.
2024-09-07 22:45:05 +02:00
Romain Vimont
1344b76f42 Refactor AOA handling
Extract event processing to a separate function.

This will make the code more readable when more event types will be
added.
2024-09-07 22:45:05 +02:00
Romain Vimont
32b1a8093a Move HID ids to common HID code
The HID ids (accessory ids or UHID ids) were defined by the keyboard and
mouse implementations.

Instead, define them in the common HID part, and make that id part of
the sc_hid_event.

This prepares the introduction of gamepad support, which will handle
several gamepads (and ids) in the common HID gamepad code.
2024-09-07 22:45:05 +02:00
Romain Vimont
4416aa29f1 Fix HID mouse header guard 2024-09-07 22:45:05 +02:00
Romain Vimont
4b95a9674a Add missing SC_ prefix for HID mouse event size 2024-09-07 22:45:05 +02:00
Romain Vimont
c7c8d71de5 Remove duplicate definition SC_HID_MAX_SIZE
This constant is defined in hid_event.h.
2024-09-07 22:45:05 +02:00
Romain Vimont
de6ee25d09 Fail on AOA keyboard/mouse initialization error
If the AOA keyboard or the AOA mouse fails to be initialized, this is a
fatal error.
2024-09-07 22:45:05 +02:00
Romain Vimont
70a9bbbc06 Introduce non-droppable control messages
Control messages are queued from the main thread and sent to the device
from a separate thread.

When the queue is full, messages are just dropped. This avoids to
accumulate too much delay between the client and the device in case of
network issue.

However, some messages should not be dropped: for example, dropping a
UHID_CREATE message would make invalid all further UHID_INPUT messages.
Therefore, mark these messages as non-droppable.

A non-droppable event is queued anyway (resizing the queue if
necessary, unless the allocation fails).
2024-09-07 22:45:05 +02:00
Romain Vimont
97e83fa3d6 Remove atomics from keyboard_uhid
The UHID output callback is now called from the same (main) thread as
the process_key() function.
2024-09-07 22:45:05 +02:00
Romain Vimont
0215693725 Process UHID outputs events from the main thread
This will guarantee that the callbacks of UHID devices implementations
will always be called from the same (main) thread.
2024-09-07 22:45:05 +02:00
Romain Vimont
220cad2e7a Set clipboard from the main thread
The clipboard changes from the device are received from a separate
thread, but it must be handled from the main thread.
2024-09-07 22:45:05 +02:00
Romain Vimont
0b6119a594 Add mechanism to execute code on the main thread
This allows to schedule a runnable to be executed on the main thread,
until the event loop is explicitly terminated.

It is guaranteed that all accepted runnables will be executed (this
avoids possible memory leaks if a runnable owns resources).
2024-09-07 22:44:51 +02:00
Romain Vimont
9cae029ef2 Expose main thread id
This will allow to assert that a function is called from the main
thread.
2024-09-07 22:05:18 +02:00
Romain Vimont
72d5bcc12a Extract sc_push_event()
Expose a convenience function to push an event without args to the main
thread.
2024-09-07 22:03:27 +02:00
Romain Vimont
d56b8a6a95 Store events numbers in an enum
This avoids to manually set an explicit value for each item.
2024-09-07 22:03:27 +02:00
Romain Vimont
580cba73c0 Fix deprecated reference in scrcpy manpage
The options --hid-keyboard and --hid-mouse do not exist anymore. They
have been replaced by --keyboard=XXX and --mouse=XXX.
2024-09-07 18:36:05 +02:00
Romain Vimont
523299743c Do not send uninitialized HID event
If the function returns false, then there is nothing to send.
2024-09-07 18:35:16 +02:00
20 changed files with 42 additions and 127 deletions

View File

@@ -26,8 +26,6 @@ _scrcpy() {
-e --select-tcpip
-f --fullscreen
--force-adb-forward
-G
--gamepad=
-h --help
-K
--keyboard=
@@ -129,10 +127,6 @@ _scrcpy() {
COMPREPLY=($(compgen -W 'disabled sdk uhid aoa' -- "$cur"))
return
;;
--gamepad)
COMPREPLY=($(compgen -W 'disabled uhid aoa' -- "$cur"))
return
;;
--orientation|--display-orientation)
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
return

View File

@@ -33,10 +33,8 @@ arguments=(
{-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]'
'--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]'
'-G[Use UHID/AOA gamepad (same as --gamepad=uhid or --gamepad=aoa, depending on OTG mode)]'
'--gamepad=[Set the gamepad input mode]:mode:(disabled uhid aoa)'
{-h,--help}'[Print the help]'
'-K[Use UHID/AOA keyboard (same as --keyboard=uhid or --keyboard=aoa, depending on OTG mode)]'
'-K[Use UHID keyboard (same as --keyboard=uhid)]'
'--keyboard=[Set the keyboard input mode]:mode:(disabled sdk uhid aoa)'
'--kill-adb-on-close[Kill adb when scrcpy terminates]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
@@ -46,7 +44,7 @@ arguments=(
'--list-encoders[List video and audio encoders available on the device]'
'--lock-video-orientation=[Lock video orientation]:orientation:(unlocked initial 0 90 180 270)'
{-m,--max-size=}'[Limit both the width and height of the video to value]'
'-M[Use UHID/AOA mouse (same as --mouse=uhid or --mouse=aoa, depending on OTG mode)]'
'-M[Use UHID mouse (same as --mouse=uhid)]'
'--max-fps=[Limit the frame rate of screen capture]'
'--mouse=[Set the mouse input mode]:mode:(disabled sdk uhid aoa)'
'--mouse-bind=[Configure bindings of secondary clicks]'

View File

@@ -2812,7 +2812,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_UHID_OR_AOA) {
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA
: SC_MOUSE_INPUT_MODE_UHID;
: SC_MOUSE_INPUT_MODE_SDK;
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK
&& !opts->video_playback) {
LOGE("SDK mouse mode requires video playback. Try --mouse=uhid.");
@@ -2885,16 +2885,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}
enum sc_gamepad_input_mode gmode = opts->gamepad_input_mode;
if (gmode != SC_GAMEPAD_INPUT_MODE_AOA
&& gmode != SC_GAMEPAD_INPUT_MODE_DISABLED) {
LOGE("In OTG mode, --gamepad only supports aoa or disabled.");
}
if (kmode == SC_KEYBOARD_INPUT_MODE_DISABLED
&& mmode == SC_MOUSE_INPUT_MODE_DISABLED
&& gmode == SC_GAMEPAD_INPUT_MODE_DISABLED) {
LOGE("Cannot not disable all inputs in OTG mode.");
&& mmode == SC_MOUSE_INPUT_MODE_DISABLED) {
LOGE("Could not disable both keyboard and mouse in OTG mode.");
return false;
}
}
@@ -2935,18 +2928,18 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
if (opts->camera_id && opts->camera_facing != SC_CAMERA_FACING_ANY) {
LOGE("Cannot specify both --camera-id and --camera-facing");
LOGE("Could not specify both --camera-id and --camera-facing");
return false;
}
if (opts->camera_size) {
if (opts->max_size) {
LOGE("Cannot specify both --camera-size and -m/--max-size");
LOGE("Could not specify both --camera-size and -m/--max-size");
return false;
}
if (opts->camera_ar) {
LOGE("Cannot specify both --camera-size and --camera-ar");
LOGE("Could not specify both --camera-size and --camera-ar");
return false;
}
}
@@ -3087,19 +3080,19 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
if (!opts->control) {
if (opts->turn_screen_off) {
LOGE("Cannot request to turn screen off if control is disabled");
LOGE("Could not request to turn screen off if control is disabled");
return false;
}
if (opts->stay_awake) {
LOGE("Cannot request to stay awake if control is disabled");
LOGE("Could not request to stay awake if control is disabled");
return false;
}
if (opts->show_touches) {
LOGE("Cannot request to show touches if control is disabled");
LOGE("Could not request to show touches if control is disabled");
return false;
}
if (opts->power_off_on_close) {
LOGE("Cannot request power off on close if control is disabled");
LOGE("Could not request power off on close if control is disabled");
return false;
}
}
@@ -3124,7 +3117,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
// OTG mode is compatible with only very few options.
// Only report obvious errors.
if (opts->record_filename) {
LOGE("OTG mode: cannot record");
LOGE("OTG mode: could not record");
return false;
}
if (opts->turn_screen_off) {

View File

@@ -83,34 +83,15 @@ write_position(uint8_t *buf, const struct sc_position *position) {
sc_write16be(&buf[10], position->screen_size.height);
}
// Write truncated string, and return the size
// write length (4 bytes) + string (non null-terminated)
static size_t
write_string_payload(uint8_t *payload, const char *utf8, size_t max_len) {
if (!utf8) {
return 0;
}
write_string(const char *utf8, size_t max_len, uint8_t *buf) {
size_t len = sc_str_utf8_truncation_index(utf8, max_len);
memcpy(payload, utf8, len);
return len;
}
// Write length (4 bytes) + string (non null-terminated)
static size_t
write_string(uint8_t *buf, const char *utf8, size_t max_len) {
size_t len = write_string_payload(buf + 4, utf8, max_len);
sc_write32be(buf, len);
memcpy(&buf[4], utf8, len);
return 4 + len;
}
// Write length (1 byte) + string (non null-terminated)
static size_t
write_string_tiny(uint8_t *buf, const char *utf8, size_t max_len) {
assert(max_len <= 0xFF);
size_t len = write_string_payload(buf + 1, utf8, max_len);
buf[0] = len;
return 1 + len;
}
size_t
sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
buf[0] = msg->type;
@@ -122,8 +103,9 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
sc_write32be(&buf[10], msg->inject_keycode.metastate);
return 14;
case SC_CONTROL_MSG_TYPE_INJECT_TEXT: {
size_t len = write_string(&buf[1], msg->inject_text.text,
SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
size_t len =
write_string(msg->inject_text.text,
SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH, &buf[1]);
return 1 + len;
}
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
@@ -155,26 +137,19 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
sc_write64be(&buf[1], msg->set_clipboard.sequence);
buf[9] = !!msg->set_clipboard.paste;
size_t len = write_string(&buf[10], msg->set_clipboard.text,
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
size_t len = write_string(msg->set_clipboard.text,
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
&buf[10]);
return 10 + len;
case SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_screen_power_mode.mode;
return 2;
case SC_CONTROL_MSG_TYPE_UHID_CREATE:
sc_write16be(&buf[1], msg->uhid_create.id);
size_t index = 3;
index += write_string_tiny(&buf[index], msg->uhid_create.name, 127);
sc_write16be(&buf[index], msg->uhid_create.report_desc_size);
index += 2;
memcpy(&buf[index], msg->uhid_create.report_desc,
msg->uhid_create.report_desc_size);
index += msg->uhid_create.report_desc_size;
return index;
sc_write16be(&buf[3], msg->uhid_create.report_desc_size);
memcpy(&buf[5], msg->uhid_create.report_desc,
msg->uhid_create.report_desc_size);
return 5 + msg->uhid_create.report_desc_size;
case SC_CONTROL_MSG_TYPE_UHID_INPUT:
sc_write16be(&buf[1], msg->uhid_input.id);
sc_write16be(&buf[3], msg->uhid_input.size);
@@ -280,15 +255,10 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
LOG_CMSG("rotate device");
break;
case SC_CONTROL_MSG_TYPE_UHID_CREATE: {
// 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);
case SC_CONTROL_MSG_TYPE_UHID_CREATE:
LOG_CMSG("UHID create [%" PRIu16 "] report_desc_size=%" PRIu16,
msg->uhid_create.id, msg->uhid_create.report_desc_size);
break;
}
case SC_CONTROL_MSG_TYPE_UHID_INPUT: {
char *hex = sc_str_to_hex_string(msg->uhid_input.data,
msg->uhid_input.size);

View File

@@ -98,7 +98,6 @@ struct sc_control_msg {
} set_screen_power_mode;
struct {
uint16_t id;
const char *name; // pointer to static data
uint16_t report_desc_size;
const uint8_t *report_desc; // pointer to static data
} uhid_create;

View File

@@ -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;
};

View File

@@ -90,7 +90,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
// Usage Minimum (1)
0x19, 0x01,
// Usage Maximum (16)
0x29, 0x10,
0x29, 0x0F,
// Logical Minimum (0)
0x15, 0x00,
// Logical Maximum (1)
@@ -244,14 +244,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);

View File

@@ -9,7 +9,7 @@
#include "input_events.h"
#define SC_MAX_GAMEPADS 8
#define SC_HID_ID_GAMEPAD_FIRST 3
#define SC_HID_ID_GAMEPAD_FIRST 2
#define SC_HID_ID_GAMEPAD_LAST (SC_HID_ID_GAMEPAD_FIRST + SC_MAX_GAMEPADS - 1)
struct sc_hid_gamepad_slot {

View File

@@ -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 = "Keyboard";
hid_open->report_desc = SC_HID_KEYBOARD_REPORT_DESC;
hid_open->report_desc_size = sizeof(SC_HID_KEYBOARD_REPORT_DESC);
}

View File

@@ -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 = "Mouse";
hid_open->report_desc = SC_HID_MOUSE_REPORT_DESC;
hid_open->report_desc_size = sizeof(SC_HID_MOUSE_REPORT_DESC);
}

View File

@@ -756,7 +756,6 @@ aoa_complete:
if (options->gamepad_input_mode == SC_GAMEPAD_INPUT_MODE_UHID) {
sc_gamepad_uhid_init(&s->gamepad_uhid, &s->controller);
gp = &s->gamepad_uhid.gamepad_processor;
uhid_gamepad = &s->gamepad_uhid;
}
struct sc_uhid_devices *uhid_devices = NULL;

View File

@@ -31,7 +31,6 @@ 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.name = hid_open->name;
msg.uhid_create.report_desc = hid_open->report_desc;
msg.uhid_create.report_desc_size = hid_open->report_desc_size;

View File

@@ -141,7 +141,6 @@ 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.name = hid_open.name;
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)) {

View File

@@ -81,7 +81,6 @@ 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.name = hid_open.name;
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)) {

View File

@@ -329,7 +329,6 @@ static void test_serialize_uhid_create(void) {
.type = SC_CONTROL_MSG_TYPE_UHID_CREATE,
.uhid_create = {
.id = 42,
.name = "ABC",
.report_desc_size = sizeof(report_desc),
.report_desc = report_desc,
},
@@ -337,14 +336,12 @@ 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 == 16);
const uint8_t expected[] = {
SC_CONTROL_MSG_TYPE_UHID_CREATE,
0, 42, // id
3, // name size
65, 66, 67, // "ABC"
0, 11, // report desc size
0, 11, // size
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
};
assert(!memcmp(buf, expected, sizeof(expected)));

View File

@@ -131,11 +131,10 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createUhidCreate(int id, String name, byte[] reportDesc) {
public static ControlMessage createUhidCreate(int id, byte[] reportDesc) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_UHID_CREATE;
msg.id = id;
msg.text = name;
msg.data = reportDesc;
return msg;
}

View File

@@ -75,12 +75,6 @@ public class ControlMessageReader {
return value;
}
private String parseString(int sizeBytes) throws IOException {
assert sizeBytes > 0 && sizeBytes <= 4;
byte[] data = parseByteArray(sizeBytes);
return new String(data, StandardCharsets.UTF_8);
}
private String parseString() throws IOException {
byte[] data = parseByteArray(4);
return new String(data, StandardCharsets.UTF_8);
@@ -140,9 +134,8 @@ public class ControlMessageReader {
private ControlMessage parseUhidCreate() throws IOException {
int id = dis.readUnsignedShort();
String name = parseString(1);
byte[] data = parseByteArray(2);
return ControlMessage.createUhidCreate(id, name, data);
return ControlMessage.createUhidCreate(id, data);
}
private ControlMessage parseUhidInput() throws IOException {

View File

@@ -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.getData());
break;
case ControlMessage.TYPE_UHID_INPUT:
getUhidManager().writeInput(msg.getId(), msg.getData());

View File

@@ -1,7 +1,6 @@
package com.genymobile.scrcpy.control;
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.StringUtils;
import android.os.Build;
import android.os.HandlerThread;
@@ -47,7 +46,7 @@ public final class UhidManager {
}
}
public void open(int id, String name, byte[] reportDesc) throws IOException {
public void open(int id, byte[] reportDesc) throws IOException {
try {
FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0);
try {
@@ -57,7 +56,7 @@ public final class UhidManager {
close(old);
}
byte[] req = buildUhidCreate2Req(name, reportDesc);
byte[] req = buildUhidCreate2Req(reportDesc);
Os.write(fd, req, 0, req.length);
registerUhidListener(id, fd);
@@ -147,7 +146,7 @@ public final class UhidManager {
}
}
private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) {
private static byte[] buildUhidCreate2Req(byte[] reportDesc) {
/*
* struct uhid_event {
* uint32_t type;
@@ -172,19 +171,8 @@ public final class UhidManager {
byte[] empty = new byte[256];
ByteBuffer buf = ByteBuffer.allocate(280 + reportDesc.length).order(ByteOrder.nativeOrder());
buf.putInt(UHID_CREATE2);
final String prefix = "scrcpy: ";
if (name.isEmpty()) {
name = "(no name)";
}
byte[] utf8Name = name.getBytes(StandardCharsets.UTF_8);
int len = StringUtils.getUtf8TruncationIndex(utf8Name, 127 - prefix.length());
int nameLen = prefix.length() + len;
assert nameLen <= 127;
buf.put(prefix.getBytes(StandardCharsets.US_ASCII));
buf.put(utf8Name, 0, len);
buf.put(empty, 0, 256 - nameLen);
buf.put("scrcpy".getBytes(StandardCharsets.US_ASCII));
buf.put(empty, 0, 256 - "scrcpy".length());
buf.putShort((short) reportDesc.length);
buf.putShort(BUS_VIRTUAL);
buf.putInt(0); // vendor id

View File

@@ -324,10 +324,8 @@ public class ControlMessageReaderTest {
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_UHID_CREATE);
dos.writeShort(42); // id
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};
dos.writeShort(data.length); // report desc size
dos.writeShort(data.length); // size
dos.write(data);
byte[] packet = bos.toByteArray();
@@ -337,7 +335,6 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.read();
Assert.assertEquals(ControlMessage.TYPE_UHID_CREATE, event.getType());
Assert.assertEquals(42, event.getId());
Assert.assertEquals("ABC", event.getText());
Assert.assertArrayEquals(data, event.getData());
Assert.assertEquals(-1, bis.read()); // EOS