mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-03-23 20:44:41 +01:00
Compare commits
40 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 | ||
|
|
21b412cd98 |
@@ -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]'
|
||||
|
||||
@@ -175,6 +175,10 @@ Start in fullscreen.
|
||||
.B \-\-force\-adb\-forward
|
||||
Do not attempt to use "adb reverse" to connect to the device.
|
||||
|
||||
.TP
|
||||
.B \-G
|
||||
Same as \fB\-\-gamepad=uhid\fR, or \fB\-\-keyboard=aoa\fR if \fB\-\-otg\fR is set.
|
||||
|
||||
.TP
|
||||
.BI "\-\-gamepad " mode
|
||||
Select how to send gamepad inputs to the device.
|
||||
@@ -192,7 +196,7 @@ Print this help.
|
||||
|
||||
.TP
|
||||
.B \-K
|
||||
Same as \fB\-\-keyboard=uhid\fR.
|
||||
Same as \fB\-\-keyboard=uhid\fR, or \fB\-\-keyboard=aoa\fR if \fB\-\-otg\fR is set.
|
||||
|
||||
.TP
|
||||
.BI "\-\-keyboard " mode
|
||||
@@ -257,7 +261,7 @@ Default is 0 (unlimited).
|
||||
|
||||
.TP
|
||||
.B \-M
|
||||
Same as \fB\-\-mouse=uhid\fR.
|
||||
Same as \fB\-\-mouse=uhid\fR, or \fB\-\-mouse=aoa\fR if \fB\-\-otg\fR is set.
|
||||
|
||||
.TP
|
||||
.BI "\-\-max\-fps " value
|
||||
|
||||
@@ -375,7 +375,7 @@ static const struct sc_option options[] = {
|
||||
},
|
||||
{
|
||||
.shortopt = 'G',
|
||||
.text = "Same as --gamepad=uhid.",
|
||||
.text = "Same as --gamepad=uhid, or --gamepad=aoa if --otg is set.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_GAMEPAD,
|
||||
@@ -397,7 +397,7 @@ static const struct sc_option options[] = {
|
||||
},
|
||||
{
|
||||
.shortopt = 'K',
|
||||
.text = "Same as --keyboard=uhid.",
|
||||
.text = "Same as --keyboard=uhid, or --keyboard=aoa if --otg is set.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_KEYBOARD,
|
||||
@@ -493,7 +493,7 @@ static const struct sc_option options[] = {
|
||||
},
|
||||
{
|
||||
.shortopt = 'M',
|
||||
.text = "Same as --mouse=uhid.",
|
||||
.text = "Same as --mouse=uhid, or --mouse=aoa if --otg is set.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_MAX_FPS,
|
||||
@@ -2264,7 +2264,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
args->help = true;
|
||||
break;
|
||||
case 'K':
|
||||
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_UHID;
|
||||
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_UHID_OR_AOA;
|
||||
break;
|
||||
case OPT_KEYBOARD:
|
||||
if (!parse_keyboard(optarg, &opts->keyboard_input_mode)) {
|
||||
@@ -2286,7 +2286,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID_OR_AOA;
|
||||
break;
|
||||
case OPT_MOUSE:
|
||||
if (!parse_mouse(optarg, &opts->mouse_input_mode)) {
|
||||
@@ -2671,7 +2671,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
opts->audio_dup = true;
|
||||
break;
|
||||
case 'G':
|
||||
opts->gamepad_input_mode = SC_GAMEPAD_INPUT_MODE_UHID;
|
||||
opts->gamepad_input_mode = SC_GAMEPAD_INPUT_MODE_UHID_OR_AOA;
|
||||
break;
|
||||
case OPT_GAMEPAD:
|
||||
if (!parse_gamepad(optarg, &opts->gamepad_input_mode)) {
|
||||
@@ -2795,7 +2795,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
|
||||
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
|
||||
: SC_KEYBOARD_INPUT_MODE_SDK;
|
||||
} else if (opts->keyboard_input_mode
|
||||
== SC_KEYBOARD_INPUT_MODE_UHID_OR_AOA) {
|
||||
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
|
||||
: SC_KEYBOARD_INPUT_MODE_UHID;
|
||||
}
|
||||
|
||||
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
|
||||
if (otg) {
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
|
||||
@@ -2805,19 +2810,24 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
} else {
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
|
||||
}
|
||||
} 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;
|
||||
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK
|
||||
&& !opts->video_playback) {
|
||||
LOGE("SDK mouse mode requires video playback. Try --mouse=uhid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts->gamepad_input_mode == SC_GAMEPAD_INPUT_MODE_AUTO) {
|
||||
if (otg) {
|
||||
opts->gamepad_input_mode = SC_GAMEPAD_INPUT_MODE_AOA;
|
||||
} else {
|
||||
// UHID does not work on all devices (with old Android
|
||||
// versions), so it cannot be enabled by default
|
||||
opts->gamepad_input_mode = SC_GAMEPAD_INPUT_MODE_DISABLED;
|
||||
}
|
||||
// UHID does not work on all devices (with old Android
|
||||
// versions), so it cannot be enabled by default
|
||||
opts->gamepad_input_mode = otg ? SC_GAMEPAD_INPUT_MODE_AOA
|
||||
: SC_GAMEPAD_INPUT_MODE_DISABLED;
|
||||
} else if (opts->gamepad_input_mode
|
||||
== SC_GAMEPAD_INPUT_MODE_UHID_OR_AOA) {
|
||||
opts->gamepad_input_mode = otg ? SC_GAMEPAD_INPUT_MODE_AOA
|
||||
: SC_GAMEPAD_INPUT_MODE_UHID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2875,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;
|
||||
}
|
||||
}
|
||||
@@ -2918,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;
|
||||
}
|
||||
}
|
||||
@@ -3070,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;
|
||||
}
|
||||
}
|
||||
@@ -3107,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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "events.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
bool
|
||||
sc_push_event_impl(uint32_t type, const char *name) {
|
||||
@@ -32,9 +33,34 @@ sc_post_to_main_thread(sc_runnable_fn run, void *userdata) {
|
||||
// ret == 0: event was filtered
|
||||
// ret == 1: success
|
||||
if (ret != 1) {
|
||||
LOGW("Coud not post to main thread: %s", SDL_GetError());
|
||||
if (ret == 0) {
|
||||
// if ret == 0, this is expected on exit, log in debug mode
|
||||
LOGD("Could not post runnable to main thread (filtered)");
|
||||
} else {
|
||||
assert(ret < 0);
|
||||
LOGW("Coud not post to main thread: %s", SDL_GetError());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int SDLCALL
|
||||
task_event_filter(void *userdata, SDL_Event *event) {
|
||||
(void) userdata;
|
||||
|
||||
if (event->type == SC_EVENT_RUN_ON_MAIN_THREAD) {
|
||||
// Reject this event type from now on
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
sc_reject_new_runnables(void) {
|
||||
assert(sc_thread_get_id() == SC_MAIN_THREAD_ID);
|
||||
|
||||
SDL_SetEventFilter(task_event_filter, NULL);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef SC_EVENTS
|
||||
#define SC_EVENTS
|
||||
#ifndef SC_EVENTS_H
|
||||
#define SC_EVENTS_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -32,4 +32,7 @@ typedef void (*sc_runnable_fn)(void *userdata);
|
||||
bool
|
||||
sc_post_to_main_thread(sc_runnable_fn run, void *userdata);
|
||||
|
||||
void
|
||||
sc_reject_new_runnables(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -76,8 +76,8 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
|
||||
0x09, 0xC4,
|
||||
// Logical Minimum (0)
|
||||
0x15, 0x00,
|
||||
// Logical Maximum (65535)
|
||||
0x27, 0xFF, 0xFF, 0x00, 0x00,
|
||||
// Logical Maximum (32767)
|
||||
0x26, 0xFF, 0x7F,
|
||||
// Report Size (16)
|
||||
0x75, 0x10,
|
||||
// Report Count (2)
|
||||
@@ -89,26 +89,19 @@ static const uint8_t SC_HID_GAMEPAD_REPORT_DESC[] = {
|
||||
0x05, 0x09,
|
||||
// Usage Minimum (1)
|
||||
0x19, 0x01,
|
||||
// Usage Maximum (15)
|
||||
0x29, 0x0E,
|
||||
// Usage Maximum (16)
|
||||
0x29, 0x10,
|
||||
// Logical Minimum (0)
|
||||
0x15, 0x00,
|
||||
// Logical Maximum (1)
|
||||
0x25, 0x01,
|
||||
// Report Count (15)
|
||||
0x95, 0x0F,
|
||||
// Report Count (16)
|
||||
0x95, 0x10,
|
||||
// Report Size (1)
|
||||
0x75, 0x01,
|
||||
// Input (Data, Variable, Absolute): 15 buttons bits
|
||||
// Input (Data, Variable, Absolute): 16 buttons bits
|
||||
0x81, 0x02,
|
||||
|
||||
// Report Count (1)
|
||||
0x95, 0x01,
|
||||
// Report Size (1)
|
||||
0x75, 0x01,
|
||||
// Input (Constant): 1-bit padding
|
||||
0x81, 0x01,
|
||||
|
||||
// Usage Page (Generic Desktop)
|
||||
0x05, 0x01,
|
||||
// Usage (Hat switch)
|
||||
@@ -251,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);
|
||||
|
||||
@@ -320,14 +319,13 @@ sc_hid_gamepad_event_from_slot(uint16_t hid_id,
|
||||
hid_input->size = SC_HID_GAMEPAD_EVENT_SIZE;
|
||||
|
||||
uint8_t *data = hid_input->data;
|
||||
// Buttons are individual bits in the report descriptor, so the resulting
|
||||
// u16 must be written in little-endian
|
||||
sc_write16be(data, slot->axis_left_x);
|
||||
sc_write16be(data + 2, slot->axis_left_y);
|
||||
sc_write16be(data + 4, slot->axis_right_x);
|
||||
sc_write16be(data + 6, slot->axis_right_y);
|
||||
sc_write16be(data + 8, slot->axis_left_trigger);
|
||||
sc_write16be(data + 10, slot->axis_right_trigger);
|
||||
// Values must be written in little-endian
|
||||
sc_write16le(data, slot->axis_left_x);
|
||||
sc_write16le(data + 2, slot->axis_left_y);
|
||||
sc_write16le(data + 4, slot->axis_right_x);
|
||||
sc_write16le(data + 6, slot->axis_right_y);
|
||||
sc_write16le(data + 8, slot->axis_left_trigger);
|
||||
sc_write16le(data + 10, slot->axis_right_trigger);
|
||||
sc_write16le(data + 12, slot->buttons & SC_HID_BUTTONS_MASK);
|
||||
data[14] = sc_hid_gamepad_get_dpad_value(slot->buttons);
|
||||
}
|
||||
@@ -406,6 +404,7 @@ sc_hid_gamepad_generate_input_from_button(struct sc_hid_gamepad *hid,
|
||||
|
||||
uint16_t hid_id = sc_hid_gamepad_slot_get_id(slot_idx);
|
||||
sc_hid_gamepad_event_from_slot(hid_id, slot, hid_input);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -425,26 +424,28 @@ 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]
|
||||
uint16_t value = ((int32_t) event->value) + 0x8000;
|
||||
// [-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 = value;
|
||||
slot->axis_left_x = AXIS_RESCALE(event->value);
|
||||
break;
|
||||
case SC_GAMEPAD_AXIS_LEFTY:
|
||||
slot->axis_left_y = value;
|
||||
slot->axis_left_y = AXIS_RESCALE(event->value);
|
||||
break;
|
||||
case SC_GAMEPAD_AXIS_RIGHTX:
|
||||
slot->axis_right_x = value;
|
||||
slot->axis_right_x = AXIS_RESCALE(event->value);
|
||||
break;
|
||||
case SC_GAMEPAD_AXIS_RIGHTY:
|
||||
slot->axis_right_y = value;
|
||||
slot->axis_right_y = AXIS_RESCALE(event->value);
|
||||
break;
|
||||
case SC_GAMEPAD_AXIS_LEFT_TRIGGER:
|
||||
slot->axis_left_trigger = value;
|
||||
// Trigger is always positive between 0 and 32767
|
||||
slot->axis_left_trigger = MAX(0, event->value);
|
||||
break;
|
||||
case SC_GAMEPAD_AXIS_RIGHT_TRIGGER:
|
||||
slot->axis_right_trigger = value;
|
||||
// Trigger is always positive between 0 and 32767
|
||||
slot->axis_right_trigger = MAX(0, event->value);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -452,5 +453,6 @@ sc_hid_gamepad_generate_input_from_axis(struct sc_hid_gamepad *hid,
|
||||
|
||||
uint16_t hid_id = sc_hid_gamepad_slot_get_id(slot_idx);
|
||||
sc_hid_gamepad_event_from_slot(hid_id, slot, hid_input);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ enum sc_lock_video_orientation {
|
||||
|
||||
enum sc_keyboard_input_mode {
|
||||
SC_KEYBOARD_INPUT_MODE_AUTO,
|
||||
SC_KEYBOARD_INPUT_MODE_UHID_OR_AOA, // normal vs otg mode
|
||||
SC_KEYBOARD_INPUT_MODE_DISABLED,
|
||||
SC_KEYBOARD_INPUT_MODE_SDK,
|
||||
SC_KEYBOARD_INPUT_MODE_UHID,
|
||||
@@ -150,6 +151,7 @@ enum sc_keyboard_input_mode {
|
||||
|
||||
enum sc_mouse_input_mode {
|
||||
SC_MOUSE_INPUT_MODE_AUTO,
|
||||
SC_MOUSE_INPUT_MODE_UHID_OR_AOA, // normal vs otg mode
|
||||
SC_MOUSE_INPUT_MODE_DISABLED,
|
||||
SC_MOUSE_INPUT_MODE_SDK,
|
||||
SC_MOUSE_INPUT_MODE_UHID,
|
||||
@@ -158,6 +160,7 @@ enum sc_mouse_input_mode {
|
||||
|
||||
enum sc_gamepad_input_mode {
|
||||
SC_GAMEPAD_INPUT_MODE_AUTO,
|
||||
SC_GAMEPAD_INPUT_MODE_UHID_OR_AOA, // normal vs otg mode
|
||||
SC_GAMEPAD_INPUT_MODE_DISABLED,
|
||||
SC_GAMEPAD_INPUT_MODE_UHID,
|
||||
SC_GAMEPAD_INPUT_MODE_AOA,
|
||||
|
||||
@@ -200,6 +200,21 @@ event_loop(struct scrcpy *s) {
|
||||
return SCRCPY_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static void
|
||||
terminate_event_loop(void) {
|
||||
sc_reject_new_runnables();
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SC_EVENT_RUN_ON_MAIN_THREAD) {
|
||||
// Make sure all posted runnables are run, to avoid memory leaks
|
||||
sc_runnable_fn run = event.user.data1;
|
||||
void *userdata = event.user.data2;
|
||||
run(userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true on success, false on error
|
||||
static bool
|
||||
await_for_server(bool *connected) {
|
||||
@@ -741,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;
|
||||
@@ -888,6 +904,7 @@ aoa_complete:
|
||||
}
|
||||
|
||||
ret = event_loop(s);
|
||||
terminate_event_loop();
|
||||
LOGD("quit...");
|
||||
|
||||
if (options->video_playback) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -83,7 +84,10 @@ sc_gamepad_processor_process_gamepad_axis(struct sc_gamepad_processor *gp,
|
||||
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
|
||||
|
||||
struct sc_hid_input hid_input;
|
||||
sc_hid_gamepad_generate_input_from_axis(&gamepad->hid, &hid_input, event);
|
||||
if (!sc_hid_gamepad_generate_input_from_axis(&gamepad->hid, &hid_input,
|
||||
event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_gamepad_uhid_send_input(gamepad, &hid_input, "gamepad axis");
|
||||
}
|
||||
@@ -94,7 +98,10 @@ sc_gamepad_processor_process_gamepad_button(struct sc_gamepad_processor *gp,
|
||||
struct sc_gamepad_uhid *gamepad = DOWNCAST(gp);
|
||||
|
||||
struct sc_hid_input hid_input;
|
||||
sc_hid_gamepad_generate_input_from_button(&gamepad->hid, &hid_input, event);
|
||||
if (!sc_hid_gamepad_generate_input_from_button(&gamepad->hid, &hid_input,
|
||||
event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_gamepad_uhid_send_input(gamepad, &hid_input, "gamepad button");
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@ sc_keyboard_uhid_synchronize_mod(struct sc_keyboard_uhid *kb) {
|
||||
kb->device_mod = mod;
|
||||
|
||||
struct sc_hid_input hid_input;
|
||||
sc_hid_keyboard_generate_input_from_mods(&hid_input, diff);
|
||||
if (!sc_hid_keyboard_generate_input_from_mods(&hid_input, diff)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGV("HID keyboard state synchronized");
|
||||
|
||||
@@ -139,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)) {
|
||||
|
||||
@@ -42,11 +42,6 @@ event_loop(struct scrcpy_otg *s) {
|
||||
case SDL_QUIT:
|
||||
LOGD("User requested to quit");
|
||||
return SCRCPY_EXIT_SUCCESS;
|
||||
case SC_EVENT_RUN_ON_MAIN_THREAD:
|
||||
sc_runnable_fn run = event.user.data1;
|
||||
void *userdata = event.user.data2;
|
||||
run(userdata);
|
||||
break;
|
||||
default:
|
||||
sc_screen_otg_handle_event(&s->screen_otg, &event);
|
||||
break;
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -3,31 +3,22 @@ package com.genymobile.scrcpy.control;
|
||||
import android.net.LocalSocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public final class ControlChannel {
|
||||
private final InputStream inputStream;
|
||||
private final OutputStream outputStream;
|
||||
|
||||
private final ControlMessageReader reader = new ControlMessageReader();
|
||||
private final DeviceMessageWriter writer = new DeviceMessageWriter();
|
||||
private final ControlMessageReader reader;
|
||||
private final DeviceMessageWriter writer;
|
||||
|
||||
public ControlChannel(LocalSocket controlSocket) throws IOException {
|
||||
this.inputStream = controlSocket.getInputStream();
|
||||
this.outputStream = controlSocket.getOutputStream();
|
||||
reader = new ControlMessageReader(controlSocket.getInputStream());
|
||||
writer = new DeviceMessageWriter(controlSocket.getOutputStream());
|
||||
}
|
||||
|
||||
public ControlMessage recv() throws IOException {
|
||||
ControlMessage msg = reader.next();
|
||||
while (msg == null) {
|
||||
reader.readFrom(inputStream);
|
||||
msg = reader.next();
|
||||
}
|
||||
return msg;
|
||||
return reader.read();
|
||||
}
|
||||
|
||||
public void send(DeviceMessage msg) throws IOException {
|
||||
writer.writeTo(msg, outputStream);
|
||||
writer.write(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,271 +1,166 @@
|
||||
package com.genymobile.scrcpy.control;
|
||||
|
||||
import com.genymobile.scrcpy.util.Binary;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.device.Position;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ControlMessageReader {
|
||||
|
||||
static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13;
|
||||
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 31;
|
||||
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
|
||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||
static final int GET_CLIPBOARD_LENGTH = 1;
|
||||
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 9;
|
||||
static final int UHID_CREATE_FIXED_PAYLOAD_LENGTH = 4;
|
||||
static final int UHID_INPUT_FIXED_PAYLOAD_LENGTH = 4;
|
||||
static final int UHID_DESTROY_PAYLOAD_LENGTH = 2;
|
||||
|
||||
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
|
||||
|
||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = MESSAGE_MAX_SIZE - 14; // type: 1 byte; sequence: 8 bytes; paste flag: 1 byte; length: 4 bytes
|
||||
public static final int INJECT_TEXT_MAX_LENGTH = 300;
|
||||
|
||||
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
|
||||
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
|
||||
private final DataInputStream dis;
|
||||
|
||||
public ControlMessageReader() {
|
||||
// invariant: the buffer is always in "get" mode
|
||||
buffer.limit(0);
|
||||
public ControlMessageReader(InputStream rawInputStream) {
|
||||
dis = new DataInputStream(new BufferedInputStream(rawInputStream));
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
return buffer.remaining() == rawBuffer.length;
|
||||
}
|
||||
|
||||
public void readFrom(InputStream input) throws IOException {
|
||||
if (isFull()) {
|
||||
throw new IllegalStateException("Buffer full, call next() to consume");
|
||||
}
|
||||
buffer.compact();
|
||||
int head = buffer.position();
|
||||
int r = input.read(rawBuffer, head, rawBuffer.length - head);
|
||||
if (r == -1) {
|
||||
throw new EOFException("Controller socket closed");
|
||||
}
|
||||
buffer.position(head + r);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public ControlMessage next() {
|
||||
if (!buffer.hasRemaining()) {
|
||||
return null;
|
||||
}
|
||||
int savedPosition = buffer.position();
|
||||
|
||||
int type = buffer.get();
|
||||
ControlMessage msg;
|
||||
public ControlMessage read() throws IOException {
|
||||
int type = dis.readUnsignedByte();
|
||||
switch (type) {
|
||||
case ControlMessage.TYPE_INJECT_KEYCODE:
|
||||
msg = parseInjectKeycode();
|
||||
break;
|
||||
return parseInjectKeycode();
|
||||
case ControlMessage.TYPE_INJECT_TEXT:
|
||||
msg = parseInjectText();
|
||||
break;
|
||||
return parseInjectText();
|
||||
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||
msg = parseInjectTouchEvent();
|
||||
break;
|
||||
return parseInjectTouchEvent();
|
||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||
msg = parseInjectScrollEvent();
|
||||
break;
|
||||
return parseInjectScrollEvent();
|
||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||
msg = parseBackOrScreenOnEvent();
|
||||
break;
|
||||
return parseBackOrScreenOnEvent();
|
||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||
msg = parseGetClipboard();
|
||||
break;
|
||||
return parseGetClipboard();
|
||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||
msg = parseSetClipboard();
|
||||
break;
|
||||
return parseSetClipboard();
|
||||
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||
msg = parseSetScreenPowerMode();
|
||||
break;
|
||||
return parseSetScreenPowerMode();
|
||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case ControlMessage.TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case ControlMessage.TYPE_COLLAPSE_PANELS:
|
||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||
msg = ControlMessage.createEmpty(type);
|
||||
break;
|
||||
return ControlMessage.createEmpty(type);
|
||||
case ControlMessage.TYPE_UHID_CREATE:
|
||||
msg = parseUhidCreate();
|
||||
break;
|
||||
return parseUhidCreate();
|
||||
case ControlMessage.TYPE_UHID_INPUT:
|
||||
msg = parseUhidInput();
|
||||
break;
|
||||
return parseUhidInput();
|
||||
case ControlMessage.TYPE_UHID_DESTROY:
|
||||
msg = parseUhidDestroy();
|
||||
break;
|
||||
return parseUhidDestroy();
|
||||
default:
|
||||
Ln.w("Unknown event type: " + type);
|
||||
msg = null;
|
||||
break;
|
||||
throw new ControlProtocolException("Unknown event type: " + type);
|
||||
}
|
||||
|
||||
if (msg == null) {
|
||||
// failure, reset savedPosition
|
||||
buffer.position(savedPosition);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectKeycode() {
|
||||
if (buffer.remaining() < INJECT_KEYCODE_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int action = Binary.toUnsigned(buffer.get());
|
||||
int keycode = buffer.getInt();
|
||||
int repeat = buffer.getInt();
|
||||
int metaState = buffer.getInt();
|
||||
private ControlMessage parseInjectKeycode() throws IOException {
|
||||
int action = dis.readUnsignedByte();
|
||||
int keycode = dis.readInt();
|
||||
int repeat = dis.readInt();
|
||||
int metaState = dis.readInt();
|
||||
return ControlMessage.createInjectKeycode(action, keycode, repeat, metaState);
|
||||
}
|
||||
|
||||
private int parseBufferLength(int sizeBytes) {
|
||||
private int parseBufferLength(int sizeBytes) throws IOException {
|
||||
assert sizeBytes > 0 && sizeBytes <= 4;
|
||||
if (buffer.remaining() < sizeBytes) {
|
||||
return -1;
|
||||
}
|
||||
int value = 0;
|
||||
for (int i = 0; i < sizeBytes; ++i) {
|
||||
value = (value << 8) | (buffer.get() & 0xFF);
|
||||
value = (value << 8) | dis.readUnsignedByte();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String parseString() {
|
||||
int len = parseBufferLength(4);
|
||||
if (len == -1 || buffer.remaining() < len) {
|
||||
return null;
|
||||
}
|
||||
int position = buffer.position();
|
||||
// Move the buffer position to consume the text
|
||||
buffer.position(position + len);
|
||||
return new String(rawBuffer, position, len, StandardCharsets.UTF_8);
|
||||
private String parseString(int sizeBytes) throws IOException {
|
||||
assert sizeBytes > 0 && sizeBytes <= 4;
|
||||
byte[] data = parseByteArray(sizeBytes);
|
||||
return new String(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private byte[] parseByteArray(int sizeBytes) {
|
||||
private String parseString() throws IOException {
|
||||
byte[] data = parseByteArray(4);
|
||||
return new String(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private byte[] parseByteArray(int sizeBytes) throws IOException {
|
||||
int len = parseBufferLength(sizeBytes);
|
||||
if (len == -1 || buffer.remaining() < len) {
|
||||
return null;
|
||||
}
|
||||
byte[] data = new byte[len];
|
||||
buffer.get(data);
|
||||
dis.readFully(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectText() {
|
||||
private ControlMessage parseInjectText() throws IOException {
|
||||
String text = parseString();
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
return ControlMessage.createInjectText(text);
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectTouchEvent() {
|
||||
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int action = Binary.toUnsigned(buffer.get());
|
||||
long pointerId = buffer.getLong();
|
||||
Position position = readPosition(buffer);
|
||||
float pressure = Binary.u16FixedPointToFloat(buffer.getShort());
|
||||
int actionButton = buffer.getInt();
|
||||
int buttons = buffer.getInt();
|
||||
private ControlMessage parseInjectTouchEvent() throws IOException {
|
||||
int action = dis.readUnsignedByte();
|
||||
long pointerId = dis.readLong();
|
||||
Position position = parsePosition();
|
||||
float pressure = Binary.u16FixedPointToFloat(dis.readShort());
|
||||
int actionButton = dis.readInt();
|
||||
int buttons = dis.readInt();
|
||||
return ControlMessage.createInjectTouchEvent(action, pointerId, position, pressure, actionButton, buttons);
|
||||
}
|
||||
|
||||
private ControlMessage parseInjectScrollEvent() {
|
||||
if (buffer.remaining() < INJECT_SCROLL_EVENT_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
Position position = readPosition(buffer);
|
||||
float hScroll = Binary.i16FixedPointToFloat(buffer.getShort());
|
||||
float vScroll = Binary.i16FixedPointToFloat(buffer.getShort());
|
||||
int buttons = buffer.getInt();
|
||||
private ControlMessage parseInjectScrollEvent() throws IOException {
|
||||
Position position = parsePosition();
|
||||
float hScroll = Binary.i16FixedPointToFloat(dis.readShort());
|
||||
float vScroll = Binary.i16FixedPointToFloat(dis.readShort());
|
||||
int buttons = dis.readInt();
|
||||
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons);
|
||||
}
|
||||
|
||||
private ControlMessage parseBackOrScreenOnEvent() {
|
||||
if (buffer.remaining() < BACK_OR_SCREEN_ON_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int action = Binary.toUnsigned(buffer.get());
|
||||
private ControlMessage parseBackOrScreenOnEvent() throws IOException {
|
||||
int action = dis.readUnsignedByte();
|
||||
return ControlMessage.createBackOrScreenOn(action);
|
||||
}
|
||||
|
||||
private ControlMessage parseGetClipboard() {
|
||||
if (buffer.remaining() < GET_CLIPBOARD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int copyKey = Binary.toUnsigned(buffer.get());
|
||||
private ControlMessage parseGetClipboard() throws IOException {
|
||||
int copyKey = dis.readUnsignedByte();
|
||||
return ControlMessage.createGetClipboard(copyKey);
|
||||
}
|
||||
|
||||
private ControlMessage parseSetClipboard() {
|
||||
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
long sequence = buffer.getLong();
|
||||
boolean paste = buffer.get() != 0;
|
||||
private ControlMessage parseSetClipboard() throws IOException {
|
||||
long sequence = dis.readLong();
|
||||
boolean paste = dis.readByte() != 0;
|
||||
String text = parseString();
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
return ControlMessage.createSetClipboard(sequence, text, paste);
|
||||
}
|
||||
|
||||
private ControlMessage parseSetScreenPowerMode() {
|
||||
if (buffer.remaining() < SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int mode = buffer.get();
|
||||
private ControlMessage parseSetScreenPowerMode() throws IOException {
|
||||
int mode = dis.readUnsignedByte();
|
||||
return ControlMessage.createSetScreenPowerMode(mode);
|
||||
}
|
||||
|
||||
private ControlMessage parseUhidCreate() {
|
||||
if (buffer.remaining() < UHID_CREATE_FIXED_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int id = buffer.getShort();
|
||||
private ControlMessage parseUhidCreate() throws IOException {
|
||||
int id = dis.readUnsignedShort();
|
||||
String name = parseString(1);
|
||||
byte[] data = parseByteArray(2);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return ControlMessage.createUhidCreate(id, data);
|
||||
return ControlMessage.createUhidCreate(id, name, data);
|
||||
}
|
||||
|
||||
private ControlMessage parseUhidInput() {
|
||||
if (buffer.remaining() < UHID_INPUT_FIXED_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int id = buffer.getShort();
|
||||
private ControlMessage parseUhidInput() throws IOException {
|
||||
int id = dis.readUnsignedShort();
|
||||
byte[] data = parseByteArray(2);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return ControlMessage.createUhidInput(id, data);
|
||||
}
|
||||
|
||||
private ControlMessage parseUhidDestroy() {
|
||||
if (buffer.remaining() < UHID_DESTROY_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
int id = buffer.getShort();
|
||||
private ControlMessage parseUhidDestroy() throws IOException {
|
||||
int id = dis.readUnsignedShort();
|
||||
return ControlMessage.createUhidDestroy(id);
|
||||
}
|
||||
|
||||
private static Position readPosition(ByteBuffer buffer) {
|
||||
int x = buffer.getInt();
|
||||
int y = buffer.getInt();
|
||||
int screenWidth = Binary.toUnsigned(buffer.getShort());
|
||||
int screenHeight = Binary.toUnsigned(buffer.getShort());
|
||||
private Position parsePosition() throws IOException {
|
||||
int x = dis.readInt();
|
||||
int y = dis.readInt();
|
||||
int screenWidth = dis.readUnsignedShort();
|
||||
int screenHeight = dis.readUnsignedShort();
|
||||
return new Position(x, y, screenWidth, screenHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.genymobile.scrcpy.control;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ControlProtocolException extends IOException {
|
||||
public ControlProtocolException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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,11 +1,11 @@
|
||||
package com.genymobile.scrcpy.control;
|
||||
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.util.StringUtils;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class DeviceMessageWriter {
|
||||
@@ -13,35 +13,35 @@ public class DeviceMessageWriter {
|
||||
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
|
||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = MESSAGE_MAX_SIZE - 5; // type: 1 byte; length: 4 bytes
|
||||
|
||||
private final byte[] rawBuffer = new byte[MESSAGE_MAX_SIZE];
|
||||
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
|
||||
private final DataOutputStream dos;
|
||||
|
||||
public void writeTo(DeviceMessage msg, OutputStream output) throws IOException {
|
||||
buffer.clear();
|
||||
buffer.put((byte) msg.getType());
|
||||
switch (msg.getType()) {
|
||||
public DeviceMessageWriter(OutputStream rawOutputStream) {
|
||||
dos = new DataOutputStream(new BufferedOutputStream(rawOutputStream));
|
||||
}
|
||||
|
||||
public void write(DeviceMessage msg) throws IOException {
|
||||
int type = msg.getType();
|
||||
dos.writeByte(type);
|
||||
switch (type) {
|
||||
case DeviceMessage.TYPE_CLIPBOARD:
|
||||
String text = msg.getText();
|
||||
byte[] raw = text.getBytes(StandardCharsets.UTF_8);
|
||||
int len = StringUtils.getUtf8TruncationIndex(raw, CLIPBOARD_TEXT_MAX_LENGTH);
|
||||
buffer.putInt(len);
|
||||
buffer.put(raw, 0, len);
|
||||
output.write(rawBuffer, 0, buffer.position());
|
||||
dos.writeInt(len);
|
||||
dos.write(raw, 0, len);
|
||||
break;
|
||||
case DeviceMessage.TYPE_ACK_CLIPBOARD:
|
||||
buffer.putLong(msg.getSequence());
|
||||
output.write(rawBuffer, 0, buffer.position());
|
||||
dos.writeLong(msg.getSequence());
|
||||
break;
|
||||
case DeviceMessage.TYPE_UHID_OUTPUT:
|
||||
buffer.putShort((short) msg.getId());
|
||||
dos.writeShort(msg.getId());
|
||||
byte[] data = msg.getData();
|
||||
buffer.putShort((short) data.length);
|
||||
buffer.put(data);
|
||||
output.write(rawBuffer, 0, buffer.position());
|
||||
dos.writeShort(data.length);
|
||||
dos.write(data);
|
||||
break;
|
||||
default:
|
||||
Ln.w("Unknown device message: " + msg.getType());
|
||||
break;
|
||||
throw new ControlProtocolException("Unknown event type: " + type);
|
||||
}
|
||||
dos.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.junit.Test;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
@@ -18,8 +19,6 @@ public class ControlMessageReaderTest {
|
||||
|
||||
@Test
|
||||
public void testParseKeycodeEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_KEYCODE);
|
||||
@@ -29,23 +28,21 @@ public class ControlMessageReaderTest {
|
||||
dos.writeInt(KeyEvent.META_CTRL_ON);
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
// The message type (1 byte) does not count
|
||||
Assert.assertEquals(ControlMessageReader.INJECT_KEYCODE_PAYLOAD_LENGTH, packet.length - 1);
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
|
||||
Assert.assertEquals(KeyEvent.KEYCODE_ENTER, event.getKeycode());
|
||||
Assert.assertEquals(5, event.getRepeat());
|
||||
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTextEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_TEXT);
|
||||
@@ -54,17 +51,18 @@ public class ControlMessageReaderTest {
|
||||
dos.write(text);
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_TEXT, event.getType());
|
||||
Assert.assertEquals("testé", event.getText());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLongTextEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_TEXT);
|
||||
@@ -74,17 +72,18 @@ public class ControlMessageReaderTest {
|
||||
dos.write(text);
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_TEXT, event.getType());
|
||||
Assert.assertEquals(new String(text, StandardCharsets.US_ASCII), event.getText());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTouchEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_TOUCH_EVENT);
|
||||
@@ -100,12 +99,10 @@ public class ControlMessageReaderTest {
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
// The message type (1 byte) does not count
|
||||
Assert.assertEquals(ControlMessageReader.INJECT_TOUCH_EVENT_PAYLOAD_LENGTH, packet.length - 1);
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_TOUCH_EVENT, event.getType());
|
||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||
Assert.assertEquals(-42, event.getPointerId());
|
||||
@@ -116,12 +113,12 @@ public class ControlMessageReaderTest {
|
||||
Assert.assertEquals(1f, event.getPressure(), 0f); // must be exact
|
||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getActionButton());
|
||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getButtons());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseScrollEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_INJECT_SCROLL_EVENT);
|
||||
@@ -132,15 +129,12 @@ public class ControlMessageReaderTest {
|
||||
dos.writeShort(0); // 0.0f encoded as i16
|
||||
dos.writeShort(0x8000); // -1.0f encoded as i16
|
||||
dos.writeInt(1);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
// The message type (1 byte) does not count
|
||||
Assert.assertEquals(ControlMessageReader.INJECT_SCROLL_EVENT_PAYLOAD_LENGTH, packet.length - 1);
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_SCROLL_EVENT, event.getType());
|
||||
Assert.assertEquals(260, event.getPosition().getPoint().getX());
|
||||
Assert.assertEquals(1026, event.getPosition().getPoint().getY());
|
||||
@@ -149,96 +143,96 @@ public class ControlMessageReaderTest {
|
||||
Assert.assertEquals(0f, event.getHScroll(), 0f);
|
||||
Assert.assertEquals(-1f, event.getVScroll(), 0f);
|
||||
Assert.assertEquals(1, event.getButtons());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBackOrScreenOnEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_BACK_OR_SCREEN_ON);
|
||||
dos.writeByte(KeyEvent.ACTION_UP);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_BACK_OR_SCREEN_ON, event.getType());
|
||||
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExpandNotificationPanelEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL, event.getType());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExpandSettingsPanelEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_EXPAND_SETTINGS_PANEL);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_EXPAND_SETTINGS_PANEL, event.getType());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCollapsePanelsEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_COLLAPSE_PANELS);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_COLLAPSE_PANELS, event.getType());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseGetClipboardEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_GET_CLIPBOARD);
|
||||
dos.writeByte(ControlMessage.COPY_KEY_COPY);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_GET_CLIPBOARD, event.getType());
|
||||
Assert.assertEquals(ControlMessage.COPY_KEY_COPY, event.getCopyKey());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSetClipboardEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
|
||||
@@ -247,22 +241,22 @@ public class ControlMessageReaderTest {
|
||||
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
|
||||
dos.writeInt(text.length);
|
||||
dos.write(text);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||
Assert.assertEquals(0x0102030405060708L, event.getSequence());
|
||||
Assert.assertEquals("testé", event.getText());
|
||||
Assert.assertTrue(event.getPaste());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBigSetClipboardEvent() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
|
||||
@@ -278,78 +272,79 @@ public class ControlMessageReaderTest {
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
|
||||
Assert.assertEquals(0x0807060504030201L, event.getSequence());
|
||||
Assert.assertEquals(text, event.getText());
|
||||
Assert.assertTrue(event.getPaste());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSetScreenPowerMode() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_SET_SCREEN_POWER_MODE);
|
||||
dos.writeByte(Device.POWER_MODE_NORMAL);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
// The message type (1 byte) does not count
|
||||
Assert.assertEquals(ControlMessageReader.SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH, packet.length - 1);
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_SET_SCREEN_POWER_MODE, event.getType());
|
||||
Assert.assertEquals(Device.POWER_MODE_NORMAL, event.getAction());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseRotateDevice() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_ROTATE_DEVICE);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_ROTATE_DEVICE, event.getType());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseUhidCreate() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
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();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseUhidInput() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_UHID_INPUT);
|
||||
@@ -357,55 +352,55 @@ public class ControlMessageReaderTest {
|
||||
byte[] data = {1, 2, 3, 4, 5};
|
||||
dos.writeShort(data.length); // size
|
||||
dos.write(data);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_UHID_INPUT, event.getType());
|
||||
Assert.assertEquals(42, event.getId());
|
||||
Assert.assertArrayEquals(data, event.getData());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseUhidDestroy() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_UHID_DESTROY);
|
||||
dos.writeShort(42); // id
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_UHID_DESTROY, event.getType());
|
||||
Assert.assertEquals(42, event.getId());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseOpenHardKeyboardSettings() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS, event.getType());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiEvents() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
|
||||
@@ -422,27 +417,29 @@ public class ControlMessageReaderTest {
|
||||
dos.writeInt(KeyEvent.META_CTRL_ON);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
|
||||
ControlMessage event = reader.next();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
|
||||
Assert.assertEquals(KeyEvent.KEYCODE_ENTER, event.getKeycode());
|
||||
Assert.assertEquals(0, event.getRepeat());
|
||||
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||
|
||||
event = reader.next();
|
||||
event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getKeycode());
|
||||
Assert.assertEquals(1, event.getRepeat());
|
||||
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPartialEvents() throws IOException {
|
||||
ControlMessageReader reader = new ControlMessageReader();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
|
||||
@@ -456,31 +453,21 @@ public class ControlMessageReaderTest {
|
||||
dos.writeByte(MotionEvent.ACTION_DOWN);
|
||||
|
||||
byte[] packet = bos.toByteArray();
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.next();
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
|
||||
Assert.assertEquals(KeyEvent.KEYCODE_ENTER, event.getKeycode());
|
||||
Assert.assertEquals(4, event.getRepeat());
|
||||
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||
|
||||
event = reader.next();
|
||||
Assert.assertNull(event); // the event is not complete
|
||||
|
||||
bos.reset();
|
||||
dos.writeInt(MotionEvent.BUTTON_PRIMARY);
|
||||
dos.writeInt(5); // repeat
|
||||
dos.writeInt(KeyEvent.META_CTRL_ON);
|
||||
packet = bos.toByteArray();
|
||||
reader.readFrom(new ByteArrayInputStream(packet));
|
||||
|
||||
// the event is now complete
|
||||
event = reader.next();
|
||||
Assert.assertEquals(ControlMessage.TYPE_INJECT_KEYCODE, event.getType());
|
||||
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||
Assert.assertEquals(MotionEvent.BUTTON_PRIMARY, event.getKeycode());
|
||||
Assert.assertEquals(5, event.getRepeat());
|
||||
Assert.assertEquals(KeyEvent.META_CTRL_ON, event.getMetaState());
|
||||
try {
|
||||
event = reader.read();
|
||||
Assert.fail("Reader did not reach EOF");
|
||||
} catch (EOFException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ public class DeviceMessageWriterTest {
|
||||
|
||||
@Test
|
||||
public void testSerializeClipboard() throws IOException {
|
||||
DeviceMessageWriter writer = new DeviceMessageWriter();
|
||||
|
||||
String text = "aéûoç";
|
||||
byte[] data = text.getBytes(StandardCharsets.UTF_8);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
@@ -21,12 +19,13 @@ public class DeviceMessageWriterTest {
|
||||
dos.writeByte(DeviceMessage.TYPE_CLIPBOARD);
|
||||
dos.writeInt(data.length);
|
||||
dos.write(data);
|
||||
|
||||
byte[] expected = bos.toByteArray();
|
||||
|
||||
DeviceMessage msg = DeviceMessage.createClipboard(text);
|
||||
bos = new ByteArrayOutputStream();
|
||||
writer.writeTo(msg, bos);
|
||||
DeviceMessageWriter writer = new DeviceMessageWriter(bos);
|
||||
|
||||
DeviceMessage msg = DeviceMessage.createClipboard(text);
|
||||
writer.write(msg);
|
||||
|
||||
byte[] actual = bos.toByteArray();
|
||||
|
||||
@@ -35,18 +34,17 @@ public class DeviceMessageWriterTest {
|
||||
|
||||
@Test
|
||||
public void testSerializeAckSetClipboard() throws IOException {
|
||||
DeviceMessageWriter writer = new DeviceMessageWriter();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(DeviceMessage.TYPE_ACK_CLIPBOARD);
|
||||
dos.writeLong(0x0102030405060708L);
|
||||
|
||||
byte[] expected = bos.toByteArray();
|
||||
|
||||
DeviceMessage msg = DeviceMessage.createAckClipboard(0x0102030405060708L);
|
||||
bos = new ByteArrayOutputStream();
|
||||
writer.writeTo(msg, bos);
|
||||
DeviceMessageWriter writer = new DeviceMessageWriter(bos);
|
||||
|
||||
DeviceMessage msg = DeviceMessage.createAckClipboard(0x0102030405060708L);
|
||||
writer.write(msg);
|
||||
|
||||
byte[] actual = bos.toByteArray();
|
||||
|
||||
@@ -55,8 +53,6 @@ public class DeviceMessageWriterTest {
|
||||
|
||||
@Test
|
||||
public void testSerializeUhidOutput() throws IOException {
|
||||
DeviceMessageWriter writer = new DeviceMessageWriter();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(DeviceMessage.TYPE_UHID_OUTPUT);
|
||||
@@ -64,12 +60,13 @@ public class DeviceMessageWriterTest {
|
||||
byte[] data = {1, 2, 3, 4, 5};
|
||||
dos.writeShort(data.length);
|
||||
dos.write(data);
|
||||
|
||||
byte[] expected = bos.toByteArray();
|
||||
|
||||
DeviceMessage msg = DeviceMessage.createUhidOutput(42, data);
|
||||
bos = new ByteArrayOutputStream();
|
||||
writer.writeTo(msg, bos);
|
||||
DeviceMessageWriter writer = new DeviceMessageWriter(bos);
|
||||
|
||||
DeviceMessage msg = DeviceMessage.createUhidOutput(42, data);
|
||||
writer.write(msg);
|
||||
|
||||
byte[] actual = bos.toByteArray();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user