Skip to content

Commit

Permalink
Mention physical gamepad names for UHID devices
Browse files Browse the repository at this point in the history
Initialize UHID devices with a custom name:
 - "scrcpy: $GAMEPAD_NAME" for gamepads
 - "scrcpy" for keyboard and mouse (or if no gamepad name is available)

The name may appear in Android apps.
  • Loading branch information
rom1v committed Sep 9, 2024
1 parent 7f6adc0 commit 32091f0
Show file tree
Hide file tree
Showing 15 changed files with 87 additions and 21 deletions.
52 changes: 42 additions & 10 deletions app/src/control_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(uint8_t *buf, const char *utf8, size_t max_len) {
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;
Expand Down Expand Up @@ -144,10 +163,18 @@ 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.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);
Expand Down Expand Up @@ -253,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);
Expand Down
1 change: 1 addition & 0 deletions app/src/control_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions app/src/hid/hid_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
6 changes: 6 additions & 0 deletions app/src/hid/hid_gamepad.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,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);

Expand Down
1 change: 1 addition & 0 deletions app/src/hid/hid_keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 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);
}
Expand Down
1 change: 1 addition & 0 deletions app/src/hid/hid_mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 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);
}
Expand Down
1 change: 1 addition & 0 deletions app/src/uhid/gamepad_uhid.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,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;

Expand Down
1 change: 1 addition & 0 deletions app/src/uhid/keyboard_uhid.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,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)) {
Expand Down
1 change: 1 addition & 0 deletions app/src/uhid/mouse_uhid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
7 changes: 5 additions & 2 deletions app/tests/test_control_msg_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,19 +329,22 @@ 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,
},
};

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)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ public static ControlMessage createEmpty(int type) {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ private int parseBufferLength(int sizeBytes) throws IOException {
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);
Expand Down Expand Up @@ -134,8 +140,9 @@ private ControlMessage parseSetScreenPowerMode() throws IOException {

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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ private boolean handleEvent() throws IOException {
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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -46,7 +47,7 @@ public UhidManager(DeviceMessageSender sender) {
}
}

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 {
Expand All @@ -56,7 +57,7 @@ public void open(int id, byte[] reportDesc) throws IOException {
close(old);
}

byte[] req = buildUhidCreate2Req(reportDesc);
byte[] req = buildUhidCreate2Req(name, reportDesc);
Os.write(fd, req, 0, req.length);

registerUhidListener(id, fd);
Expand Down Expand Up @@ -146,7 +147,7 @@ public void writeInput(int id, byte[] data) throws IOException {
}
}

private static byte[] buildUhidCreate2Req(byte[] reportDesc) {
private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) {
/*
* struct uhid_event {
* uint32_t type;
Expand All @@ -171,8 +172,14 @@ private static byte[] buildUhidCreate2Req(byte[] reportDesc) {
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());

String actualName = name.isEmpty() ? "scrcpy" : "scrcpy: " + name;
byte[] utf8Name = actualName.getBytes(StandardCharsets.UTF_8);
int len = StringUtils.getUtf8TruncationIndex(utf8Name, 127);
assert len <= 127;
buf.put(utf8Name, 0, len);
buf.put(empty, 0, 256 - len);

buf.putShort((short) reportDesc.length);
buf.putShort(BUS_VIRTUAL);
buf.putInt(0); // vendor id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,10 @@ public void testParseUhidCreate() throws IOException {
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();

Expand All @@ -335,6 +337,7 @@ public void testParseUhidCreate() throws IOException {
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
Expand Down

0 comments on commit 32091f0

Please sign in to comment.