mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-03-18 01:54:26 +01:00
Compare commits
39 Commits
gamepad.dr
...
gamepad.dr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b587014ccf | ||
|
|
890ee4d893 | ||
|
|
2e79f4b9ef | ||
|
|
676a83610d | ||
|
|
f151992e08 | ||
|
|
54b6fa5f6e | ||
|
|
2d32cbc716 | ||
|
|
29105b240b | ||
|
|
d14cc19c91 | ||
|
|
385b31fb06 | ||
|
|
c4b6bb312c | ||
|
|
abec04e8e7 | ||
|
|
0ea20f90be | ||
|
|
a5ffe5b060 | ||
|
|
6f1d79ba17 | ||
|
|
44e29989ee | ||
|
|
00786942be | ||
|
|
e6017cdc5d | ||
|
|
8fb87b5e6b | ||
|
|
24f7ea5894 | ||
|
|
dbdfd9c8bf | ||
|
|
d58eb616f0 | ||
|
|
230e6b4079 | ||
|
|
3bd07aa8ff | ||
|
|
28c91ecba2 | ||
|
|
9ebb836b20 | ||
|
|
1559940cee | ||
|
|
0cc1a855dc | ||
|
|
b49064064c | ||
|
|
ba342c398d | ||
|
|
d7fe119c8e | ||
|
|
c5ccae5538 | ||
|
|
87d9d68c07 | ||
|
|
684e2b632e | ||
|
|
8598e7d7a8 | ||
|
|
8b372ae809 | ||
|
|
28512d3872 | ||
|
|
c11f07c1e8 | ||
|
|
903a5aaaf5 |
@@ -26,6 +26,8 @@ _scrcpy() {
|
||||
-e --select-tcpip
|
||||
-f --fullscreen
|
||||
--force-adb-forward
|
||||
-G
|
||||
--gamepad=
|
||||
-h --help
|
||||
-K
|
||||
--keyboard=
|
||||
@@ -127,6 +129,10 @@ _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
|
||||
|
||||
@@ -33,8 +33,10 @@ 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 keyboard (same as --keyboard=uhid)]'
|
||||
'-K[Use UHID/AOA keyboard (same as --keyboard=uhid or --keyboard=aoa, depending on OTG mode)]'
|
||||
'--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]'
|
||||
@@ -44,7 +46,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 mouse (same as --mouse=uhid)]'
|
||||
'-M[Use UHID/AOA mouse (same as --mouse=uhid or --mouse=aoa, depending on OTG mode)]'
|
||||
'--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]'
|
||||
|
||||
@@ -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_SDK;
|
||||
: SC_MOUSE_INPUT_MODE_UHID;
|
||||
} 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,9 +2885,16 @@ 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) {
|
||||
LOGE("Could not disable both keyboard and mouse in OTG mode.");
|
||||
&& mmode == SC_MOUSE_INPUT_MODE_DISABLED
|
||||
&& gmode == SC_GAMEPAD_INPUT_MODE_DISABLED) {
|
||||
LOGE("Cannot not disable all inputs in OTG mode.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2928,18 +2935,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("Could not specify both --camera-id and --camera-facing");
|
||||
LOGE("Cannot specify both --camera-id and --camera-facing");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts->camera_size) {
|
||||
if (opts->max_size) {
|
||||
LOGE("Could not specify both --camera-size and -m/--max-size");
|
||||
LOGE("Cannot specify both --camera-size and -m/--max-size");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts->camera_ar) {
|
||||
LOGE("Could not specify both --camera-size and --camera-ar");
|
||||
LOGE("Cannot specify both --camera-size and --camera-ar");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3080,19 +3087,19 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
|
||||
if (!opts->control) {
|
||||
if (opts->turn_screen_off) {
|
||||
LOGE("Could not request to turn screen off if control is disabled");
|
||||
LOGE("Cannot request to turn screen off if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (opts->stay_awake) {
|
||||
LOGE("Could not request to stay awake if control is disabled");
|
||||
LOGE("Cannot request to stay awake if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (opts->show_touches) {
|
||||
LOGE("Could not request to show touches if control is disabled");
|
||||
LOGE("Cannot request to show touches if control is disabled");
|
||||
return false;
|
||||
}
|
||||
if (opts->power_off_on_close) {
|
||||
LOGE("Could not request power off on close if control is disabled");
|
||||
LOGE("Cannot request power off on close if control is disabled");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3117,7 +3124,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: could not record");
|
||||
LOGE("OTG mode: cannot record");
|
||||
return false;
|
||||
}
|
||||
if (opts->turn_screen_off) {
|
||||
|
||||
@@ -83,15 +83,34 @@ write_position(uint8_t *buf, const struct sc_position *position) {
|
||||
sc_write16be(&buf[10], position->screen_size.height);
|
||||
}
|
||||
|
||||
// write length (4 bytes) + string (non null-terminated)
|
||||
// Write truncated string, and return the size
|
||||
static size_t
|
||||
write_string(const char *utf8, size_t max_len, uint8_t *buf) {
|
||||
write_string_payload(uint8_t *payload, const char *utf8, size_t max_len) {
|
||||
if (!utf8) {
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
@@ -103,9 +122,8 @@ 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(msg->inject_text.text,
|
||||
SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH, &buf[1]);
|
||||
size_t len = write_string(&buf[1], msg->inject_text.text,
|
||||
SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH);
|
||||
return 1 + len;
|
||||
}
|
||||
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
||||
@@ -137,19 +155,26 @@ 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(msg->set_clipboard.text,
|
||||
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
|
||||
&buf[10]);
|
||||
size_t len = write_string(&buf[10], msg->set_clipboard.text,
|
||||
SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
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);
|
||||
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;
|
||||
|
||||
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;
|
||||
case SC_CONTROL_MSG_TYPE_UHID_INPUT:
|
||||
sc_write16be(&buf[1], msg->uhid_input.id);
|
||||
sc_write16be(&buf[3], msg->uhid_input.size);
|
||||
@@ -255,10 +280,15 @@ 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:
|
||||
LOG_CMSG("UHID create [%" PRIu16 "] report_desc_size=%" PRIu16,
|
||||
msg->uhid_create.id, msg->uhid_create.report_desc_size);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case SC_CONTROL_MSG_TYPE_UHID_INPUT: {
|
||||
char *hex = sc_str_to_hex_string(msg->uhid_input.data,
|
||||
msg->uhid_input.size);
|
||||
|
||||
@@ -98,6 +98,7 @@ 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;
|
||||
|
||||
@@ -15,6 +15,7 @@ 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;
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
|
||||
// Usage Minimum (1)
|
||||
0x19, 0x01,
|
||||
// Usage Maximum (16)
|
||||
0x29, 0x0F,
|
||||
0x29, 0x10,
|
||||
// Logical Minimum (0)
|
||||
0x15, 0x00,
|
||||
// Logical Maximum (1)
|
||||
@@ -244,8 +244,14 @@ 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);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "input_events.h"
|
||||
|
||||
#define SC_MAX_GAMEPADS 8
|
||||
#define SC_HID_ID_GAMEPAD_FIRST 2
|
||||
#define SC_HID_ID_GAMEPAD_FIRST 3
|
||||
#define SC_HID_ID_GAMEPAD_LAST (SC_HID_ID_GAMEPAD_FIRST + SC_MAX_GAMEPADS - 1)
|
||||
|
||||
struct sc_hid_gamepad_slot {
|
||||
|
||||
@@ -335,6 +335,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -190,6 +190,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -756,6 +756,7 @@ 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;
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ 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)) {
|
||||
|
||||
@@ -81,6 +81,7 @@ 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)) {
|
||||
|
||||
@@ -329,6 +329,7 @@ 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,
|
||||
},
|
||||
@@ -336,12 +337,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 == 16);
|
||||
assert(size == 20);
|
||||
|
||||
const uint8_t expected[] = {
|
||||
SC_CONTROL_MSG_TYPE_UHID_CREATE,
|
||||
0, 42, // id
|
||||
0, 11, // size
|
||||
3, // name size
|
||||
65, 66, 67, // "ABC"
|
||||
0, 11, // report desc size
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
};
|
||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||
|
||||
@@ -131,10 +131,11 @@ public final class ControlMessage {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createUhidCreate(int id, byte[] reportDesc) {
|
||||
public static ControlMessage createUhidCreate(int id, String name, byte[] reportDesc) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_UHID_CREATE;
|
||||
msg.id = id;
|
||||
msg.text = name;
|
||||
msg.data = reportDesc;
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,12 @@ 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);
|
||||
@@ -134,8 +140,9 @@ public class ControlMessageReader {
|
||||
|
||||
private ControlMessage parseUhidCreate() throws IOException {
|
||||
int id = dis.readUnsignedShort();
|
||||
String name = parseString(1);
|
||||
byte[] data = parseByteArray(2);
|
||||
return ControlMessage.createUhidCreate(id, data);
|
||||
return ControlMessage.createUhidCreate(id, name, data);
|
||||
}
|
||||
|
||||
private ControlMessage parseUhidInput() throws IOException {
|
||||
|
||||
@@ -210,7 +210,7 @@ public class Controller implements AsyncProcessor {
|
||||
device.rotateDevice();
|
||||
break;
|
||||
case ControlMessage.TYPE_UHID_CREATE:
|
||||
getUhidManager().open(msg.getId(), msg.getData());
|
||||
getUhidManager().open(msg.getId(), msg.getText(), msg.getData());
|
||||
break;
|
||||
case ControlMessage.TYPE_UHID_INPUT:
|
||||
getUhidManager().writeInput(msg.getId(), msg.getData());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -46,7 +47,7 @@ public final class UhidManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void open(int id, byte[] reportDesc) throws IOException {
|
||||
public void open(int id, String name, byte[] reportDesc) throws IOException {
|
||||
try {
|
||||
FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0);
|
||||
try {
|
||||
@@ -56,7 +57,7 @@ public final class UhidManager {
|
||||
close(old);
|
||||
}
|
||||
|
||||
byte[] req = buildUhidCreate2Req(reportDesc);
|
||||
byte[] req = buildUhidCreate2Req(name, reportDesc);
|
||||
Os.write(fd, req, 0, req.length);
|
||||
|
||||
registerUhidListener(id, fd);
|
||||
@@ -146,7 +147,7 @@ public final class UhidManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] buildUhidCreate2Req(byte[] reportDesc) {
|
||||
private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) {
|
||||
/*
|
||||
* struct uhid_event {
|
||||
* uint32_t type;
|
||||
@@ -171,8 +172,19 @@ public final class UhidManager {
|
||||
byte[] empty = new byte[256];
|
||||
ByteBuffer buf = ByteBuffer.allocate(280 + reportDesc.length).order(ByteOrder.nativeOrder());
|
||||
buf.putInt(UHID_CREATE2);
|
||||
buf.put("scrcpy".getBytes(StandardCharsets.US_ASCII));
|
||||
buf.put(empty, 0, 256 - "scrcpy".length());
|
||||
|
||||
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.putShort((short) reportDesc.length);
|
||||
buf.putShort(BUS_VIRTUAL);
|
||||
buf.putInt(0); // vendor id
|
||||
|
||||
@@ -324,8 +324,10 @@ 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); // size
|
||||
dos.writeShort(data.length); // report desc size
|
||||
dos.write(data);
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
@@ -335,6 +337,7 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user