mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-02-04 13:34:26 +01:00
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 all further UHID_INPUT messages invalid. Therefore, mark these messages as non-droppable. A non-droppable event is queued anyway (resizing the queue if necessary, unless the allocation fails). PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
302 lines
11 KiB
C
302 lines
11 KiB
C
#include "control_msg.h"
|
|
|
|
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "util/binary.h"
|
|
#include "util/log.h"
|
|
#include "util/str.h"
|
|
|
|
/**
|
|
* Map an enum value to a string based on an array, without crashing on an
|
|
* out-of-bounds index.
|
|
*/
|
|
#define ENUM_TO_LABEL(labels, value) \
|
|
((size_t) (value) < ARRAY_LEN(labels) ? labels[value] : "???")
|
|
|
|
#define KEYEVENT_ACTION_LABEL(value) \
|
|
ENUM_TO_LABEL(android_keyevent_action_labels, value)
|
|
|
|
#define MOTIONEVENT_ACTION_LABEL(value) \
|
|
ENUM_TO_LABEL(android_motionevent_action_labels, value)
|
|
|
|
#define SCREEN_POWER_MODE_LABEL(value) \
|
|
ENUM_TO_LABEL(screen_power_mode_labels, value)
|
|
|
|
static const char *const android_keyevent_action_labels[] = {
|
|
"down",
|
|
"up",
|
|
"multi",
|
|
};
|
|
|
|
static const char *const android_motionevent_action_labels[] = {
|
|
"down",
|
|
"up",
|
|
"move",
|
|
"cancel",
|
|
"outside",
|
|
"pointer-down",
|
|
"pointer-up",
|
|
"hover-move",
|
|
"scroll",
|
|
"hover-enter",
|
|
"hover-exit",
|
|
"btn-press",
|
|
"btn-release",
|
|
};
|
|
|
|
static const char *const screen_power_mode_labels[] = {
|
|
"off",
|
|
"doze",
|
|
"normal",
|
|
"doze-suspend",
|
|
"suspend",
|
|
};
|
|
|
|
static const char *const copy_key_labels[] = {
|
|
"none",
|
|
"copy",
|
|
"cut",
|
|
};
|
|
|
|
static inline const char *
|
|
get_well_known_pointer_id_name(uint64_t pointer_id) {
|
|
switch (pointer_id) {
|
|
case SC_POINTER_ID_MOUSE:
|
|
return "mouse";
|
|
case SC_POINTER_ID_GENERIC_FINGER:
|
|
return "finger";
|
|
case SC_POINTER_ID_VIRTUAL_FINGER:
|
|
return "vfinger";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_position(uint8_t *buf, const struct sc_position *position) {
|
|
sc_write32be(&buf[0], position->point.x);
|
|
sc_write32be(&buf[4], position->point.y);
|
|
sc_write16be(&buf[8], position->screen_size.width);
|
|
sc_write16be(&buf[10], position->screen_size.height);
|
|
}
|
|
|
|
// write length (4 bytes) + string (non null-terminated)
|
|
static size_t
|
|
write_string(const char *utf8, size_t max_len, uint8_t *buf) {
|
|
size_t len = sc_str_utf8_truncation_index(utf8, max_len);
|
|
sc_write32be(buf, len);
|
|
memcpy(&buf[4], utf8, len);
|
|
return 4 + len;
|
|
}
|
|
|
|
size_t
|
|
sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
|
|
buf[0] = msg->type;
|
|
switch (msg->type) {
|
|
case SC_CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
|
buf[1] = msg->inject_keycode.action;
|
|
sc_write32be(&buf[2], msg->inject_keycode.keycode);
|
|
sc_write32be(&buf[6], msg->inject_keycode.repeat);
|
|
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]);
|
|
return 1 + len;
|
|
}
|
|
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
|
buf[1] = msg->inject_touch_event.action;
|
|
sc_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
|
write_position(&buf[10], &msg->inject_touch_event.position);
|
|
uint16_t pressure =
|
|
sc_float_to_u16fp(msg->inject_touch_event.pressure);
|
|
sc_write16be(&buf[22], pressure);
|
|
sc_write32be(&buf[24], msg->inject_touch_event.action_button);
|
|
sc_write32be(&buf[28], msg->inject_touch_event.buttons);
|
|
return 32;
|
|
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
|
write_position(&buf[1], &msg->inject_scroll_event.position);
|
|
int16_t hscroll =
|
|
sc_float_to_i16fp(msg->inject_scroll_event.hscroll);
|
|
int16_t vscroll =
|
|
sc_float_to_i16fp(msg->inject_scroll_event.vscroll);
|
|
sc_write16be(&buf[13], (uint16_t) hscroll);
|
|
sc_write16be(&buf[15], (uint16_t) vscroll);
|
|
sc_write32be(&buf[17], msg->inject_scroll_event.buttons);
|
|
return 21;
|
|
case SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
|
buf[1] = msg->inject_keycode.action;
|
|
return 2;
|
|
case SC_CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
|
buf[1] = msg->get_clipboard.copy_key;
|
|
return 2;
|
|
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]);
|
|
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;
|
|
case SC_CONTROL_MSG_TYPE_UHID_INPUT:
|
|
sc_write16be(&buf[1], msg->uhid_input.id);
|
|
sc_write16be(&buf[3], msg->uhid_input.size);
|
|
memcpy(&buf[5], msg->uhid_input.data, msg->uhid_input.size);
|
|
return 5 + msg->uhid_input.size;
|
|
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
|
case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
|
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
|
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
|
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
|
// no additional data
|
|
return 1;
|
|
default:
|
|
LOGW("Unknown message type: %u", (unsigned) msg->type);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
sc_control_msg_log(const struct sc_control_msg *msg) {
|
|
#define LOG_CMSG(fmt, ...) LOGV("input: " fmt, ## __VA_ARGS__)
|
|
switch (msg->type) {
|
|
case SC_CONTROL_MSG_TYPE_INJECT_KEYCODE:
|
|
LOG_CMSG("key %-4s code=%d repeat=%" PRIu32 " meta=%06lx",
|
|
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action),
|
|
(int) msg->inject_keycode.keycode,
|
|
msg->inject_keycode.repeat,
|
|
(long) msg->inject_keycode.metastate);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_INJECT_TEXT:
|
|
LOG_CMSG("text \"%s\"", msg->inject_text.text);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT: {
|
|
int action = msg->inject_touch_event.action
|
|
& AMOTION_EVENT_ACTION_MASK;
|
|
uint64_t id = msg->inject_touch_event.pointer_id;
|
|
const char *pointer_name = get_well_known_pointer_id_name(id);
|
|
if (pointer_name) {
|
|
// string pointer id
|
|
LOG_CMSG("touch [id=%s] %-4s position=%" PRIi32 ",%" PRIi32
|
|
" pressure=%f action_button=%06lx buttons=%06lx",
|
|
pointer_name,
|
|
MOTIONEVENT_ACTION_LABEL(action),
|
|
msg->inject_touch_event.position.point.x,
|
|
msg->inject_touch_event.position.point.y,
|
|
msg->inject_touch_event.pressure,
|
|
(long) msg->inject_touch_event.action_button,
|
|
(long) msg->inject_touch_event.buttons);
|
|
} else {
|
|
// numeric pointer id
|
|
LOG_CMSG("touch [id=%" PRIu64_ "] %-4s position=%" PRIi32 ",%"
|
|
PRIi32 " pressure=%f action_button=%06lx"
|
|
" buttons=%06lx",
|
|
id,
|
|
MOTIONEVENT_ACTION_LABEL(action),
|
|
msg->inject_touch_event.position.point.x,
|
|
msg->inject_touch_event.position.point.y,
|
|
msg->inject_touch_event.pressure,
|
|
(long) msg->inject_touch_event.action_button,
|
|
(long) msg->inject_touch_event.buttons);
|
|
}
|
|
break;
|
|
}
|
|
case SC_CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
|
LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%f"
|
|
" vscroll=%f buttons=%06lx",
|
|
msg->inject_scroll_event.position.point.x,
|
|
msg->inject_scroll_event.position.point.y,
|
|
msg->inject_scroll_event.hscroll,
|
|
msg->inject_scroll_event.vscroll,
|
|
(long) msg->inject_scroll_event.buttons);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
|
|
LOG_CMSG("back-or-screen-on %s",
|
|
KEYEVENT_ACTION_LABEL(msg->inject_keycode.action));
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_GET_CLIPBOARD:
|
|
LOG_CMSG("get clipboard copy_key=%s",
|
|
copy_key_labels[msg->get_clipboard.copy_key]);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
|
LOG_CMSG("clipboard %" PRIu64_ " %s \"%s\"",
|
|
msg->set_clipboard.sequence,
|
|
msg->set_clipboard.paste ? "paste" : "nopaste",
|
|
msg->set_clipboard.text);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
|
|
LOG_CMSG("power mode %s",
|
|
SCREEN_POWER_MODE_LABEL(msg->set_screen_power_mode.mode));
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
|
LOG_CMSG("expand notification panel");
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
|
LOG_CMSG("expand settings panel");
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
|
LOG_CMSG("collapse panels");
|
|
break;
|
|
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);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_UHID_INPUT: {
|
|
char *hex = sc_str_to_hex_string(msg->uhid_input.data,
|
|
msg->uhid_input.size);
|
|
if (hex) {
|
|
LOG_CMSG("UHID input [%" PRIu16 "] %s",
|
|
msg->uhid_input.id, hex);
|
|
free(hex);
|
|
} else {
|
|
LOG_CMSG("UHID input [%" PRIu16 "] size=%" PRIu16,
|
|
msg->uhid_input.id, msg->uhid_input.size);
|
|
}
|
|
break;
|
|
}
|
|
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
|
LOG_CMSG("open hard keyboard settings");
|
|
break;
|
|
default:
|
|
LOG_CMSG("unknown type: %u", (unsigned) msg->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool
|
|
sc_control_msg_is_droppable(const struct sc_control_msg *msg) {
|
|
// Cannot drop UHID_CREATE messages, because it would cause all further
|
|
// UHID_INPUT messages for this device to be invalid
|
|
return msg->type != SC_CONTROL_MSG_TYPE_UHID_CREATE;
|
|
}
|
|
|
|
void
|
|
sc_control_msg_destroy(struct sc_control_msg *msg) {
|
|
switch (msg->type) {
|
|
case SC_CONTROL_MSG_TYPE_INJECT_TEXT:
|
|
free(msg->inject_text.text);
|
|
break;
|
|
case SC_CONTROL_MSG_TYPE_SET_CLIPBOARD:
|
|
free(msg->set_clipboard.text);
|
|
break;
|
|
default:
|
|
// do nothing
|
|
break;
|
|
}
|
|
}
|